mirror of
https://github.com/Eugeny/tabby.git
synced 2025-07-19 18:07:58 +00:00
strongly typed partial profiles wip
This commit is contained in:
@@ -16,7 +16,7 @@ export { BootstrapData, PluginInfo, BOOTSTRAP_DATA } from './mainProcess'
|
|||||||
export { HostWindowService } from './hostWindow'
|
export { HostWindowService } from './hostWindow'
|
||||||
export { HostAppService, Platform } from './hostApp'
|
export { HostAppService, Platform } from './hostApp'
|
||||||
export { FileProvider } from './fileProvider'
|
export { FileProvider } from './fileProvider'
|
||||||
export { ProfileProvider, Profile, ProfileSettingsComponent } from './profileProvider'
|
export { ProfileProvider, Profile, PartialProfile, ProfileSettingsComponent } from './profileProvider'
|
||||||
export { PromptModalComponent } from '../components/promptModal.component'
|
export { PromptModalComponent } from '../components/promptModal.component'
|
||||||
|
|
||||||
export { AppService } from '../services/app.service'
|
export { AppService } from '../services/app.service'
|
||||||
|
@@ -1,45 +1,56 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-type-alias */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { NewTabParameters } from '../services/tabs.service'
|
import { NewTabParameters } from '../services/tabs.service'
|
||||||
|
|
||||||
export interface Profile {
|
export interface Profile {
|
||||||
id?: string
|
id: string
|
||||||
type: string
|
type: string
|
||||||
name: string
|
name: string
|
||||||
group?: string
|
group?: string
|
||||||
options: Record<string, any>
|
options: any
|
||||||
|
|
||||||
icon?: string
|
icon?: string
|
||||||
color?: string
|
color?: string
|
||||||
disableDynamicTitle?: boolean
|
disableDynamicTitle: boolean
|
||||||
|
|
||||||
weight?: number
|
weight: number
|
||||||
isBuiltin?: boolean
|
isBuiltin: boolean
|
||||||
isTemplate?: boolean
|
isTemplate: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProfileSettingsComponent {
|
export type PartialProfile<T extends Profile> = Omit<Omit<Omit<{
|
||||||
profile: Profile
|
[K in keyof T]?: T[K]
|
||||||
|
}, 'options'>, 'type'>, 'name'> & {
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
options?: {
|
||||||
|
[K in keyof T['options']]?: T['options'][K]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProfileSettingsComponent<P extends Profile> {
|
||||||
|
profile: P
|
||||||
save?: () => void
|
save?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ProfileProvider {
|
export abstract class ProfileProvider<P extends Profile> {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
supportsQuickConnect = false
|
supportsQuickConnect = false
|
||||||
settingsComponent?: new (...args: any[]) => ProfileSettingsComponent
|
settingsComponent?: new (...args: any[]) => ProfileSettingsComponent<P>
|
||||||
configDefaults = {}
|
configDefaults = {}
|
||||||
|
|
||||||
abstract getBuiltinProfiles (): Promise<Profile[]>
|
abstract getBuiltinProfiles (): Promise<PartialProfile<P>[]>
|
||||||
|
|
||||||
abstract getNewTabParameters (profile: Profile): Promise<NewTabParameters<BaseTabComponent>>
|
abstract getNewTabParameters (profile: PartialProfile<P>): Promise<NewTabParameters<BaseTabComponent>>
|
||||||
|
|
||||||
abstract getDescription (profile: Profile): string
|
abstract getDescription (profile: PartialProfile<P>): string
|
||||||
|
|
||||||
quickConnect (query: string): Profile|null {
|
quickConnect (query: string): PartialProfile<P>|null {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProfile (profile: Profile): void { }
|
deleteProfile (profile: P): void { }
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'
|
|||||||
|
|
||||||
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
|
import { ToolbarButton, ToolbarButtonProvider } from './api/toolbarButtonProvider'
|
||||||
import { HostAppService, Platform } from './api/hostApp'
|
import { HostAppService, Platform } from './api/hostApp'
|
||||||
import { Profile } from './api/profileProvider'
|
import { PartialProfile, Profile } from './api/profileProvider'
|
||||||
import { ConfigService } from './services/config.service'
|
import { ConfigService } from './services/config.service'
|
||||||
import { HotkeysService } from './services/hotkeys.service'
|
import { HotkeysService } from './services/hotkeys.service'
|
||||||
import { ProfilesService } from './services/profiles.service'
|
import { ProfilesService } from './services/profiles.service'
|
||||||
@@ -32,7 +32,7 @@ export class ButtonProvider extends ToolbarButtonProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchProfile (profile: Profile) {
|
async launchProfile (profile: PartialProfile<Profile>) {
|
||||||
await this.profilesService.openNewTabForProfile(profile)
|
await this.profilesService.openNewTabForProfile(profile)
|
||||||
|
|
||||||
let recentProfiles = this.config.store.recentProfiles
|
let recentProfiles = this.config.store.recentProfiles
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ConfigService, NewTabParameters, Profile, ProfileProvider } from './api'
|
import { ConfigService, NewTabParameters, PartialProfile, Profile, ProfileProvider } from './api'
|
||||||
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
import { SplitTabComponent, SplitTabRecoveryProvider } from './components/splitTab.component'
|
||||||
|
|
||||||
export interface SplitLayoutProfileOptions {
|
export interface SplitLayoutProfileOptions {
|
||||||
@@ -13,7 +13,7 @@ export interface SplitLayoutProfile extends Profile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SplitLayoutProfilesService extends ProfileProvider {
|
export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfile> {
|
||||||
id = 'split-layout'
|
id = 'split-layout'
|
||||||
name = 'Saved layout'
|
name = 'Saved layout'
|
||||||
configDefaults = {
|
configDefaults = {
|
||||||
@@ -29,7 +29,7 @@ export class SplitLayoutProfilesService extends ProfileProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<Profile[]> {
|
async getBuiltinProfiles (): Promise<PartialProfile<SplitLayoutProfile>[]> {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ export class SplitLayoutProfilesService extends ProfileProvider {
|
|||||||
|
|
||||||
async createProfile (tab: SplitTabComponent, name: string): Promise<void> {
|
async createProfile (tab: SplitTabComponent, name: string): Promise<void> {
|
||||||
const token = await tab.getRecoveryToken()
|
const token = await tab.getRecoveryToken()
|
||||||
const profile: SplitLayoutProfile = {
|
const profile: PartialProfile<SplitLayoutProfile> = {
|
||||||
id: `${this.id}:custom:${slugify(name)}:${uuidv4()}`,
|
id: `${this.id}:custom:${slugify(name)}:${uuidv4()}`,
|
||||||
type: this.id,
|
type: this.id,
|
||||||
name,
|
name,
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { Injectable, Inject } from '@angular/core'
|
import { Injectable, Inject } from '@angular/core'
|
||||||
import { NewTabParameters } from './tabs.service'
|
import { NewTabParameters } from './tabs.service'
|
||||||
import { BaseTabComponent } from '../components/baseTab.component'
|
import { BaseTabComponent } from '../components/baseTab.component'
|
||||||
import { Profile, ProfileProvider } from '../api/profileProvider'
|
import { PartialProfile, Profile, ProfileProvider } from '../api/profileProvider'
|
||||||
import { SelectorOption } from '../api/selector'
|
;import { SelectorOption } from '../api/selector'
|
||||||
import { AppService } from './app.service'
|
import { AppService } from './app.service'
|
||||||
import { configMerge, ConfigProxy, ConfigService } from './config.service'
|
import { configMerge, ConfigProxy, ConfigService } from './config.service'
|
||||||
import { NotificationsService } from './notifications.service'
|
import { NotificationsService } from './notifications.service'
|
||||||
@@ -29,15 +29,18 @@ export class ProfilesService {
|
|||||||
private config: ConfigService,
|
private config: ConfigService,
|
||||||
private notifications: NotificationsService,
|
private notifications: NotificationsService,
|
||||||
private selector: SelectorService,
|
private selector: SelectorService,
|
||||||
@Inject(ProfileProvider) private profileProviders: ProfileProvider[],
|
@Inject(ProfileProvider) private profileProviders: ProfileProvider<Profile>[],
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async openNewTabForProfile (profile: Profile): Promise<BaseTabComponent|null> {
|
async openNewTabForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<BaseTabComponent|null> {
|
||||||
const params = await this.newTabParametersForProfile(profile)
|
const params = await this.newTabParametersForProfile(profile)
|
||||||
if (params) {
|
if (params) {
|
||||||
const tab = this.app.openNewTab(params)
|
const tab = this.app.openNewTab(params)
|
||||||
;(this.app.getParentTab(tab) ?? tab).color = profile.color ?? null
|
;(this.app.getParentTab(tab) ?? tab).color = profile.color ?? null
|
||||||
tab.setTitle(profile.name)
|
|
||||||
|
if (profile.name) {
|
||||||
|
tab.setTitle(profile.name)
|
||||||
|
}
|
||||||
if (profile.disableDynamicTitle) {
|
if (profile.disableDynamicTitle) {
|
||||||
tab['enableDynamicTitle'] = false
|
tab['enableDynamicTitle'] = false
|
||||||
}
|
}
|
||||||
@@ -46,16 +49,16 @@ export class ProfilesService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
async newTabParametersForProfile (profile: Profile): Promise<NewTabParameters<BaseTabComponent>|null> {
|
async newTabParametersForProfile <P extends Profile> (profile: PartialProfile<P>): Promise<NewTabParameters<BaseTabComponent>|null> {
|
||||||
profile = this.getConfigProxyForProfile(profile)
|
const fullProfile = this.getConfigProxyForProfile(profile)
|
||||||
return this.providerForProfile(profile)?.getNewTabParameters(profile) ?? null
|
return this.providerForProfile(fullProfile)?.getNewTabParameters(fullProfile) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviders (): ProfileProvider[] {
|
getProviders (): ProfileProvider<Profile>[] {
|
||||||
return [...this.profileProviders]
|
return [...this.profileProviders]
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProfiles (): Promise<Profile[]> {
|
async getProfiles (): Promise<PartialProfile<Profile>[]> {
|
||||||
const lists = await Promise.all(this.config.enabledServices(this.profileProviders).map(x => x.getBuiltinProfiles()))
|
const lists = await Promise.all(this.config.enabledServices(this.profileProviders).map(x => x.getBuiltinProfiles()))
|
||||||
let list = lists.reduce((a, b) => a.concat(b), [])
|
let list = lists.reduce((a, b) => a.concat(b), [])
|
||||||
list = [
|
list = [
|
||||||
@@ -68,28 +71,29 @@ export class ProfilesService {
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
providerForProfile (profile: Profile): ProfileProvider|null {
|
providerForProfile <T extends Profile> (profile: PartialProfile<T>): ProfileProvider<T>|null {
|
||||||
return this.profileProviders.find(x => x.id === profile.type) ?? null
|
const provider = this.profileProviders.find(x => x.id === profile.type) ?? null
|
||||||
|
return provider as unknown as ProfileProvider<T>|null
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription (profile: Profile): string|null {
|
getDescription <P extends Profile> (profile: PartialProfile<P>): string|null {
|
||||||
profile = this.getConfigProxyForProfile(profile)
|
profile = this.getConfigProxyForProfile(profile)
|
||||||
return this.providerForProfile(profile)?.getDescription(profile) ?? null
|
return this.providerForProfile(profile)?.getDescription(profile) ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
selectorOptionForProfile <T> (profile: Profile): SelectorOption<T> {
|
selectorOptionForProfile <P extends Profile, T> (profile: PartialProfile<P>): SelectorOption<T> {
|
||||||
profile = this.getConfigProxyForProfile(profile)
|
const fullProfile = this.getConfigProxyForProfile(profile)
|
||||||
return {
|
return {
|
||||||
icon: profile.icon,
|
icon: profile.icon,
|
||||||
name: profile.group ? `${profile.group} / ${profile.name}` : profile.name,
|
name: profile.group ? `${fullProfile.group} / ${fullProfile.name}` : fullProfile.name,
|
||||||
description: this.providerForProfile(profile)?.getDescription(profile),
|
description: this.providerForProfile(fullProfile)?.getDescription(fullProfile),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showProfileSelector (): Promise<Profile|null> {
|
showProfileSelector (): Promise<PartialProfile<Profile>|null> {
|
||||||
return new Promise<Profile|null>(async (resolve, reject) => {
|
return new Promise<PartialProfile<Profile>|null>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const recentProfiles: Profile[] = this.config.store.recentProfiles
|
const recentProfiles: PartialProfile<Profile>[] = this.config.store.recentProfiles
|
||||||
|
|
||||||
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
|
let options: SelectorOption<void>[] = recentProfiles.map(p => ({
|
||||||
...this.selectorOptionForProfile(p),
|
...this.selectorOptionForProfile(p),
|
||||||
@@ -159,7 +163,7 @@ export class ProfilesService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async quickConnect (query: string): Promise<Profile|null> {
|
async quickConnect (query: string): Promise<PartialProfile<Profile>|null> {
|
||||||
for (const provider of this.getProviders()) {
|
for (const provider of this.getProviders()) {
|
||||||
if (provider.supportsQuickConnect) {
|
if (provider.supportsQuickConnect) {
|
||||||
const profile = provider.quickConnect(query)
|
const profile = provider.quickConnect(query)
|
||||||
@@ -172,9 +176,9 @@ export class ProfilesService {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfigProxyForProfile (profile: Profile): Profile {
|
getConfigProxyForProfile <T extends Profile> (profile: PartialProfile<T>): T {
|
||||||
const provider = this.providerForProfile(profile)
|
const provider = this.providerForProfile(profile)
|
||||||
const defaults = configMerge(this.profileDefaults, provider?.configDefaults ?? {})
|
const defaults = configMerge(this.profileDefaults, provider?.configDefaults ?? {})
|
||||||
return new ConfigProxy(profile, defaults) as unknown as Profile
|
return new ConfigProxy(profile, defaults) as unknown as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@ import { ProfileSettingsComponent } from 'tabby-core'
|
|||||||
@Component({
|
@Component({
|
||||||
template: require('./localProfileSettings.component.pug'),
|
template: require('./localProfileSettings.component.pug'),
|
||||||
})
|
})
|
||||||
export class LocalProfileSettingsComponent implements ProfileSettingsComponent {
|
export class LocalProfileSettingsComponent implements ProfileSettingsComponent<LocalProfile> {
|
||||||
profile: LocalProfile
|
profile: LocalProfile
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import deepClone from 'clone-deep'
|
import deepClone from 'clone-deep'
|
||||||
import { Injectable, Inject } from '@angular/core'
|
import { Injectable, Inject } from '@angular/core'
|
||||||
import { ProfileProvider, Profile, NewTabParameters, ConfigService, SplitTabComponent, AppService } from 'tabby-core'
|
import { ProfileProvider, NewTabParameters, ConfigService, SplitTabComponent, AppService, PartialProfile } from 'tabby-core'
|
||||||
import { TerminalTabComponent } from './components/terminalTab.component'
|
import { TerminalTabComponent } from './components/terminalTab.component'
|
||||||
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
|
import { LocalProfileSettingsComponent } from './components/localProfileSettings.component'
|
||||||
import { ShellProvider, Shell, SessionOptions } from './api'
|
import { ShellProvider, Shell, SessionOptions, LocalProfile } from './api'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class LocalProfilesService extends ProfileProvider {
|
export class LocalProfilesService extends ProfileProvider<LocalProfile> {
|
||||||
id = 'local'
|
id = 'local'
|
||||||
name = 'Local'
|
name = 'Local'
|
||||||
settingsComponent = LocalProfileSettingsComponent
|
settingsComponent = LocalProfileSettingsComponent
|
||||||
@@ -34,7 +34,7 @@ export class LocalProfilesService extends ProfileProvider {
|
|||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<Profile[]> {
|
async getBuiltinProfiles (): Promise<PartialProfile<LocalProfile>[]> {
|
||||||
return (await this.getShells()).map(shell => ({
|
return (await this.getShells()).map(shell => ({
|
||||||
id: `local:${shell.id}`,
|
id: `local:${shell.id}`,
|
||||||
type: 'local',
|
type: 'local',
|
||||||
@@ -45,20 +45,20 @@ export class LocalProfilesService extends ProfileProvider {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNewTabParameters (profile: Profile): Promise<NewTabParameters<TerminalTabComponent>> {
|
async getNewTabParameters (profile: PartialProfile<LocalProfile>): Promise<NewTabParameters<TerminalTabComponent>> {
|
||||||
profile = deepClone(profile)
|
profile = deepClone(profile)
|
||||||
|
|
||||||
if (!profile.options?.cwd) {
|
if (!profile.options?.cwd) {
|
||||||
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
if (this.app.activeTab instanceof TerminalTabComponent && this.app.activeTab.session) {
|
||||||
profile.options ??= {}
|
profile.options ??= {}
|
||||||
profile.options.cwd = await this.app.activeTab.session.getWorkingDirectory()
|
profile.options.cwd = await this.app.activeTab.session.getWorkingDirectory() ?? undefined
|
||||||
}
|
}
|
||||||
if (this.app.activeTab instanceof SplitTabComponent) {
|
if (this.app.activeTab instanceof SplitTabComponent) {
|
||||||
const focusedTab = this.app.activeTab.getFocusedTab()
|
const focusedTab = this.app.activeTab.getFocusedTab()
|
||||||
|
|
||||||
if (focusedTab instanceof TerminalTabComponent && focusedTab.session) {
|
if (focusedTab instanceof TerminalTabComponent && focusedTab.session) {
|
||||||
profile.options ??= {}
|
profile.options ??= {}
|
||||||
profile.options.cwd = await focusedTab.session.getWorkingDirectory()
|
profile.options!.cwd = await focusedTab.session.getWorkingDirectory() ?? undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ export class LocalProfilesService extends ProfileProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription (profile: Profile): string {
|
getDescription (profile: PartialProfile<LocalProfile>): string {
|
||||||
return profile.options?.command
|
return profile.options?.command ?? ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import * as fs from 'mz/fs'
|
import * as fs from 'mz/fs'
|
||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Logger, LogService, ConfigService, ProfilesService } from 'tabby-core'
|
import { Logger, LogService, ConfigService, ProfilesService, PartialProfile } from 'tabby-core'
|
||||||
import { TerminalTabComponent } from '../components/terminalTab.component'
|
import { TerminalTabComponent } from '../components/terminalTab.component'
|
||||||
import { LocalProfile } from '../api'
|
import { LocalProfile } from '../api'
|
||||||
|
|
||||||
@@ -17,40 +17,42 @@ export class TerminalService {
|
|||||||
this.logger = log.create('terminal')
|
this.logger = log.create('terminal')
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDefaultProfile (): Promise<LocalProfile> {
|
async getDefaultProfile (): Promise<PartialProfile<LocalProfile>> {
|
||||||
const profiles = await this.profilesService.getProfiles()
|
const profiles = await this.profilesService.getProfiles()
|
||||||
let profile = profiles.find(x => x.id === this.config.store.terminal.profile)
|
let profile = profiles.find(x => x.id === this.config.store.terminal.profile)
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
profile = profiles.filter(x => x.type === 'local' && x.isBuiltin)[0]
|
profile = profiles.filter(x => x.type === 'local' && x.isBuiltin)[0]
|
||||||
}
|
}
|
||||||
return profile as LocalProfile
|
return profile as PartialProfile<LocalProfile>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launches a new terminal with a specific shell and CWD
|
* Launches a new terminal with a specific shell and CWD
|
||||||
* @param pause Wait for a keypress when the shell exits
|
* @param pause Wait for a keypress when the shell exits
|
||||||
*/
|
*/
|
||||||
async openTab (profile?: LocalProfile|null, cwd?: string|null, pause?: boolean): Promise<TerminalTabComponent> {
|
async openTab (profile?: PartialProfile<LocalProfile>|null, cwd?: string|null, pause?: boolean): Promise<TerminalTabComponent> {
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
profile = await this.getDefaultProfile()
|
profile = await this.getDefaultProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
cwd = cwd ?? profile.options.cwd
|
const fullProfile = this.profilesService.getConfigProxyForProfile(profile)
|
||||||
|
|
||||||
|
cwd = cwd ?? fullProfile.options.cwd
|
||||||
|
|
||||||
if (cwd && !fs.existsSync(cwd)) {
|
if (cwd && !fs.existsSync(cwd)) {
|
||||||
console.warn('Ignoring non-existent CWD:', cwd)
|
console.warn('Ignoring non-existent CWD:', cwd)
|
||||||
cwd = null
|
cwd = null
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`Starting profile ${profile.name}`, profile)
|
this.logger.info(`Starting profile ${fullProfile.name}`, fullProfile)
|
||||||
const options = {
|
const options = {
|
||||||
...profile.options,
|
...fullProfile.options,
|
||||||
pauseAfterExit: pause,
|
pauseAfterExit: pause,
|
||||||
cwd: cwd ?? undefined,
|
cwd: cwd ?? undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await this.profilesService.openNewTabForProfile({
|
return (await this.profilesService.openNewTabForProfile({
|
||||||
...profile,
|
...fullProfile,
|
||||||
options,
|
options,
|
||||||
})) as TerminalTabComponent
|
})) as TerminalTabComponent
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import { SerialService } from '../services/serial.service'
|
|||||||
@Component({
|
@Component({
|
||||||
template: require('./serialProfileSettings.component.pug'),
|
template: require('./serialProfileSettings.component.pug'),
|
||||||
})
|
})
|
||||||
export class SerialProfileSettingsComponent implements ProfileSettingsComponent {
|
export class SerialProfileSettingsComponent implements ProfileSettingsComponent<SerialProfile> {
|
||||||
profile: SerialProfile
|
profile: SerialProfile
|
||||||
foundPorts: SerialPortInfo[]
|
foundPorts: SerialPortInfo[]
|
||||||
Platform = Platform
|
Platform = Platform
|
||||||
|
@@ -10,7 +10,7 @@ import { SerialService } from './services/serial.service'
|
|||||||
import { BAUD_RATES, SerialProfile } from './api'
|
import { BAUD_RATES, SerialProfile } from './api'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SerialProfilesService extends ProfileProvider {
|
export class SerialProfilesService extends ProfileProvider<SerialProfile> {
|
||||||
id = 'serial'
|
id = 'serial'
|
||||||
name = 'Serial'
|
name = 'Serial'
|
||||||
settingsComponent = SerialProfileSettingsComponent
|
settingsComponent = SerialProfileSettingsComponent
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import SerialPort from 'serialport'
|
import SerialPort from 'serialport'
|
||||||
import { ProfilesService } from 'tabby-core'
|
import { PartialProfile, ProfilesService } from 'tabby-core'
|
||||||
import { SerialPortInfo, SerialProfile } from '../api'
|
import { SerialPortInfo, SerialProfile } from '../api'
|
||||||
import { SerialTabComponent } from '../components/serialTab.component'
|
import { SerialTabComponent } from '../components/serialTab.component'
|
||||||
|
|
||||||
@@ -24,19 +24,12 @@ export class SerialService {
|
|||||||
baudrate = parseInt(path.split('@')[1])
|
baudrate = parseInt(path.split('@')[1])
|
||||||
path = path.split('@')[0]
|
path = path.split('@')[0]
|
||||||
}
|
}
|
||||||
const profile: SerialProfile = {
|
const profile: PartialProfile<SerialProfile> = {
|
||||||
name: query,
|
name: query,
|
||||||
type: 'serial',
|
type: 'serial',
|
||||||
options: {
|
options: {
|
||||||
port: path,
|
port: path,
|
||||||
baudrate: baudrate,
|
baudrate: baudrate,
|
||||||
databits: 8,
|
|
||||||
parity: 'none',
|
|
||||||
rtscts: false,
|
|
||||||
stopbits: 1,
|
|
||||||
xany: false,
|
|
||||||
xoff: false,
|
|
||||||
xon: false,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
window.localStorage.lastSerialConnection = JSON.stringify(profile)
|
window.localStorage.lastSerialConnection = JSON.stringify(profile)
|
||||||
|
@@ -15,15 +15,15 @@ const iconsClassList = Object.keys(iconsData).map(
|
|||||||
@Component({
|
@Component({
|
||||||
template: require('./editProfileModal.component.pug'),
|
template: require('./editProfileModal.component.pug'),
|
||||||
})
|
})
|
||||||
export class EditProfileModalComponent {
|
export class EditProfileModalComponent<P extends Profile> {
|
||||||
@Input() profile: Profile & ConfigProxy
|
@Input() profile: P & ConfigProxy
|
||||||
@Input() profileProvider: ProfileProvider
|
@Input() profileProvider: ProfileProvider<P>
|
||||||
@Input() settingsComponent: new () => ProfileSettingsComponent
|
@Input() settingsComponent: new () => ProfileSettingsComponent<P>
|
||||||
groupNames: string[]
|
groupNames: string[]
|
||||||
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
|
@ViewChild('placeholder', { read: ViewContainerRef }) placeholder: ViewContainerRef
|
||||||
|
|
||||||
private _profile: Profile
|
private _profile: Profile
|
||||||
private settingsComponentInstance: ProfileSettingsComponent
|
private settingsComponentInstance: ProfileSettingsComponent<P>
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
|
@@ -3,12 +3,12 @@ import slugify from 'slugify'
|
|||||||
import deepClone from 'clone-deep'
|
import deepClone from 'clone-deep'
|
||||||
import { Component } from '@angular/core'
|
import { Component } from '@angular/core'
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||||
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent } from 'tabby-core'
|
import { ConfigService, HostAppService, Profile, SelectorService, ProfilesService, PromptModalComponent, PlatformService, BaseComponent, PartialProfile } from 'tabby-core'
|
||||||
import { EditProfileModalComponent } from './editProfileModal.component'
|
import { EditProfileModalComponent } from './editProfileModal.component'
|
||||||
|
|
||||||
interface ProfileGroup {
|
interface ProfileGroup {
|
||||||
name?: string
|
name?: string
|
||||||
profiles: Profile[]
|
profiles: PartialProfile<Profile>[]
|
||||||
editable: boolean
|
editable: boolean
|
||||||
collapsed: boolean
|
collapsed: boolean
|
||||||
}
|
}
|
||||||
@@ -19,9 +19,9 @@ interface ProfileGroup {
|
|||||||
styles: [require('./profilesSettingsTab.component.scss')],
|
styles: [require('./profilesSettingsTab.component.scss')],
|
||||||
})
|
})
|
||||||
export class ProfilesSettingsTabComponent extends BaseComponent {
|
export class ProfilesSettingsTabComponent extends BaseComponent {
|
||||||
profiles: Profile[] = []
|
profiles: PartialProfile<Profile>[] = []
|
||||||
builtinProfiles: Profile[] = []
|
builtinProfiles: PartialProfile<Profile>[] = []
|
||||||
templateProfiles: Profile[] = []
|
templateProfiles: PartialProfile<Profile>[] = []
|
||||||
profileGroups: ProfileGroup[]
|
profileGroups: ProfileGroup[]
|
||||||
filter = ''
|
filter = ''
|
||||||
|
|
||||||
@@ -45,11 +45,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
this.subscribeUntilDestroyed(this.config.changed$, () => this.refresh())
|
this.subscribeUntilDestroyed(this.config.changed$, () => this.refresh())
|
||||||
}
|
}
|
||||||
|
|
||||||
launchProfile (profile: Profile): void {
|
launchProfile (profile: PartialProfile<Profile>): void {
|
||||||
this.profilesService.openNewTabForProfile(profile)
|
this.profilesService.openNewTabForProfile(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
async newProfile (base?: Profile): Promise<void> {
|
async newProfile (base?: PartialProfile<Profile>): Promise<void> {
|
||||||
if (!base) {
|
if (!base) {
|
||||||
const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
|
const profiles = [...this.templateProfiles, ...this.builtinProfiles, ...this.profiles]
|
||||||
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
|
profiles.sort((a, b) => (a.weight ?? 0) - (b.weight ?? 0))
|
||||||
@@ -57,7 +57,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
'Select a base profile to use as a template',
|
'Select a base profile to use as a template',
|
||||||
profiles.map(p => ({
|
profiles.map(p => ({
|
||||||
icon: p.icon,
|
icon: p.icon,
|
||||||
description: this.profilesService.providerForProfile(p)?.getDescription(p),
|
description: this.profilesService.getDescription(p) ?? undefined,
|
||||||
name: p.group ? `${p.group} / ${p.name}` : p.name,
|
name: p.group ? `${p.group} / ${p.name}` : p.name,
|
||||||
result: p,
|
result: p,
|
||||||
})),
|
})),
|
||||||
@@ -74,7 +74,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
await this.config.save()
|
await this.config.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
async editProfile (profile: Profile): Promise<void> {
|
async editProfile (profile: PartialProfile<Profile>): Promise<void> {
|
||||||
const modal = this.ngbModal.open(
|
const modal = this.ngbModal.open(
|
||||||
EditProfileModalComponent,
|
EditProfileModalComponent,
|
||||||
{ size: 'lg' },
|
{ size: 'lg' },
|
||||||
@@ -93,7 +93,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
await this.config.save()
|
await this.config.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteProfile (profile: Profile): Promise<void> {
|
async deleteProfile (profile: PartialProfile<Profile>): Promise<void> {
|
||||||
if ((await this.platform.showMessageBox(
|
if ((await this.platform.showMessageBox(
|
||||||
{
|
{
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
@@ -102,7 +102,8 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
defaultId: 0,
|
defaultId: 0,
|
||||||
}
|
}
|
||||||
)).response === 1) {
|
)).response === 1) {
|
||||||
this.profilesService.providerForProfile(profile)?.deleteProfile(profile)
|
this.profilesService.providerForProfile(profile)?.deleteProfile(
|
||||||
|
this.profilesService.getConfigProxyForProfile(profile))
|
||||||
this.config.store.profiles = this.config.store.profiles.filter(x => x !== profile)
|
this.config.store.profiles = this.config.store.profiles.filter(x => x !== profile)
|
||||||
await this.config.save()
|
await this.config.save()
|
||||||
}
|
}
|
||||||
@@ -181,7 +182,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
return !this.filter || group.profiles.some(x => this.isProfileVisible(x))
|
return !this.filter || group.profiles.some(x => this.isProfileVisible(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
isProfileVisible (profile: Profile): boolean {
|
isProfileVisible (profile: PartialProfile<Profile>): boolean {
|
||||||
return !this.filter || profile.name.toLowerCase().includes(this.filter.toLowerCase())
|
return !this.filter || profile.name.toLowerCase().includes(this.filter.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,11 +190,11 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
return icon?.startsWith('<') ?? false
|
return icon?.startsWith('<') ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription (profile: Profile): string|null {
|
getDescription (profile: PartialProfile<Profile>): string|null {
|
||||||
return this.profilesService.getDescription(profile)
|
return this.profilesService.getDescription(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeLabel (profile: Profile): string {
|
getTypeLabel (profile: PartialProfile<Profile>): string {
|
||||||
const name = this.profilesService.providerForProfile(profile)?.name
|
const name = this.profilesService.providerForProfile(profile)?.name
|
||||||
if (name === 'Local') {
|
if (name === 'Local') {
|
||||||
return ''
|
return ''
|
||||||
@@ -201,7 +202,7 @@ export class ProfilesSettingsTabComponent extends BaseComponent {
|
|||||||
return name ?? 'Unknown'
|
return name ?? 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeColorClass (profile: Profile): string {
|
getTypeColorClass (profile: PartialProfile<Profile>): string {
|
||||||
return {
|
return {
|
||||||
ssh: 'secondary',
|
ssh: 'secondary',
|
||||||
serial: 'success',
|
serial: 'success',
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ProfileProvider, Profile, NewTabParameters } from 'tabby-core'
|
import { ProfileProvider, NewTabParameters, PartialProfile } from 'tabby-core'
|
||||||
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
|
import { SSHProfileSettingsComponent } from './components/sshProfileSettings.component'
|
||||||
import { SSHTabComponent } from './components/sshTab.component'
|
import { SSHTabComponent } from './components/sshTab.component'
|
||||||
import { PasswordStorageService } from './services/passwordStorage.service'
|
import { PasswordStorageService } from './services/passwordStorage.service'
|
||||||
@@ -8,7 +8,7 @@ import { ALGORITHM_BLACKLIST, SSHAlgorithmType, SSHProfile } from './api'
|
|||||||
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
|
import * as ALGORITHMS from 'ssh2/lib/protocol/constants'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class SSHProfilesService extends ProfileProvider {
|
export class SSHProfilesService extends ProfileProvider<SSHProfile> {
|
||||||
id = 'ssh'
|
id = 'ssh'
|
||||||
name = 'SSH'
|
name = 'SSH'
|
||||||
supportsQuickConnect = true
|
supportsQuickConnect = true
|
||||||
@@ -66,7 +66,7 @@ export class SSHProfilesService extends ProfileProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<Profile[]> {
|
async getBuiltinProfiles (): Promise<PartialProfile<SSHProfile>[]> {
|
||||||
return [{
|
return [{
|
||||||
id: `ssh:template`,
|
id: `ssh:template`,
|
||||||
type: 'ssh',
|
type: 'ssh',
|
||||||
@@ -83,22 +83,22 @@ export class SSHProfilesService extends ProfileProvider {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNewTabParameters (profile: Profile): Promise<NewTabParameters<SSHTabComponent>> {
|
async getNewTabParameters (profile: PartialProfile<SSHProfile>): Promise<NewTabParameters<SSHTabComponent>> {
|
||||||
return {
|
return {
|
||||||
type: SSHTabComponent,
|
type: SSHTabComponent,
|
||||||
inputs: { profile },
|
inputs: { profile },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getDescription (profile: SSHProfile): string {
|
getDescription (profile: PartialProfile<SSHProfile>): string {
|
||||||
return profile.options.host
|
return profile.options?.host ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteProfile (profile: SSHProfile): void {
|
deleteProfile (profile: SSHProfile): void {
|
||||||
this.passwordStorage.deletePassword(profile)
|
this.passwordStorage.deletePassword(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
quickConnect (query: string): SSHProfile {
|
quickConnect (query: string): PartialProfile<SSHProfile> {
|
||||||
let user = 'root'
|
let user = 'root'
|
||||||
let host = query
|
let host = query
|
||||||
let port = 22
|
let port = 22
|
||||||
|
@@ -8,6 +8,6 @@ import { TelnetProfile } from '../session'
|
|||||||
@Component({
|
@Component({
|
||||||
template: require('./telnetProfileSettings.component.pug'),
|
template: require('./telnetProfileSettings.component.pug'),
|
||||||
})
|
})
|
||||||
export class TelnetProfileSettingsComponent implements ProfileSettingsComponent {
|
export class TelnetProfileSettingsComponent implements ProfileSettingsComponent<TelnetProfile> {
|
||||||
profile: TelnetProfile
|
profile: TelnetProfile
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { ProfileProvider, Profile, NewTabParameters } from 'tabby-core'
|
import { ProfileProvider, NewTabParameters, PartialProfile } from 'tabby-core'
|
||||||
import { TelnetProfileSettingsComponent } from './components/telnetProfileSettings.component'
|
import { TelnetProfileSettingsComponent } from './components/telnetProfileSettings.component'
|
||||||
import { TelnetTabComponent } from './components/telnetTab.component'
|
import { TelnetTabComponent } from './components/telnetTab.component'
|
||||||
import { TelnetProfile } from './session'
|
import { TelnetProfile } from './session'
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class TelnetProfilesService extends ProfileProvider {
|
export class TelnetProfilesService extends ProfileProvider<TelnetProfile> {
|
||||||
id = 'telnet'
|
id = 'telnet'
|
||||||
name = 'Telnet'
|
name = 'Telnet'
|
||||||
supportsQuickConnect = false
|
supportsQuickConnect = false
|
||||||
@@ -22,7 +22,7 @@ export class TelnetProfilesService extends ProfileProvider {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBuiltinProfiles (): Promise<TelnetProfile[]> {
|
async getBuiltinProfiles (): Promise<PartialProfile<TelnetProfile>[]> {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: `telnet:template`,
|
id: `telnet:template`,
|
||||||
@@ -55,7 +55,7 @@ export class TelnetProfilesService extends ProfileProvider {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNewTabParameters (profile: Profile): Promise<NewTabParameters<TelnetTabComponent>> {
|
async getNewTabParameters (profile: PartialProfile<TelnetProfile>): Promise<NewTabParameters<TelnetTabComponent>> {
|
||||||
return {
|
return {
|
||||||
type: TelnetTabComponent,
|
type: TelnetTabComponent,
|
||||||
inputs: { profile },
|
inputs: { profile },
|
||||||
@@ -66,7 +66,7 @@ export class TelnetProfilesService extends ProfileProvider {
|
|||||||
return profile.options.host ? `${profile.options.host}:${profile.options.port}` : ''
|
return profile.options.host ? `${profile.options.host}:${profile.options.port}` : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
quickConnect (query: string): TelnetProfile {
|
quickConnect (query: string): PartialProfile<TelnetProfile> {
|
||||||
let host = query
|
let host = query
|
||||||
let port = 23
|
let port = 23
|
||||||
if (host.includes('[')) {
|
if (host.includes('[')) {
|
||||||
|
Reference in New Issue
Block a user