don't include tab state in saved layouts

This commit is contained in:
Eugene Pankov
2022-01-10 20:39:29 +01:00
parent 3a11b51729
commit 61c11abda2
15 changed files with 38 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
export { BaseComponent, SubscriptionContainer } from '../components/base.component' export { BaseComponent, SubscriptionContainer } from '../components/base.component'
export { BaseTabComponent, BaseTabProcess } from '../components/baseTab.component' export { BaseTabComponent, BaseTabProcess, GetRecoveryTokenOptions } from '../components/baseTab.component'
export { TabHeaderComponent } from '../components/tabHeader.component' export { TabHeaderComponent } from '../components/tabHeader.component'
export { SplitTabComponent, SplitContainer, SplitDirection, SplitOrientation } from '../components/splitTab.component' export { SplitTabComponent, SplitContainer, SplitDirection, SplitOrientation } from '../components/splitTab.component'
export { TabRecoveryProvider, RecoveryToken } from './tabRecovery' export { TabRecoveryProvider, RecoveryToken } from './tabRecovery'

View File

@@ -1,4 +1,3 @@
import deepClone from 'clone-deep'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { NewTabParameters } from '../services/tabs.service' import { NewTabParameters } from '../services/tabs.service'
@@ -38,14 +37,4 @@ export abstract class TabRecoveryProvider <T extends BaseTabComponent> {
* or `null` if this token is from a different tab type or is not supported * or `null` if this token is from a different tab type or is not supported
*/ */
abstract recover (recoveryToken: RecoveryToken): Promise<NewTabParameters<T>> abstract recover (recoveryToken: RecoveryToken): Promise<NewTabParameters<T>>
/**
* @param recoveryToken a recovery token found in the saved tabs list
* @returns [[RecoveryToken]] a new recovery token to create the duplicate tab from
*
* The default implementation just returns a deep copy of the original token
*/
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return deepClone(recoveryToken)
}
} }

View File

@@ -11,6 +11,10 @@ export interface BaseTabProcess {
name: string name: string
} }
export interface GetRecoveryTokenOptions {
includeState: boolean
}
/** /**
* Abstract base class for custom tab components * Abstract base class for custom tab components
*/ */
@@ -136,7 +140,7 @@ export abstract class BaseTabComponent extends BaseComponent {
* @return JSON serializable tab state representation * @return JSON serializable tab state representation
* for your [[TabRecoveryProvider]] to parse * for your [[TabRecoveryProvider]] to parse
*/ */
async getRecoveryToken (): Promise<RecoveryToken|null> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> { // eslint-disable-line @typescript-eslint/no-unused-vars
return null return null
} }

View File

@@ -1,6 +1,6 @@
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 } from '@angular/core'
import { BaseTabComponent, BaseTabProcess } 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'
@@ -93,13 +93,13 @@ export class SplitContainer {
return s return s
} }
async serialize (tabsRecovery: TabRecoveryService): Promise<RecoveryToken> { async serialize (tabsRecovery: TabRecoveryService, options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
const children: any[] = [] const children: any[] = []
for (const child of this.children) { for (const child of this.children) {
if (child instanceof SplitContainer) { if (child instanceof SplitContainer) {
children.push(await child.serialize(tabsRecovery)) children.push(await child.serialize(tabsRecovery, options))
} else { } else {
children.push(await tabsRecovery.getFullRecoveryToken(child)) children.push(await tabsRecovery.getFullRecoveryToken(child, options))
} }
} }
return { return {
@@ -308,7 +308,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
/** @hidden */ /** @hidden */
async ngAfterViewInit (): Promise<void> { async ngAfterViewInit (): Promise<void> {
if (this._recoveredState) { if (this._recoveredState) {
await this.recoverContainer(this.root, this._recoveredState, this._recoveredState.duplicate) await this.recoverContainer(this.root, this._recoveredState)
this.updateTitle() this.updateTitle()
this.layout() this.layout()
setTimeout(() => { setTimeout(() => {
@@ -574,8 +574,8 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
/** @hidden */ /** @hidden */
async getRecoveryToken (): Promise<any> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return this.root.serialize(this.tabRecovery) return this.root.serialize(this.tabRecovery, options)
} }
/** @hidden */ /** @hidden */
@@ -795,7 +795,7 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
}) })
} }
private async recoverContainer (root: SplitContainer, state: any, duplicate = false) { private async recoverContainer (root: SplitContainer, state: any) {
const children: (SplitContainer | BaseTabComponent)[] = [] const children: (SplitContainer | BaseTabComponent)[] = []
root.orientation = state.orientation root.orientation = state.orientation
root.ratios = state.ratios root.ratios = state.ratios
@@ -806,10 +806,10 @@ export class SplitTabComponent extends BaseTabComponent implements AfterViewInit
} }
if (childState.type === 'app:split-tab') { if (childState.type === 'app:split-tab') {
const child = new SplitContainer() const child = new SplitContainer()
await this.recoverContainer(child, childState, duplicate) await this.recoverContainer(child, childState)
children.push(child) children.push(child)
} else { } else {
const recovered = await this.tabRecovery.recoverTab(childState, duplicate) const recovered = await this.tabRecovery.recoverTab(childState)
if (recovered) { if (recovered) {
const tab = this.tabsService.create(recovered) const tab = this.tabsService.create(recovered)
children.push(tab) children.push(tab)
@@ -840,11 +840,4 @@ export class SplitTabRecoveryProvider extends TabRecoveryProvider<SplitTabCompon
inputs: { _recoveredState: recoveryToken }, inputs: { _recoveredState: recoveryToken },
} }
} }
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
duplicate: true,
}
}
} }

