mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-28 15:18:33 +00:00
support for providing commands as toolbar buttons
This commit is contained in:
@@ -2,21 +2,51 @@ import { BaseTabComponent } from '../components/baseTab.component'
|
|||||||
import { MenuItemOptions } from './menu'
|
import { MenuItemOptions } from './menu'
|
||||||
import { ToolbarButton } from './toolbarButtonProvider'
|
import { ToolbarButton } from './toolbarButtonProvider'
|
||||||
|
|
||||||
|
export enum CommandLocation {
|
||||||
|
LeftToolbar = 'left-toolbar',
|
||||||
|
RightToolbar = 'right-toolbar',
|
||||||
|
StartPage = 'start-page',
|
||||||
|
}
|
||||||
|
|
||||||
export class Command {
|
export class Command {
|
||||||
|
id?: string
|
||||||
label: string
|
label: string
|
||||||
sublabel?: string
|
sublabel?: string
|
||||||
click?: () => void
|
locations?: CommandLocation[]
|
||||||
|
run: () => Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw SVG icon code
|
* Raw SVG icon code
|
||||||
*/
|
*/
|
||||||
icon?: string
|
icon?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional Touch Bar icon ID
|
||||||
|
*/
|
||||||
|
touchBarNSImage?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional Touch Bar button label
|
||||||
|
*/
|
||||||
|
touchBarTitle?: string
|
||||||
|
|
||||||
|
weight?: number
|
||||||
|
|
||||||
static fromToolbarButton (button: ToolbarButton): Command {
|
static fromToolbarButton (button: ToolbarButton): Command {
|
||||||
const command = new Command()
|
const command = new Command()
|
||||||
command.label = button.commandLabel ?? button.title
|
command.label = button.title
|
||||||
command.click = button.click
|
command.run = async () => button.click?.()
|
||||||
command.icon = button.icon
|
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
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +54,7 @@ export class Command {
|
|||||||
const command = new Command()
|
const command = new Command()
|
||||||
command.label = item.commandLabel ?? item.label ?? ''
|
command.label = item.commandLabel ?? item.label ?? ''
|
||||||
command.sublabel = item.sublabel
|
command.sublabel = item.sublabel
|
||||||
command.click = item.click
|
command.run = async () => item.click?.()
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,3 +62,10 @@ export class Command {
|
|||||||
export interface CommandContext {
|
export interface CommandContext {
|
||||||
tab?: BaseTabComponent,
|
tab?: BaseTabComponent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend to add commands
|
||||||
|
*/
|
||||||
|
export abstract class CommandProvider {
|
||||||
|
abstract provide (context: CommandContext): Promise<Command[]>
|
||||||
|
}
|
||||||
|
@@ -27,13 +27,6 @@ export interface ToolbarButton {
|
|||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
submenuItems?: ToolbarButton[]
|
submenuItems?: ToolbarButton[]
|
||||||
|
|
||||||
showInToolbar?: boolean
|
|
||||||
|
|
||||||
showInStartPage?: boolean
|
|
||||||
|
|
||||||
/** @hidden */
|
|
||||||
commandLabel?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,26 +2,19 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { TranslateService } from '@ngx-translate/core'
|
import { TranslateService } from '@ngx-translate/core'
|
||||||
|
|
||||||
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
|
|
||||||
import { HostAppService, Platform } from './api/hostApp'
|
import { HostAppService, Platform } from './api/hostApp'
|
||||||
import { HotkeysService } from './services/hotkeys.service'
|
|
||||||
import { ProfilesService } from './services/profiles.service'
|
import { ProfilesService } from './services/profiles.service'
|
||||||
|
import { CommandProvider, Command, CommandLocation } from './api/commands'
|
||||||
|
|
||||||
/** @hidden */
|
/** @hidden */
|
||||||
@Injectable()
|
@Injectable({ providedIn: 'root' })
|
||||||
export class ButtonProvider extends ToolbarButtonProvider {
|
export class CoreCommandProvider extends CommandProvider {
|
||||||
constructor (
|
constructor (
|
||||||
private hostApp: HostAppService,
|
private hostApp: HostAppService,
|
||||||
private profilesService: ProfilesService,
|
private profilesService: ProfilesService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
hotkeys: HotkeysService,
|
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
hotkeys.hotkey$.subscribe(hotkey => {
|
|
||||||
if (hotkey === 'profile-selector') {
|
|
||||||
this.activate()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async activate () {
|
async activate () {
|
||||||
@@ -31,21 +24,22 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provide (): ToolbarButton[] {
|
async provide (): Promise<Command[]> {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
id: 'profile-selector',
|
||||||
|
locations: [CommandLocation.LeftToolbar, CommandLocation.StartPage],
|
||||||
|
label: this.translate.instant('Profiles & connections'),
|
||||||
icon: this.hostApp.platform === Platform.Web
|
icon: this.hostApp.platform === Platform.Web
|
||||||
? require('./icons/plus.svg')
|
? require('./icons/plus.svg')
|
||||||
: require('./icons/profiles.svg'),
|
: require('./icons/profiles.svg'),
|
||||||
title: this.translate.instant('Profiles & connections'),
|
run: async () => this.activate(),
|
||||||
click: () => this.activate(),
|
|
||||||
},
|
},
|
||||||
...this.profilesService.getRecentProfiles().map(profile => ({
|
...this.profilesService.getRecentProfiles().map(profile => ({
|
||||||
|
label: profile.name,
|
||||||
|
locations: [CommandLocation.StartPage],
|
||||||
icon: require('./icons/history.svg'),
|
icon: require('./icons/history.svg'),
|
||||||
title: profile.name,
|
run: async () => {
|
||||||
showInToolbar: false,
|
|
||||||
showinStartPage: true,
|
|
||||||
click: async () => {
|
|
||||||
const p = (await this.profilesService.getProfiles()).find(x => x.id === profile.id) ?? profile
|
const p = (await this.profilesService.getProfiles()).find(x => x.id === profile.id) ?? profile
|
||||||
this.profilesService.launchProfile(p)
|
this.profilesService.launchProfile(p)
|
||||||
},
|
},
|
@@ -38,26 +38,14 @@ title-bar(
|
|||||||
.btn-group.background
|
.btn-group.background
|
||||||
.d-flex(
|
.d-flex(
|
||||||
*ngFor='let button of leftToolbarButtons',
|
*ngFor='let button of leftToolbarButtons',
|
||||||
ngbDropdown,
|
ngbDropdown
|
||||||
(openChange)='generateButtonSubmenu(button)',
|
|
||||||
)
|
)
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
[title]='button.title',
|
[title]='button.label',
|
||||||
(click)='button.click && button.click()',
|
(click)='button.run && button.run()',
|
||||||
[fastHtmlBind]='button.icon',
|
[fastHtmlBind]='button.icon',
|
||||||
ngbDropdownToggle,
|
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(
|
.d-flex(
|
||||||
ngbDropdown,
|
ngbDropdown,
|
||||||
@@ -80,26 +68,14 @@ title-bar(
|
|||||||
.btn-group.background
|
.btn-group.background
|
||||||
.d-flex(
|
.d-flex(
|
||||||
*ngFor='let button of rightToolbarButtons',
|
*ngFor='let button of rightToolbarButtons',
|
||||||
ngbDropdown,
|
ngbDropdown
|
||||||
(openChange)='generateButtonSubmenu(button)',
|
|
||||||
)
|
)
|
||||||
button.btn.btn-secondary.btn-tab-bar(
|
button.btn.btn-secondary.btn-tab-bar(
|
||||||
[title]='button.title',
|
[title]='button.title',
|
||||||
(click)='button.click && button.click()',
|
(click)='button.run && button.run()',
|
||||||
[fastHtmlBind]='button.icon',
|
[fastHtmlBind]='button.icon',
|
||||||
ngbDropdownToggle,
|
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(
|
button.btn.btn-secondary.btn-tab-bar.btn-update(
|
||||||
*ngIf='updatesAvailable',
|
*ngIf='updatesAvailable',
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* 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 { trigger, style, animate, transition, state } from '@angular/animations'
|
||||||
import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'
|
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 { ConfigService } from '../services/config.service'
|
||||||
import { ThemesService } from '../services/themes.service'
|
import { ThemesService } from '../services/themes.service'
|
||||||
import { UpdaterService } from '../services/updater.service'
|
import { UpdaterService } from '../services/updater.service'
|
||||||
|
import { CommandService } from '../services/commands.service'
|
||||||
|
|
||||||
import { BaseTabComponent } from './baseTab.component'
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
import { SafeModeModalComponent } from './safeModeModal.component'
|
import { SafeModeModalComponent } from './safeModeModal.component'
|
||||||
import { TabBodyComponent } from './tabBody.component'
|
import { TabBodyComponent } from './tabBody.component'
|
||||||
import { SplitTabComponent } from './splitTab.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) {
|
function makeTabAnimation (dimension: string, size: number) {
|
||||||
return [
|
return [
|
||||||
@@ -63,8 +64,8 @@ function makeTabAnimation (dimension: string, size: number) {
|
|||||||
export class AppRootComponent {
|
export class AppRootComponent {
|
||||||
Platform = Platform
|
Platform = Platform
|
||||||
@Input() ready = false
|
@Input() ready = false
|
||||||
@Input() leftToolbarButtons: ToolbarButton[]
|
@Input() leftToolbarButtons: Command[]
|
||||||
@Input() rightToolbarButtons: ToolbarButton[]
|
@Input() rightToolbarButtons: Command[]
|
||||||
@HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
|
@HostBinding('class.platform-win32') platformClassWindows = process.platform === 'win32'
|
||||||
@HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
|
@HostBinding('class.platform-darwin') platformClassMacOS = process.platform === 'darwin'
|
||||||
@HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
|
@HostBinding('class.platform-linux') platformClassLinux = process.platform === 'linux'
|
||||||
@@ -79,11 +80,11 @@ export class AppRootComponent {
|
|||||||
constructor (
|
constructor (
|
||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
private updater: UpdaterService,
|
private updater: UpdaterService,
|
||||||
|
private commands: CommandService,
|
||||||
public hostWindow: HostWindowService,
|
public hostWindow: HostWindowService,
|
||||||
public hostApp: HostAppService,
|
public hostApp: HostAppService,
|
||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
public app: AppService,
|
public app: AppService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
|
||||||
platform: PlatformService,
|
platform: PlatformService,
|
||||||
log: LogService,
|
log: LogService,
|
||||||
ngbModal: NgbModal,
|
ngbModal: NgbModal,
|
||||||
@@ -170,9 +171,9 @@ export class AppRootComponent {
|
|||||||
this.activeTransfersDropdown.open()
|
this.activeTransfersDropdown.open()
|
||||||
})
|
})
|
||||||
|
|
||||||
config.ready$.toPromise().then(() => {
|
config.ready$.toPromise().then(async () => {
|
||||||
this.leftToolbarButtons = this.getToolbarButtons(false)
|
this.leftToolbarButtons = await this.getToolbarButtons(false)
|
||||||
this.rightToolbarButtons = this.getToolbarButtons(true)
|
this.rightToolbarButtons = await this.getToolbarButtons(true)
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (this.config.store.enableAutomaticUpdates) {
|
if (this.config.store.enableAutomaticUpdates) {
|
||||||
@@ -212,16 +213,6 @@ export class AppRootComponent {
|
|||||||
return this.config.store.appearance.flexTabs ? '*' : '200px'
|
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[]>) {
|
onTabsReordered (event: CdkDragDrop<BaseTabComponent[]>) {
|
||||||
const tab: BaseTabComponent = event.item.data
|
const tab: BaseTabComponent = event.item.data
|
||||||
if (!this.app.tabs.includes(tab)) {
|
if (!this.app.tabs.includes(tab)) {
|
||||||
@@ -244,14 +235,8 @@ export class AppRootComponent {
|
|||||||
return this.config.store?.appearance.vibrancy
|
return this.config.store?.appearance.vibrancy
|
||||||
}
|
}
|
||||||
|
|
||||||
private getToolbarButtons (aboveZero: boolean): ToolbarButton[] {
|
private async getToolbarButtons (aboveZero: boolean): Promise<Command[]> {
|
||||||
let buttons: ToolbarButton[] = []
|
return (await this.commands.getCommands({ tab: this.app.activeTab ?? undefined }))
|
||||||
this.config.enabledServices(this.toolbarButtonProviders).forEach(provider => {
|
.filter(x => x.locations?.includes(aboveZero ? CommandLocation.RightToolbar : CommandLocation.LeftToolbar))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { Observable, Subject, distinctUntilChanged, filter, debounceTime } from 'rxjs'
|
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 { RecoveryToken } from '../api/tabRecovery'
|
||||||
import { BaseComponent } from './base.component'
|
import { BaseComponent } from './base.component'
|
||||||
|
import { ConfigService } from '../services/config.service'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an active "process" inside a tab,
|
* 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 destroyed$ (): Observable<void> { return this.destroyed }
|
||||||
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
|
get recoveryStateChangedHint$ (): Observable<void> { return this.recoveryStateChangedHint }
|
||||||
|
|
||||||
protected constructor () {
|
protected config: ConfigService
|
||||||
|
|
||||||
|
protected constructor (injector: Injector) {
|
||||||
super()
|
super()
|
||||||
|
this.config = injector.get(ConfigService)
|
||||||
this.focused$.subscribe(() => {
|
this.focused$.subscribe(() => {
|
||||||
this.hasFocus = true
|
this.hasFocus = true
|
||||||
})
|
})
|
||||||
|
@@ -97,8 +97,8 @@ export class SelectorModalComponent<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectOption (option: SelectorOption<T>): void {
|
selectOption (option: SelectorOption<T>): void {
|
||||||
option.callback?.(this.filter)
|
|
||||||
this.modalInstance.close(option.result)
|
this.modalInstance.close(option.result)
|
||||||
|
setTimeout(() => option.callback?.(this.filter))
|
||||||
}
|
}
|
||||||
|
|
||||||
canEditSelected (): boolean {
|
canEditSelected (): boolean {
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Observable, Subject } from 'rxjs'
|
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 { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from './baseTab.component'
|
||||||
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
|
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
|
||||||
import { TabsService, NewTabParameters } from '../services/tabs.service'
|
import { TabsService, NewTabParameters } from '../services/tabs.service'
|
||||||
import { HotkeysService } from '../services/hotkeys.service'
|
import { HotkeysService } from '../services/hotkeys.service'
|
||||||
import { TabRecoveryService } from '../services/tabRecovery.service'
|
import { TabRecoveryService } from '../services/tabRecovery.service'
|
||||||
import { ConfigService } from '../api'
|
|
||||||
|
|
||||||
export type SplitOrientation = 'v' | 'h'
|
export type SplitOrientation = 'v' | 'h'
|
||||||
export type SplitDirection = 'r' | 't' | 'b' | 'l'
|
export type SplitDirection = 'r' | 't' | 'b' | 'l'
|
||||||
@@ -261,9 +260,9 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
|
|||||||
private hotkeys: HotkeysService,
|
private hotkeys: HotkeysService,
|
||||||
private tabsService: TabsService,
|
private tabsService: TabsService,
|
||||||
private tabRecovery: TabRecoveryService,
|
private tabRecovery: TabRecoveryService,
|
||||||
private config: ConfigService,
|
injector: Injector,
|
||||||
) {
|
) {
|
||||||
super()
|
super(injector)
|
||||||
this.root = new SplitContainer()
|
this.root = new SplitContainer()
|
||||||
this.setTitle('')
|
this.setTitle('')
|
||||||
|
|
||||||
|
@@ -5,11 +5,11 @@ div
|
|||||||
|
|
||||||
.list-group.mb-4
|
.list-group.mb-4
|
||||||
a.list-group-item.list-group-item-action.d-flex(
|
a.list-group-item.list-group-item-action.d-flex(
|
||||||
*ngFor='let button of getButtons(); trackBy: buttonsTrackBy',
|
*ngFor='let command of commands; trackBy: buttonsTrackBy',
|
||||||
(click)='button.click()',
|
(click)='command.run()',
|
||||||
)
|
)
|
||||||
.d-flex.align-self-center([innerHTML]='sanitizeIcon(button.icon)')
|
.d-flex.align-self-center([innerHTML]='sanitizeIcon(command.icon)')
|
||||||
span {{button.title}}
|
span {{command.label}}
|
||||||
|
|
||||||
footer.d-flex.align-items-center
|
footer.d-flex.align-items-center
|
||||||
.btn-group.mr-auto
|
.btn-group.mr-auto
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { Component, Inject } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { DomSanitizer } from '@angular/platform-browser'
|
import { DomSanitizer } from '@angular/platform-browser'
|
||||||
import { ConfigService } from '../services/config.service'
|
|
||||||
import { HomeBaseService } from '../services/homeBase.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 */
|
/** @hidden */
|
||||||
@Component({
|
@Component({
|
||||||
@@ -12,29 +12,23 @@ import { ToolbarButton, ToolbarButtonProvider } from '../api'
|
|||||||
})
|
})
|
||||||
export class StartPageComponent {
|
export class StartPageComponent {
|
||||||
version: string
|
version: string
|
||||||
|
commands: Command[] = []
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private config: ConfigService,
|
|
||||||
private domSanitizer: DomSanitizer,
|
private domSanitizer: DomSanitizer,
|
||||||
public homeBase: HomeBaseService,
|
public homeBase: HomeBaseService,
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
commands: CommandService,
|
||||||
) {
|
) {
|
||||||
}
|
commands.getCommands({}).then(c => {
|
||||||
|
this.commands = c.filter(x => x.locations?.includes(CommandLocation.StartPage))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sanitizeIcon (icon?: string): any {
|
sanitizeIcon (icon?: string): any {
|
||||||
return this.domSanitizer.bypassSecurityTrustHtml(icon ?? '')
|
return this.domSanitizer.bypassSecurityTrustHtml(icon ?? '')
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonsTrackBy (btn: ToolbarButton): any {
|
buttonsTrackBy (btn: Command): any {
|
||||||
return btn.title + btn.icon
|
return btn.label + btn.icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* 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 { TranslateService } from '@ngx-translate/core'
|
||||||
import { BaseTabComponent } from './baseTab.component'
|
import { BaseTabComponent } from './baseTab.component'
|
||||||
import { ConfigService } from '../services/config.service'
|
import { ConfigService } from '../services/config.service'
|
||||||
@@ -19,8 +19,9 @@ export class WelcomeTabComponent extends BaseTabComponent {
|
|||||||
public config: ConfigService,
|
public config: ConfigService,
|
||||||
public locale: LocaleService,
|
public locale: LocaleService,
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
injector: Injector,
|
||||||
) {
|
) {
|
||||||
super()
|
super(injector)
|
||||||
this.setTitle(translate.instant('Welcome'))
|
this.setTitle(translate.instant('Welcome'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -37,7 +37,7 @@ import { FastHtmlBindDirective } from './directives/fastHtmlBind.directive'
|
|||||||
import { DropZoneDirective } from './directives/dropZone.directive'
|
import { DropZoneDirective } from './directives/dropZone.directive'
|
||||||
import { CdkAutoDropGroup } from './directives/cdkAutoDropGroup.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 { AppService } from './services/app.service'
|
||||||
import { ConfigService } from './services/config.service'
|
import { ConfigService } from './services/config.service'
|
||||||
@@ -51,8 +51,8 @@ import { CoreConfigProvider } from './config'
|
|||||||
import { AppHotkeyProvider } from './hotkeys'
|
import { AppHotkeyProvider } from './hotkeys'
|
||||||
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
|
import { TaskCompletionContextMenu, CommonOptionsContextMenu, TabManagementContextMenu, ProfilesContextMenu } from './tabContextMenu'
|
||||||
import { LastCLIHandler, ProfileCLIHandler } from './cli'
|
import { LastCLIHandler, ProfileCLIHandler } from './cli'
|
||||||
import { ButtonProvider } from './buttonProvider'
|
|
||||||
import { SplitLayoutProfilesService } from './profiles'
|
import { SplitLayoutProfilesService } from './profiles'
|
||||||
|
import { CoreCommandProvider } from './commands'
|
||||||
|
|
||||||
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
import 'perfect-scrollbar/css/perfect-scrollbar.css'
|
||||||
|
|
||||||
@@ -75,8 +75,8 @@ const PROVIDERS = [
|
|||||||
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true },
|
{ provide: CLIHandler, useClass: LastCLIHandler, multi: true },
|
||||||
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
|
{ provide: PERFECT_SCROLLBAR_CONFIG, useValue: { suppressScrollX: true } },
|
||||||
{ provide: FileProvider, useClass: VaultFileProvider, multi: true },
|
{ provide: FileProvider, useClass: VaultFileProvider, multi: true },
|
||||||
{ provide: ToolbarButtonProvider, useClass: ButtonProvider, multi: true },
|
|
||||||
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
|
{ provide: ProfileProvider, useExisting: SplitLayoutProfilesService, multi: true },
|
||||||
|
{ provide: CommandProvider, useExisting: CoreCommandProvider, multi: true },
|
||||||
{
|
{
|
||||||
provide: LOCALE_ID,
|
provide: LOCALE_ID,
|
||||||
deps: [LocaleService],
|
deps: [LocaleService],
|
||||||
@@ -180,7 +180,7 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
|||||||
console.error('Unhandled exception:', err)
|
console.error('Unhandled exception:', err)
|
||||||
})
|
})
|
||||||
|
|
||||||
hotkeys.hotkey$.subscribe(async (hotkey) => {
|
hotkeys.hotkey$.subscribe(async hotkey => {
|
||||||
if (hotkey.startsWith('profile.')) {
|
if (hotkey.startsWith('profile.')) {
|
||||||
const id = hotkey.substring(hotkey.indexOf('.') + 1)
|
const id = hotkey.substring(hotkey.indexOf('.') + 1)
|
||||||
const profiles = await profilesService.getProfiles()
|
const profiles = await profilesService.getProfiles()
|
||||||
@@ -200,6 +200,10 @@ export default class AppModule { // eslint-disable-line @typescript-eslint/no-ex
|
|||||||
if (hotkey === 'command-selector') {
|
if (hotkey === 'command-selector') {
|
||||||
commands.showSelector()
|
commands.showSelector()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hotkey === 'profile-selector') {
|
||||||
|
commands.run('profile-selector', {})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Inject, Injectable, Optional } from '@angular/core'
|
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'
|
import { SelectorService } from './selector.service'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
@@ -11,6 +11,7 @@ export class CommandService {
|
|||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
|
@Optional() @Inject(TabContextMenuItemProvider) protected contextMenuProviders: TabContextMenuItemProvider[],
|
||||||
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
@Inject(ToolbarButtonProvider) private toolbarButtonProviders: ToolbarButtonProvider[],
|
||||||
|
@Inject(CommandProvider) private commandProviders: CommandProvider[],
|
||||||
) {
|
) {
|
||||||
this.contextMenuProviders.sort((a, b) => a.weight - b.weight)
|
this.contextMenuProviders.sort((a, b) => a.weight - b.weight)
|
||||||
}
|
}
|
||||||
@@ -60,10 +61,20 @@ export class CommandService {
|
|||||||
}
|
}
|
||||||
items.forEach(x => flattenItem(x))
|
items.forEach(x => flattenItem(x))
|
||||||
|
|
||||||
let commands = buttons.map(x => Command.fromToolbarButton(x))
|
const commands = buttons.map(x => Command.fromToolbarButton(x))
|
||||||
commands = commands.concat(flatItems.map(x => Command.fromMenuItem(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> {
|
async showSelector (): Promise<void> {
|
||||||
@@ -81,7 +92,7 @@ export class CommandService {
|
|||||||
this.translate.instant('Commands'),
|
this.translate.instant('Commands'),
|
||||||
commands.map(c => ({
|
commands.map(c => ({
|
||||||
name: c.label,
|
name: c.label,
|
||||||
callback: c.click,
|
callback: c.run,
|
||||||
description: c.sublabel,
|
description: c.sublabel,
|
||||||
icon: c.icon,
|
icon: c.icon,
|
||||||
})),
|
})),
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import { Component } from '@angular/core'
|
import { Component, Injector } from '@angular/core'
|
||||||
import { BaseTabComponent, TranslateService } from 'tabby-core'
|
import { BaseTabComponent, TranslateService } from 'tabby-core'
|
||||||
|
|
||||||
export interface Release {
|
export interface Release {
|
||||||
@@ -22,8 +22,8 @@ export class ReleaseNotesComponent extends BaseTabComponent {
|
|||||||
releases: Release[] = []
|
releases: Release[] = []
|
||||||
lastPage = 1
|
lastPage = 1
|
||||||
|
|
||||||
constructor (translate: TranslateService) {
|
constructor (translate: TranslateService, injector: Injector) {
|
||||||
super()
|
super(injector)
|
||||||
this.setTitle(translate.instant(_('Release notes')))
|
this.setTitle(translate.instant(_('Release notes')))
|
||||||
this.loadReleases(1)
|
this.loadReleases(1)
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'
|
||||||
import * as yaml from 'js-yaml'
|
import * as yaml from 'js-yaml'
|
||||||
import { debounce } from 'utils-decorators/dist/esm/debounce/debounce'
|
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 {
|
import {
|
||||||
ConfigService,
|
ConfigService,
|
||||||
BaseTabComponent,
|
BaseTabComponent,
|
||||||
@@ -52,8 +52,9 @@ export class SettingsTabComponent extends BaseTabComponent {
|
|||||||
private app: AppService,
|
private app: AppService,
|
||||||
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
|
@Inject(SettingsTabProvider) public settingsProviders: SettingsTabProvider[],
|
||||||
translate: TranslateService,
|
translate: TranslateService,
|
||||||
|
injector: Injector,
|
||||||
) {
|
) {
|
||||||
super()
|
super(injector)
|
||||||
this.setTitle(translate.instant(_('Settings')))
|
this.setTitle(translate.instant(_('Settings')))
|
||||||
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
this.settingsProviders = config.enabledServices(this.settingsProviders)
|
||||||
this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())
|
this.settingsProviders = this.settingsProviders.filter(x => !!x.getComponentType())
|
||||||
|
@@ -17,6 +17,7 @@
|
|||||||
"author": "Eugene Pankov",
|
"author": "Eugene Pankov",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"ansi-colors": "^4.1.1",
|
||||||
"@types/node": "14.14.31"
|
"@types/node": "14.14.31"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
@@ -6,3 +6,8 @@
|
|||||||
version "14.14.31"
|
version "14.14.31"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
|
||||||
integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
|
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==
|
||||||
|
@@ -175,7 +175,7 @@ export class BaseTerminalTabComponent extends BaseTabComponent implements OnInit
|
|||||||
get sessionChanged$ (): Observable<BaseSession|null> { return this.sessionChanged }
|
get sessionChanged$ (): Observable<BaseSession|null> { return this.sessionChanged }
|
||||||
|
|
||||||
constructor (protected injector: Injector) {
|
constructor (protected injector: Injector) {
|
||||||
super()
|
super(injector)
|
||||||
|
|
||||||
this.config = injector.get(ConfigService)
|
this.config = injector.get(ConfigService)
|
||||||
this.element = injector.get(ElementRef)
|
this.element = injector.get(ElementRef)
|
||||||
|
Reference in New Issue
Block a user