Browse Source

support for providing commands as toolbar buttons

Eugene Pankov 2 years ago
parent
commit
b0600b10cc

+ 41 - 4
tabby-core/src/api/commands.ts

@@ -2,21 +2,51 @@ import { BaseTabComponent } from '../components/baseTab.component'
 import { MenuItemOptions } from './menu'
 import { ToolbarButton } from './toolbarButtonProvider'
 
+export enum CommandLocation {
+    LeftToolbar = 'left-toolbar',
+    RightToolbar = 'right-toolbar',
+    StartPage = 'start-page',
+}
+
 export class Command {
+    id?: string
     label: string
     sublabel?: string
-    click?: () => void
+    locations?: CommandLocation[]
+    run: () => Promise<void>
 
     /**
      * Raw SVG icon code
      */
     icon?: string
 
+    /**
+     * Optional Touch Bar icon ID
+     */
+    touchBarNSImage?: string
+
+    /**
+     * Optional Touch Bar button label
+     */
+    touchBarTitle?: string
+
+    weight?: number
+
     static fromToolbarButton (button: ToolbarButton): Command {
         const command = new Command()
-        command.label = button.commandLabel ?? button.title
-        command.click = button.click
+        command.label = button.title
+        command.run = async () => button.click?.()
         command.icon = button.icon
+        command.locations = [CommandLocation.StartPage]
+        if ((button.weight ?? 0) <= 0) {
+            command.locations.push(CommandLocation.LeftToolbar)
+        }
+        if ((button.weight ?? 0) > 0) {
+            command.locations.push(CommandLocation.RightToolbar)
+        }
+        command.touchBarNSImage = button.touchBarNSImage
+        command.touchBarTitle = button.touchBarTitle
+        command.weight = button.weight
         return command
     }
 
@@ -24,7 +54,7 @@ export class Command {
         const command = new Command()
         command.label = item.commandLabel ?? item.label ?? ''
         command.sublabel = item.sublabel
-        command.click = item.click
+        command.run = async () => item.click?.()
         return command
     }
 }