View File

@@ -44,7 +44,7 @@ export class SplitLayoutProfilesService extends ProfileProvider<SplitLayoutProfi
} }
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({ includeState: false })
const profile: PartialProfile<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,

View File

@@ -318,7 +318,7 @@ export class AppService {
if (checkCanClose && !await tab.canClose()) { if (checkCanClose && !await tab.canClose()) {
return return
} }
const token = await this.tabRecovery.getFullRecoveryToken(tab) const token = await this.tabRecovery.getFullRecoveryToken(tab, { includeState: true })
if (token) { if (token) {
this.closedTabsStack.push(token) this.closedTabsStack.push(token)
} }

View File

@@ -1,6 +1,6 @@
import { Injectable, Inject } from '@angular/core' import { Injectable, Inject } from '@angular/core'
import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery' import { TabRecoveryProvider, RecoveryToken } from '../api/tabRecovery'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent, GetRecoveryTokenOptions } from '../components/baseTab.component'
import { Logger, LogService } from './log.service' import { Logger, LogService } from './log.service'
import { ConfigService } from './config.service' import { ConfigService } from './config.service'
import { NewTabParameters } from './tabs.service' import { NewTabParameters } from './tabs.service'
@@ -25,13 +25,13 @@ export class TabRecoveryService {
} }
window.localStorage.tabsRecovery = JSON.stringify( window.localStorage.tabsRecovery = JSON.stringify(
(await Promise.all( (await Promise.all(
tabs.map(async tab => this.getFullRecoveryToken(tab)) tabs.map(async tab => this.getFullRecoveryToken(tab, { includeState: true }))
)).filter(token => !!token) )).filter(token => !!token)
) )
} }
async getFullRecoveryToken (tab: BaseTabComponent): Promise<RecoveryToken|null> { async getFullRecoveryToken (tab: BaseTabComponent, options?: GetRecoveryTokenOptions): Promise<RecoveryToken|null> {
const token = await tab.getRecoveryToken() const token = await tab.getRecoveryToken(options)
if (token) { if (token) {
token.tabTitle = tab.title token.tabTitle = tab.title
token.tabCustomTitle = tab.customTitle token.tabCustomTitle = tab.customTitle
@@ -43,15 +43,12 @@ export class TabRecoveryService {
return token return token
} }
async recoverTab (token: RecoveryToken, duplicate = false): Promise<NewTabParameters<BaseTabComponent>|null> { async recoverTab (token: RecoveryToken): Promise<NewTabParameters<BaseTabComponent>|null> {
for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) { for (const provider of this.config.enabledServices(this.tabRecoveryProviders ?? [])) {
try { try {
if (!await provider.applicableTo(token)) { if (!await provider.applicableTo(token)) {
continue continue
} }
if (duplicate) {
token = provider.duplicate(token)
}
const tab = await provider.recover(token) const tab = await provider.recover(token)
tab.inputs = tab.inputs ?? {} tab.inputs = tab.inputs ?? {}
tab.inputs.color = token.tabColor ?? null tab.inputs.color = token.tabColor ?? null

View File

@@ -1,3 +1,4 @@
import deepClone from 'clone-deep'
import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core' import { Injectable, ComponentFactoryResolver, Injector } from '@angular/core'
import { BaseTabComponent } from '../components/baseTab.component' import { BaseTabComponent } from '../components/baseTab.component'
import { TabRecoveryService } from './tabRecovery.service' import { TabRecoveryService } from './tabRecovery.service'
@@ -48,7 +49,7 @@ export class TabsService {
if (!token) { if (!token) {
return null return null
} }
const dup = await this.tabRecovery.recoverTab(token, true) const dup = await this.tabRecovery.recoverTab(deepClone(token))
if (dup) { if (dup) {
return this.create(dup) return this.create(dup)
} }

View File

@@ -1,5 +1,5 @@
import { Component, Input, Injector } from '@angular/core' import { Component, Input, Injector } from '@angular/core'
import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild } from 'tabby-core' import { BaseTabProcess, WIN_BUILD_CONPTY_SUPPORTED, isWindowsBuild, GetRecoveryTokenOptions } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { LocalProfile, SessionOptions } from '../api' import { LocalProfile, SessionOptions } from '../api'
import { Session } from '../session' import { Session } from '../session'
@@ -74,7 +74,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
this.recoveryStateChangedHint.next() this.recoveryStateChangedHint.next()
} }
async getRecoveryToken (): Promise<any> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
const cwd = this.session ? await this.session.getWorkingDirectory() : null const cwd = this.session ? await this.session.getWorkingDirectory() : null
return { return {
type: 'app:local-tab', type: 'app:local-tab',
@@ -83,10 +83,10 @@ export class TerminalTabComponent extends BaseTerminalTabComponent {
options: { options: {
...this.profile.options, ...this.profile.options,
cwd: cwd ?? this.profile.options.cwd, cwd: cwd ?? this.profile.options.cwd,
restoreFromPTYID: this.session?.getPTYID(), restoreFromPTYID: options?.includeState && this.session?.getPTYID(),
}, },
}, },
savedState: this.frontend?.saveState(), savedState: options?.includeState && this.frontend?.saveState(),
} }
} }

View File

@@ -19,18 +19,4 @@ export class RecoveryProvider extends TabRecoveryProvider<TerminalTabComponent>
}, },
} }
} }
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
profile: {
...recoveryToken.profile,
options: {
...recoveryToken.profile.options,
restoreFromPTYID: null,
},
},
savedState: null,
}
}
} }

