global profile settings - fixes #4098

This commit is contained in:
Eugene Pankov 2021-08-22 00:10:56 +02:00
parent 0a3debb691
commit 20116d7af6
No known key found for this signature in database
GPG Key ID: 5896FCBBDD1CF4F4
6 changed files with 162 additions and 108 deletions

View File

@ -21,6 +21,8 @@ hotkeys:
profile:
__nonStructural: true
profiles: []
profileDefaults:
__nonStructural: true
recentProfiles: []
recoverTabs: true
enableAnalytics: true

View File

@ -179,9 +179,13 @@ export class ProfilesService {
return null
}
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>): T {
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>, skipUserDefaults = false): T {
const provider = this.providerForProfile(profile)
const defaults = configMerge(this.profileDefaults, provider?.configDefaults ?? {})
const defaults = [
this.profileDefaults,
provider?.configDefaults ?? {},
!provider || skipUserDefaults ? {} : this.config.store.profileDefaults[provider.id] ?? {},
].reduce(configMerge, {})
return new ConfigProxy(profile, defaults) as unknown as T
}
}

View File

@ -1,10 +1,13 @@
.modal-header
.modal-header(*ngIf='!defaultsMode')
h3.m-0 {{profile.name}}
.modal-header(*ngIf='defaultsMode')
h3.m-0 Defaults for {{profileProvider.name}}
.modal-body
.row
.col-12.col-lg-4
.form-group
.form-group(*ngIf='!defaultsMode')
label Name
input.form-control(
type='text',
@ -12,7 +15,7 @@
[(ngModel)]='profile.name',
)
.form-group
.form-group(*ngIf='!defaultsMode')
label Group
input.form-control(
type='text',
@ -22,7 +25,7 @@
[ngbTypeahead]='groupTypeahead',
)
.form-group
.form-group(*ngIf='!defaultsMode')
label Icon
.input-group
input.form-control(
@ -47,7 +50,6 @@
type='text',
[(ngModel)]='profile.color',
placeholder='#000000',
alwaysVisibleTypeahead,
[ngbTypeahead]='colorsAutocomplete',
[resultFormatter]='colorsFormatter'
)

View File