@@ -32,3 +62,10 @@ export class Command {
 export interface CommandContext {
     tab?: BaseTabComponent,
 }
+
+/**
+ * Extend to add commands
+ */
+export abstract class CommandProvider {
+    abstract provide (context: CommandContext): Promise<Command[]>
+}

+ 0 - 7
tabby-core/src/api/toolbarButtonProvider.ts

@@ -27,13 +27,6 @@ export interface ToolbarButton {
 
     /** @hidden */
     submenuItems?: ToolbarButton[]
-
-    showInToolbar?: boolean
-
-    showInStartPage?: boolean
-
-    /** @hidden */
-    commandLabel?: string
 }
 
 /**

+ 11 - 17
tabby-core/src/buttonProvider.ts → tabby-core/src/commands.ts

@@ -2,26 +2,19 @@
 import { Injectable } from '@angular/core'
 import { TranslateService } from '@ngx-translate/core'
 
-import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
 import { HostAppService, Platform } from './api/hostApp'
-import { HotkeysService } from './services/hotkeys.service'
 import { ProfilesService } from './services/profiles.service'
+import { CommandProvider, Command, CommandLocation } from './api/commands'
 
 /** @hidden */
-@Injectable()
-export class ButtonProvider extends ToolbarButtonProvider {
+@Injectable({ providedIn: 'root' })
+export class CoreCommandProvider extends CommandProvider {
     constructor (
         private hostApp: HostAppService,
         private profilesService: ProfilesService,
         private translate: TranslateService,
-        hotkeys: HotkeysService,
     ) {
         super()
-        hotkeys.hotkey$.subscribe(hotkey => {
-            if (hotkey === 'profile-selector') {
-                this.activate()
-            }
-        })
     }
 
     async activate () {
@@ -31,21 +24,22 @@ export class ButtonProvider extends ToolbarButtonProvider {
         }
     }
 
-    provide (): ToolbarButton[] {
+    async provide (): Promise<Command[]> {
         return [
             {
+                id: 'profile-selector',
+                locations: [CommandLocation.LeftToolbar, CommandLocation.StartPage],
+                label: this.translate.instant('Profiles & connections'),
                 icon: this.hostApp.platform === Platform.Web
                     ? require('./icons/plus.svg')
                     : require('./icons/profiles.svg'),
-                title: this.translate.instant('Profiles & connections'),
-                click: () => this.activate(),
+                run: async () => this.activate(),
             },
             ...this.profilesService.getRecentProfiles().map(profile => ({
+                label: profile.name,
+                locations: [CommandLocation.StartPage],
                 icon: require('./icons/history.svg'),
-                title: profile.name,
-                showInToolbar: false,
-                showinStartPage: true,
-                click: async () => {
+                run: async () => {
                     const p = (await this.profilesService.getProfiles()).find(x => x.id === profile.id) ?? profile
                     this.profilesService.launchProfile(p)
                 },

+ 5 - 29
tabby-core/src/components/appRoot.component.pug

@@ -38,26 +38,14 @@ title-bar(
         .btn-group.background
             .d-flex(
                 *ngFor='let button of leftToolbarButtons',
-                ngbDropdown,
-                (openChange)='generateButtonSubmenu(button)',
+                ngbDropdown
             )
                 button.btn.btn-secondary.btn-tab-bar(
-                    [title]='button.title',
-                    (click)='button.click && button.click()',
+                    [title]='button.label',
+                    (click)='button.run && button.run()',
                     [fastHtmlBind]='button.icon',
                     ngbDropdownToggle,
                 )
-                div(*ngIf='button.submenu', ngbDropdownMenu)
-                    button.dropdown-item.d-flex.align-items-center(
-                        *ngFor='let item of button.submenuItems',
-                        (click)='item.click()',
-                        ngbDropdownItem,
-                    )
-                        .icon-wrapper(
-                            *ngIf='hasIcons(button.submenuItems)',
-                            [fastHtmlBind]='item.icon'
-                        )
-                        div([class.ml-3]='hasIcons(button.submenuItems)') {{item.title}}
 
             .d-flex(
                 ngbDropdown,
@@ -80,26 +68,14 @@ title-bar(
         .btn-group.background
             .d-flex(
                 *ngFor='let button of rightToolbarButtons',
-                ngbDropdown,
-                (openChange)='generateButtonSubmenu(button)',
+                ngbDropdown
             )
                 button.btn.btn-secondary.btn-tab-bar(
                     [title]='button.title',
-                    (click)='button.click && button.click()',
+                    (click)='button.run && button.run()',
                     [fastHtmlBind]='button.icon',
                     ngbDropdownToggle,
                 )
-                div(*ngIf='button.submenu', ngbDropdownMenu)
-                    button.dropdown-item.d-flex.align-items-center(
-                        *ngFor='let item of button.submenuItems',
-                        (click)='item.click()',
-                        ngbDropdownItem,
-                    )
-                        .icon-wrapper(
-                            *ngIf='hasIcons(button.submenuItems)',
-                            [fastHtmlBind]='item.icon'
-                        )
-                        div([class.ml-3]='hasIcons(button.submenuItems)') {{item.title}}
 
             button.btn.btn-secondary.btn-tab-bar.btn-update(
                 *ngIf='updatesAvailable',

+ 12 - 27
tabby-core/src/components/appRoot.component.ts

@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
-import { Component, Inject, Input, HostListener, HostBinding, ViewChildren, ViewChild } from '@angular/core'
+import { Component, Input, HostListener, HostBinding, ViewChildren, ViewChild } from '@angular/core'
 import { trigger, style, animate, transition, state } from '@angular/animations'
 import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
 import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'
@@ -10,12 +10,13 @@ import { Logger, LogService } from '../services/log.service'
 import { ConfigService } from '../services/config.service'
 import { ThemesService } from '../services/themes.service'
 import { UpdaterService } from '../services/updater.service'
+import { CommandService } from '../services/commands.service'
 
 import { BaseTabComponent } from './baseTab.component'
 import { SafeModeModalComponent } from './safeModeModal.component'
 import { TabBodyComponent } from './tabBody.component'
 import { SplitTabComponent } from './splitTab.component'
-import { AppService, FileTransfer, HostWindowService, PlatformService, ToolbarButton, ToolbarButtonProvider } from '../api'
+import { AppService, Command, CommandLocation, FileTransfer, HostWindowService, PlatformService } from '../api'
 
 function makeTabAnimation (dimension: string, size: number) {
     return [
@@ -63,8 +64,8 @@ function makeTabAnimation (dimension: string, size: number) {
 export class AppRootComponent {
     Platform = Platform
     @Input() ready = false
-    @Input() leftToolbarButtons: ToolbarButton[]
-    @Input() rightToolbarButtons: ToolbarButton[]
+    @Input() leftToolbarButtons: Command[]
+    @Input() rightToolbarButtons: Command[]
     @HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
     @HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
     @HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
@@ -79,11 +80,11 @@ export class AppRootComponent {
     constructor (
         private hotkeys: HotkeysService,
         private updater: UpdaterService,
+        private commands: CommandService,
         public hostWindow: HostWindowService,
         public hostApp: HostAppService,
         public config: ConfigService,
         public app: AppService,
-        @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
         platform: PlatformService,
         log: LogService,
         ngbModal: NgbModal,
@@ -170,9 +171,9 @@ export class AppRootComponent {
             this.activeTransfersDropdown.open()
         })
 
-        config.ready$.toPromise().then(() => {
-            this.leftToolbarButtons = this.getToolbarButtons(false)
-            this.rightToolbarButtons = this.getToolbarButtons(true)
+        config.ready$.toPromise().then(async () => {
+            this.leftToolbarButtons = await this.getToolbarButtons(false)
+            this.rightToolbarButtons = await this.getToolbarButtons(true)
 
             setInterval(() => {
                 if (this.config.store.enableAutomaticUpdates) {
@@ -212,16 +213,6 @@ export class AppRootComponent {
         return this.config.store.appearance.flexTabs ? '*' : '200px'
     }
 
-    async generateButtonSubmenu (button: ToolbarButton) {
-        if (button.submenu) {
-            button.submenuItems = await button.submenu()
-        }
-    }
-
-    hasIcons (submenuItems: ToolbarButton[]): boolean {
-        return submenuItems.some(x => !!x.icon)
-    }
-
     onTabsReordered (event: CdkDragDrop<BaseTabComponent[]>) {
         const tab: BaseTabComponent = event.item.data
         if (!this.app.tabs.includes(tab)) {
@@ -244,14 +235,8 @@ export class AppRootComponent {
         return this.config.store?.appearance.vibrancy
     }
 
-    private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {
-        let buttons: ToolbarButton[] = []
-        this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
-            buttons = buttons.concat(provider.provide())
-        })
-        return buttons
-            .filter(x => x.showInToolbar ?? true)
-            .filter(button => (button.weight ?? 0) > 0 === aboveZero)
-            .sort((a: ToolbarButton, b: ToolbarButton) => (a.weight ?? 0) - (b.weight ?? 0))
+    private async getToolbarButtons (aboveZero: boolean): Promise<Command[]> {
+        return (await this.commands.getCommands({ tab: this.app.activeTab ?? undefined }))
+            .filter(x => x.locations?.includes(aboveZero ? CommandLocation.RightToolbar : CommandLocation.LeftToolbar))
     }
 }

+ 6 - 2
tabby-core/src/components/baseTab.component.ts

@@ -1,7 +1,8 @@
 import { Observable, Subject, distinctUntilChanged, filter, debounceTime } from 'rxjs'
-import { EmbeddedViewRef, ViewContainerRef, ViewRef } from '@angular/core'
+import { EmbeddedViewRef, Injector, ViewContainerRef, ViewRef } from '@angular/core'
 import { RecoveryToken } from '../api/tabRecovery'
 import { BaseComponent } from './base.component'
+import { ConfigService } from '../services/config.service'
 
 /**
  * Represents an active "process" inside a tab,
@@ -87,8 +88,11 @@ export abstract class BaseTabComponent extends BaseComponent {
     get destroyed$ (): Observable<void> { return this.destroyed }
     get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
 
-    protected constructor () {
+    protected config: ConfigService
+
+    protected constructor (injector: Injector) {
         super()
+        this.config = injector.get(ConfigService)
         this.focused$.subscribe(() => {
             this.hasFocus = true
         })

+ 1 - 1
tabby-core/src/components/selectorModal.component.ts

@@ -97,8 +97,8 @@ export class SelectorModalComponent<T> {
     }
 
     selectOption (option: SelectorOption<T>): void {
-        option.callback?.(this.filter)
         this.modalInstance.close(option.result)
+        setTimeout(() => option.callback?.(this.filter))
     }
 
     canEditSelected (): boolean {

+ 3 - 4
tabby-core/src/components/splitTab.component.ts

@@ -1,11 +1,10 @@
 import { Observable, Subject } from 'rxjs'
-import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy } from '@angular/core'
+import { Component, Injectable, ViewChild, ViewContainerRef, EmbeddedViewRef, AfterViewInit, OnDestroy, Injector } from '@angular/core'
 import { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from './baseTab.component'
 import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
 import { TabsService, NewTabParameters } from '../services/tabs.service'
 import { HotkeysService } from '../services/hotkeys.service'
 import { TabRecoveryService } from '../services/tabRecovery.service'
-import { ConfigService } from '../api'
 
 export type SplitOrientation = 'v' | 'h'
 export type SplitDirection = 'r' | 't' | 'b' | 'l'
@@ -261,9 +260,9 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
         private hotkeys: HotkeysService,
         private tabsService: TabsService,
         private tabRecovery: TabRecoveryService,
-        private config: ConfigService,
+        injector: Injector,
     ) {
-        super()
+        super(injector)
         this.root = new SplitContainer()
         this.setTitle('')
 

+ 4 - 4
tabby-core/src/components/startPage.component.pug

@@ -5,11 +5,11 @@ div
 
     .list-group.mb-4
         a.list-group-item.list-group-item-action.d-flex(
-            *ngFor='let button of getButtons(); trackBy: buttonsTrackBy',
-            (click)='button.click()',
+            *ngFor='let command of commands; trackBy: buttonsTrackBy',
+            (click)='command.run()',
         )
-            .d-flex.align-self-center([innerHTML]='sanitizeIcon(button.icon)')
-            span {{button.title}}
+            .d-flex.align-self-center([innerHTML]='sanitizeIcon(command.icon)')
+            span {{command.label}}
 
 footer.d-flex.align-items-center
     .btn-group.mr-auto

+ 10 - 16
tabby-core/src/components/startPage.component.ts

@@ -1,8 +1,8 @@
-import { Component, Inject } from '@angular/core'
+import { Component } from '@angular/core'
 import { DomSanitizer } from '@angular/platform-browser'
-import { ConfigService } from '../services/config.service'
 import { HomeBaseService } from '../services/homeBase.service'
-import { ToolbarButton, ToolbarButtonProvider } from '../api'
+import { CommandService } from '../services/commands.service'
+import { Command, CommandLocation } from '../api/commands'
 
 /** @hidden */
 @Component({
@@ -12,29 +12,23 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
 })
 export class StartPageComponent {
     version: string
+    commands: Command[] = []
 
     constructor (
-        private config: ConfigService,
         private domSanitizer: DomSanitizer,
         public homeBase: HomeBaseService,
-        @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
+        commands: CommandService,
     ) {
-    }
-
-    getButtons (): ToolbarButton[] {
-        return this.config.enabledServices(this.toolbarButtonProviders)
-            .map(provider => provider.provide())
-            .reduce((a, b) => a.concat(b))
-            .filter(x => x.showInStartPage ?? true)
-            .filter(x => !!x.click)
-            .sort((a: ToolbarButton, b: ToolbarButton) => (a.weight ?? 0) - (b.weight ?? 0))
+        commands.getCommands({}).then(c => {
+            this.commands = c.filter(x => x.locations?.includes(CommandLocation.StartPage))
+        })
     }
 
     sanitizeIcon (icon?: string): any {
         return this.domSanitizer.bypassSecurityTrustHtml(icon ?? '')
     }
 
-    buttonsTrackBy (btn: ToolbarButton): any {
-        return btn.title + btn.icon
+    buttonsTrackBy (btn: Command): any {
+        return btn.label + btn.icon
     }
 }

+ 3 - 2
tabby-core/src/components/welcomeTab.component.ts

@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
-import { Component } from '@angular/core'
+import { Component, Injector } from '@angular/core'
 import { TranslateService } from '@ngx-translate/core'
 import { BaseTabComponent } from './baseTab.component'
 import { ConfigService } from '../services/config.service'
@@ -19,8 +19,9 @@ export class WelcomeTabComponent extends BaseTabComponent {
         public config: ConfigService,
         public locale: LocaleService,
         translate: TranslateService,
+        injector: Injector,
     ) {
-        super()
+        super(injector)
         this.setTitle(translate.instant('Welcome'))
     }
 

+ 8 - 4
tabby-core/src/index.ts

@@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
 import { DropZoneDirective } from './directives/dropZone.directive'
 import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.directive'
 
-import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ToolbarButtonProvider, ProfilesService, ProfileProvider, SelectorOption, Profile, SelectorService } from './api'
+import { Theme, CLIHandler, TabContextMenuItemProvider, TabRecoveryProvider, HotkeyProvider, ConfigProvider, PlatformService, FileProvider, ProfilesService, ProfileProvider, SelectorOption, Profile, SelectorService, CommandProvider } from './api'
 
 import { AppService } from './services/app.service'
 import { ConfigService } from './services/config.service'
@@ -51,8 +51,8 @@ import { CoreConfigProvider } from './config'
 import { AppHotkeyProvider } from './hotkeys'
 import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
 import { LastCLIHandler, ProfileCLIHandler } from './cli'
-import { ButtonProvider } from './buttonProvider'
 import { SplitLayoutProfilesService } from './profiles'
+import { CoreCommandProvider } from './commands'
 
 import 'perfect-scrollbar/css/perfect-scrollbar.css'
 
@@ -75,8 +75,8 @@ const PROVIDERS = [
     { provide: CLIHandler, useClass: LastCLIHandler, multi: true },
     { provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
     { provide: FileProvider, useClass: VaultFileProvider, multi: true },
-    { provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
     { provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
+    { provide: CommandProvider, useExisting: CoreCommandProvider, multi: true },
     {
         provide: LOCALE_ID,
         deps: [LocaleService],
@@ -180,7 +180,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
             console.error('Unhandled exception:', err)
         })
 
-        hotkeys.hotkey$.subscribe(async (hotkey) => {
+        hotkeys.hotkey$.subscribe(async hotkey => {
             if (hotkey.startsWith('profile.')) {
                 const id = hotkey.substring(hotkey.indexOf('.') + 1)
                 const profiles = await profilesService.getProfiles()
@@ -200,6 +200,10 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
             if (hotkey === 'command-selector') {
                 commands.showSelector()
             }
+
+            if (hotkey === 'profile-selector') {
+                commands.run('profile-selector', {})
+            }
         })
     }
 

+ 16 - 5
tabby-core/src/services/commands.service.ts

@@ -1,5 +1,5 @@
 import { Inject, Injectable, Optional } from '@angular/core'
-import { AppService, Command, CommandContext, ConfigService, MenuItemOptions, SplitTabComponent, TabContextMenuItemProvider, ToolbarButton, ToolbarButtonProvider, TranslateService } from '../api'
+import { AppService, Command, CommandContext, CommandProvider, ConfigService, MenuItemOptions, SplitTabComponent, TabContextMenuItemProvider, ToolbarButton, ToolbarButtonProvider, TranslateService } from '../api'
 import { SelectorService } from './selector.service'
 
 @Injectable({ providedIn: 'root' })
@@ -11,6 +11,7 @@ export class CommandService {
         private translate: TranslateService,
         @Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
         @Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
+        @Inject(CommandProvider) private commandProviders: CommandProvider[],
     ) {
         this.contextMenuProviders.sort((a, b) => a.weight - b.weight)
     }
@@ -60,10 +61,20 @@ export class CommandService {
         }
         items.forEach(x => flattenItem(x))
 
-        let commands = buttons.map(x => Command.fromToolbarButton(x))
-        commands = commands.concat(flatItems.map(x => Command.fromMenuItem(x)))
+        const commands = buttons.map(x => Command.fromToolbarButton(x))
+        commands.push(...flatItems.map(x => Command.fromMenuItem(x)))
 
-        return commands
+        for (const provider of this.config.enabledServices(this.commandProviders)) {
+            commands.push(...await provider.provide(context))
+        }
+
+        return commands.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
+    }
+
+    async run (id: string, context: CommandContext): Promise<void> {
+        const commands = await this.getCommands(context)
+        const command = commands.find(x => x.id === id)
+        await command?.run()
     }
 
     async showSelector (): Promise<void> {
@@ -81,7 +92,7 @@ export class CommandService {
             this.translate.instant('Commands'),
             commands.map(c => ({
                 name: c.label,
-                callback: c.click,
+                callback: c.run,
                 description: c.sublabel,
                 icon: c.icon,
             })),

+ 3 - 3
tabby-settings/src/components/releaseNotesTab.component.ts

@@ -2,7 +2,7 @@
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
 import axios from 'axios'
 import { marked } from 'marked'
-import { Component } from '@angular/core'
+import { Component, Injector } from '@angular/core'
 import { BaseTabComponent, TranslateService } from 'tabby-core'
 
 export interface Release {
@@ -22,8 +22,8 @@ export class ReleaseNotesComponent extends BaseTabComponent {
     releases: Release[] = []
     lastPage = 1
 
-    constructor (translate: TranslateService) {
-        super()
+    constructor (translate: TranslateService, injector: Injector) {
+        super(injector)
         this.setTitle(translate.instant(_('Release notes')))
         this.loadReleases(1)
     }

+ 3 - 2
tabby-settings/src/components/settingsTab.component.ts

@@ -2,7 +2,7 @@
 import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
 import * as yaml from 'js-yaml'
 import { debounce } from 'utils-decorators/dist/esm/debounce/debounce'
-import { Component, Inject, Input, HostBinding, NgZone } from '@angular/core'
+import { Component, Inject, Input, HostBinding, NgZone, Injector } from '@angular/core'
 import {
     ConfigService,
     BaseTabComponent,
@@ -52,8 +52,9 @@ export class SettingsTabComponent extends BaseTabComponent {
         private app: AppService,
         @Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
         translate: TranslateService,
+        injector: Injector,
     ) {
-        super()
+        super(injector)
         this.setTitle(translate.instant(_('Settings')))
         this.settingsProviders = config.enabledServices(this.settingsProviders)
         this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())

+ 1 - 0
tabby-telnet/package.json

@@ -17,6 +17,7 @@
   "author": "Eugene Pankov",
   "license": "MIT",
   "devDependencies": {
+    "ansi-colors": "^4.1.1",
     "@types/node": "14.14.31"
   },
   "peerDependencies": {

+ 5 - 0
tabby-telnet/yarn.lock

@@ -6,3 +6,8 @@
   version "14.14.31"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
   integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
+
+ansi-colors@^4.1.1:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+  integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==

+ 1 - 1
tabby-terminal/src/api/baseTerminalTab.component.ts

@@ -175,7 +175,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
     get sessionChanged$ (): Observable<BaseSession|null> { return this.sessionChanged }
 
     constructor (protected injector: Injector) {
-        super()
+        super(injector)
 
         this.config = injector.get(ConfigService)
         this.element = injector.get(ElementRef)