View File

@@ -2,7 +2,7 @@
import colors from 'ansi-colors' import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core' import { Component, Injector } from '@angular/core'
import { first } from 'rxjs' import { first } from 'rxjs'
import { Platform, SelectorService } from 'tabby-core' import { GetRecoveryTokenOptions, Platform, SelectorService } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SerialSession, BAUD_RATES, SerialProfile } from '../api' import { SerialSession, BAUD_RATES, SerialProfile } from '../api'
@@ -98,11 +98,11 @@ export class SerialTabComponent extends BaseTerminalTabComponent {
super.attachSessionHandlers() super.attachSessionHandlers()
} }
async getRecoveryToken (): Promise<any> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<any> {
return { return {
type: 'app:serial-tab', type: 'app:serial-tab',
profile: this.profile, profile: this.profile,
savedState: this.frontend?.saveState(), savedState: options?.includeState && this.frontend?.saveState(),
} }
} }

View File

@@ -2,7 +2,7 @@ import colors from 'ansi-colors'
import { Component, Injector, HostListener } from '@angular/core' import { Component, Injector, HostListener } from '@angular/core'
import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
import { first } from 'rxjs' import { first } from 'rxjs'
import { Platform, ProfilesService, RecoveryToken } from 'tabby-core' import { GetRecoveryTokenOptions, Platform, ProfilesService, RecoveryToken } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { SSHService } from '../services/ssh.service' import { SSHService } from '../services/ssh.service'
import { KeyboardInteractivePrompt, SSHSession } from '../session/ssh' import { KeyboardInteractivePrompt, SSHSession } from '../session/ssh'
@@ -210,11 +210,11 @@ export class SSHTabComponent extends BaseTerminalTabComponent {
this.session?.resize(this.size.columns, this.size.rows) this.session?.resize(this.size.columns, this.size.rows)
} }
async getRecoveryToken (): Promise<RecoveryToken> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
return { return {
type: 'app:ssh-tab', type: 'app:ssh-tab',
profile: this.profile, profile: this.profile,
savedState: this.frontend?.saveState(), savedState: options?.includeState && this.frontend?.saveState(),
} }
} }

View File

@@ -19,11 +19,4 @@ export class RecoveryProvider extends TabRecoveryProvider<SSHTabComponent> {
}, },
} }
} }
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
savedState: null,
}
}
} }

View File

@@ -1,7 +1,7 @@
import colors from 'ansi-colors' import colors from 'ansi-colors'
import { Component, Injector } from '@angular/core' import { Component, Injector } from '@angular/core'
import { first } from 'rxjs' import { first } from 'rxjs'
import { Platform, RecoveryToken } from 'tabby-core' import { GetRecoveryTokenOptions, Platform, RecoveryToken } from 'tabby-core'
import { BaseTerminalTabComponent } from 'tabby-terminal' import { BaseTerminalTabComponent } from 'tabby-terminal'
import { TelnetProfile, TelnetSession } from '../session' import { TelnetProfile, TelnetSession } from '../session'
@@ -97,11 +97,11 @@ export class TelnetTabComponent extends BaseTerminalTabComponent {
} }
} }
async getRecoveryToken (): Promise<RecoveryToken> { async getRecoveryToken (options?: GetRecoveryTokenOptions): Promise<RecoveryToken> {
return { return {
type: 'app:telnet-tab', type: 'app:telnet-tab',
profile: this.profile, profile: this.profile,
savedState: this.frontend?.saveState(), savedState: options?.includeState && this.frontend?.saveState(),
} }
} }

View File

@@ -19,11 +19,4 @@ export class RecoveryProvider extends TabRecoveryProvider<TelnetTabComponent> {
}, },
} }
} }
duplicate (recoveryToken: RecoveryToken): RecoveryToken {
return {
...recoveryToken,
savedState: null,
}
}
} }