@ -19,6 +19,7 @@ export class EditProfileModalComponent<P extends Profile> {
@Input() profile: P & ConfigProxy
@Input() profileProvider: ProfileProvider<P>
@Input() settingsComponent: new () => ProfileSettingsComponent<P>
@Input() defaultsMode = false
groupNames: string[]
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
@ -55,7 +56,7 @@ export class EditProfileModalComponent<P extends Profile> {
ngOnInit () {
this._profile = this.profile
this.profile = this.profilesService.getConfigProxyForProfile(this.profile)
this.profile = this.profilesService.getConfigProxyForProfile(this.profile, this.defaultsMode)
}
ngAfterViewInit () {

View File

@ -1,108 +1,127 @@
h3.mb-3 Profiles
.form-line
.header
.title Default profile for new tabs
ul.nav-tabs(ngbNav, #nav='ngbNav')
li(ngbNavItem)
a(ngbNavLink) Profiles
ng-template(ngbNavContent)
.form-line
.header
.title Default profile for new tabs
select.form-control(
[(ngModel)]='config.store.terminal.profile',
(ngModelChange)='config.save()',
)
option(
*ngFor='let profile of profiles',
[ngValue]='profile.id'
) {{profile.name}}
option(
*ngFor='let profile of builtinProfiles',
[ngValue]='profile.id'
) {{profile.name}}
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show recent profiles in selector
.description Set to 0 to disable recent profiles
input.form-control(
type='number',
min='0',
step='1',
[(ngModel)]='config.store.terminal.showRecentProfiles',
(ngModelChange)='config.save()'
)
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show built-in profiles in selector
.description If disabled, only custom profiles will show up in the profile selector
toggle(
[(ngModel)]='config.store.terminal.showBuiltinProfiles',
(ngModelChange)='config.save()'
)
.d-flex.mb-3.mt-4
.input-group
.input-group-prepend
.input-group-text
i.fas.fa-fw.fa-search
input.form-control(type='search', placeholder='Filter', [(ngModel)]='filter')
button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()')
i.fas.fa-fw.fa-plus
| New profile
.list-group.list-group-light.mt-3.mb-3
ng-container(*ngFor='let group of profileGroups')
ng-container(*ngIf='isGroupVisible(group)')
.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='group.collapsed = !group.collapsed'
)
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); editGroup(group)'
select.form-control(
[(ngModel)]='config.store.terminal.profile',
(ngModelChange)='config.save()',
)
i.fas.fa-pencil-alt
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); deleteGroup(group)'
option(
*ngFor='let profile of profiles',
[ngValue]='profile.id'
) {{profile.name}}
option(
*ngFor='let profile of builtinProfiles',
[ngValue]='profile.id'
) {{profile.name}}
.d-flex.mb-3.mt-4
.input-group
.input-group-prepend
.input-group-text
i.fas.fa-fw.fa-search
input.form-control(type='search', placeholder='Filter', [(ngModel)]='filter')
button.btn.btn-primary.flex-shrink-0.ml-3((click)='newProfile()')
i.fas.fa-fw.fa-plus
| New profile
.list-group.list-group-light.mt-3.mb-3
ng-container(*ngFor='let group of profileGroups')
ng-container(*ngIf='isGroupVisible(group)')
.list-group-item.list-group-item-action.d-flex.align-items-center(
(click)='group.collapsed = !group.collapsed'
)
.fa.fa-fw.fa-chevron-right(*ngIf='group.collapsed')
.fa.fa-fw.fa-chevron-down(*ngIf='!group.collapsed')
span.ml-3.mr-auto {{group.name || "Ungrouped"}}
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); editGroup(group)'
)
i.fas.fa-pencil-alt
button.btn.btn-sm.btn-link.hover-reveal.ml-2(
*ngIf='group.editable && group.name',
(click)='$event.stopPropagation(); deleteGroup(group)'
)
i.fas.fa-trash-alt
ng-container(*ngIf='!group.collapsed')
ng-container(*ngFor='let profile of group.profiles')
.list-group-item.pl-5.d-flex.align-items-center(
*ngIf='isProfileVisible(profile)',
[class.list-group-item-action]='!profile.isBuiltin',
(click)='profile.isBuiltin ? null : editProfile(profile)'
)
i.icon(
class='fa-fw {{profile.icon}}',
[style.color]='profile.color',
*ngIf='!iconIsSVG(profile.icon)'
)
.icon(
[fastHtmlBind]='profile.icon',
*ngIf='iconIsSVG(profile.icon)'
)
div {{profile.name}}
.text-muted.ml-2 {{getDescription(profile)}}
.mr-auto
button.btn.btn-link.hover-reveal.ml-1((click)='$event.stopPropagation(); launchProfile(profile)')
i.fas.fa-play
button.btn.btn-link.hover-reveal.ml-1((click)='$event.stopPropagation(); newProfile(profile)')
i.fas.fa-copy
button.btn.btn-link.hover-reveal.ml-1(
*ngIf='!profile.isBuiltin',
(click)='$event.stopPropagation(); deleteProfile(profile)'
)
i.fas.fa-trash-alt
.ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}}
li(ngbNavItem)
a(ngbNavLink) Advanced
ng-template(ngbNavContent)
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show recent profiles in selector
.description Set to 0 to disable recent profiles
input.form-control(
type='number',
min='0',
step='1',
[(ngModel)]='config.store.terminal.showRecentProfiles',
(ngModelChange)='config.save()'
)
i.fas.fa-trash-alt
ng-container(*ngIf='!group.collapsed')
ng-container(*ngFor='let profile of group.profiles')
.list-group-item.pl-5.d-flex.align-items-center(
*ngIf='isProfileVisible(profile)',
[class.list-group-item-action]='!profile.isBuiltin',
(click)='profile.isBuiltin ? null : editProfile(profile)'
)
i.icon(
class='fa-fw {{profile.icon}}',
[style.color]='profile.color',
*ngIf='!iconIsSVG(profile.icon)'
)
.icon(
[fastHtmlBind]='profile.icon',
*ngIf='iconIsSVG(profile.icon)'
)
div {{profile.name}}
.text-muted.ml-2 {{getDescription(profile)}}
.form-line(*ngIf='config.store.profiles.length > 0')
.header
.title Show built-in profiles in selector
.description If disabled, only custom profiles will show up in the profile selector
.mr-auto
toggle(
[(ngModel)]='config.store.terminal.showBuiltinProfiles',
(ngModelChange)='config.save()'
)
button.btn.btn-link.hover-reveal.ml-1((click)='$event.stopPropagation(); launchProfile(profile)')
i.fas.fa-play
.form-line
.header
.title Default profile settings
.description These apply to all profiles of a given type
button.btn.btn-link.hover-reveal.ml-1((click)='$event.stopPropagation(); newProfile(profile)')
i.fas.fa-copy
.list-group.list-group-light.mt-3.mb-3
a.list-group-item.list-group-item-action(
(click)='editDefaults(provider)',
*ngFor='let provider of profileProviders'
) {{provider.name}}
button.btn.btn-link.hover-reveal.ml-1(
*ngIf='!profile.isBuiltin',
(click)='$event.stopPropagation(); deleteProfile(profile)'
)
i.fas.fa-trash-alt
.ml-1(class='badge badge-{{getTypeColorClass(profile)}}') {{getTypeLabel(profile)}}
div([ngbNavOutlet]='nav')

View File

@ -1,9 +1,9 @@
import { v4 as uuidv4 } from 'uuid'
import slugify from 'slugify'
import deepClone from 'clone-deep'
import { Component } from '@angular/core'
import { Component, Inject } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile } from 'tabby-core'
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile, ProfileProvider } from 'tabby-core'
import { EditProfileModalComponent } from './editProfileModal.component'
interface ProfileGroup {
@ -28,12 +28,14 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
constructor (
public config: ConfigService,
public hostApp: HostAppService,
@Inject(ProfileProvider) public profileProviders: ProfileProvider<Profile>[],
private profilesService: ProfilesService,
private selector: SelectorService,
private ngbModal: NgbModal,
private platform: PlatformService,
) {
super()
this.profileProviders.sort((a, b) => a.name.localeCompare(b.name))
}
async ngOnInit (): Promise<void> {
@ -102,6 +104,8 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
delete profile[k]
}
Object.assign(profile, result)
profile.type = modal.componentInstance.profileProvider.id
}
async deleteProfile (profile: PartialProfile<Profile>): Promise<void> {
@ -224,4 +228,26 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
'split-layout': 'primary',
}[this.profilesService.providerForProfile(profile)?.id ?? ''] ?? 'warning'
}
async editDefaults (provider: ProfileProvider<Profile>): Promise<void> {
const modal = this.ngbModal.open(
EditProfileModalComponent,
{ size: 'lg' },
)
const model = this.config.store.profileDefaults[provider.id] ?? {}
model.type = provider.id
modal.componentInstance.profile = Object.assign({}, model)
modal.componentInstance.profileProvider = provider
modal.componentInstance.defaultsMode = true
const result = await modal.result
// Fully replace the config
for (const k in model) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete model[k]
}
Object.assign(model, result)
this.config.store.profileDefaults[provider.id] = model
await this.config.save()
}
}