From 268af8806cefc8dba071ead3a03870acdcf39a8e Mon Sep 17 00:00:00 2001 From: Clem Date: Wed, 12 Apr 2023 23:20:06 +0200 Subject: [PATCH 1/7] resolves Eugeny/tabby#7751, Eugeny/tabby#8062, Eugeny/tabby#6617 add configurable behavior when session ends --- tabby-core/src/configDefaults.yaml | 1 + .../src/components/terminalTab.component.ts | 8 ++-- .../src/components/serialTab.component.ts | 21 +++++++++-- tabby-ssh/src/components/sshTab.component.ts | 37 ++++++++++--------- .../src/components/telnetTab.component.ts | 25 +++++++++---- .../terminalSettingsTab.component.pug | 18 +++++++++ tabby-terminal/src/config.ts | 1 + 7 files changed, 79 insertions(+), 32 deletions(-) diff --git a/tabby-core/src/configDefaults.yaml b/tabby-core/src/configDefaults.yaml index fd51a672..3b829ab1 100644 --- a/tabby-core/src/configDefaults.yaml +++ b/tabby-core/src/configDefaults.yaml @@ -25,6 +25,7 @@ terminal: paneResizeStep: 0.1 focusFollowsMouse: false identification: null + behaviorOnSessionEnds: 'keep-open' hotkeys: profile: __nonStructural: true diff --git a/tabby-local/src/components/terminalTab.component.ts b/tabby-local/src/components/terminalTab.component.ts index 325264d8..5fb09beb 100644 --- a/tabby-local/src/components/terminalTab.component.ts +++ b/tabby-local/src/components/terminalTab.component.ts @@ -28,7 +28,6 @@ export class TerminalTabComponent extends BaseTerminalTabComponent this.sessionOptions = this.profile.options this.logger = this.log.create('terminalTab') - this.session = new Session(this.injector) const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY @@ -56,6 +55,9 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } initializeSession (columns: number, rows: number): void { + + const session = new Session(this.injector) + if (this.profile.options.runAsAdministrator && this.uac?.isAvailable) { this.profile = { ...this.profile, @@ -63,13 +65,13 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } } - this.session!.start({ + session.start({ ...this.profile.options, width: columns, height: rows, }) - this.attachSessionHandlers(true) + this.setSession(session, this.config.store.terminal.behaviorOnSessionEnds.endsWith('close')) this.recoveryStateChangedHint.next() } diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index d648dac4..944df11b 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -82,12 +82,25 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.session?.resize(this.size.columns, this.size.rows) }) this.attachSessionHandler(this.session!.destroyed$, () => { - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open) { + if (this.frontend) { + // Session was closed abruptly + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + // Close the tab + this.destroy() + } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { + // Automatically reconnect the session this.reconnect() + } else { + // Reconnect Offer + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open) { + this.reconnect() + } + }) } - }) + } + }) super.attachSessionHandlers() } diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index 11af1368..14296ad5 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -157,24 +157,27 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem protected attachSessionHandlers (): void { const session = this.session! this.attachSessionHandler(session.destroyed$, () => { - if ( - // Ctrl-D - this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 || - this.recentInputs.endsWith('exit\r') - ) { - // User closed the session - this.destroy() - } else if (this.frontend) { - // Session was closed abruptly + if (this.frontend) { + this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`) - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) + + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + // Close the tab + this.destroy() + } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { + // Automatically reconnect the session + this.reconnect() + } else { + // Reconnect Offer + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) + } } } }) diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index 440e3432..58e4e7fe 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -48,14 +48,23 @@ export class TelnetTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + // Close the tab + this.destroy() + } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { + // Automatically reconnect the session + this.reconnect() + } else { + // Reconnect Offer + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) + } } } }) diff --git a/tabby-terminal/src/components/terminalSettingsTab.component.pug b/tabby-terminal/src/components/terminalSettingsTab.component.pug index 9005f12b..b0265c6d 100644 --- a/tabby-terminal/src/components/terminalSettingsTab.component.pug +++ b/tabby-terminal/src/components/terminalSettingsTab.component.pug @@ -225,6 +225,24 @@ div.mt-4 (ngModelChange)='config.save()', ) +.mt-4 + h3(translate) Closing + + .form-line + .header + .title(translate) Tab's behavior when session ends + .description(*ngIf='config.store.terminal.behaviorOnSessionEnds.startsWith("reconnect-or")', translate) Automatically reconnect the Serial, Telnet or SSH session + + select.form-control( + [(ngModel)]='config.store.terminal.behaviorOnSessionEnds', + (ngModelChange)='config.save()' + ) + option(ngValue='keep-open', translate) Keep open + option(ngValue='reconnect-or-keep-open', translate) Reconnect, otherwise keep open + option(ngValue='reconnect-or-close', translate) Reconnect, otherwise close + option(ngValue='close', translate) Close + + div.mt-4(*ngIf='hostApp.platform === Platform.Windows') h3(translate) Windows diff --git a/tabby-terminal/src/config.ts b/tabby-terminal/src/config.ts index 8c4276b0..35bd77ca 100644 --- a/tabby-terminal/src/config.ts +++ b/tabby-terminal/src/config.ts @@ -24,6 +24,7 @@ export class TerminalConfigProvider extends ConfigProvider { hideCloseButton: false, hideTabOptionsButton: false, rightClick: 'menu', + behaviorOnSessionEnds: 'keep-open', pasteOnMiddleClick: true, copyOnSelect: false, copyAsHTML: true, From b0973791a8a012dc4ea602265333e916d1e63e74 Mon Sep 17 00:00:00 2001 From: Clem Date: Wed, 12 Apr 2023 23:52:25 +0200 Subject: [PATCH 2/7] lint --- tabby-serial/src/components/serialTab.component.ts | 2 +- tabby-ssh/src/components/sshTab.component.ts | 3 +-- tabby-telnet/src/components/telnetTab.component.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index 944df11b..c7dc6f15 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -84,7 +84,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(this.session!.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index 14296ad5..4e681595 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -158,10 +158,9 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem const session = this.session! this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { - this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`) - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index 58e4e7fe..77b3ceb7 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -48,7 +48,7 @@ export class TelnetTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { From 8cb75e14a2c96ef0be93a553edabf473bdec4d76 Mon Sep 17 00:00:00 2001 From: Clem Date: Sat, 22 Apr 2023 14:36:14 +0200 Subject: [PATCH 3/7] Revert "lint" This reverts commit b0973791a8a012dc4ea602265333e916d1e63e74. --- tabby-serial/src/components/serialTab.component.ts | 2 +- tabby-ssh/src/components/sshTab.component.ts | 3 ++- tabby-telnet/src/components/telnetTab.component.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index c7dc6f15..944df11b 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -84,7 +84,7 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(this.session!.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index 4e681595..14296ad5 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -158,9 +158,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem const session = this.session! this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { + this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`) - if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index 77b3ceb7..58e4e7fe 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -48,7 +48,7 @@ export class TelnetTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds === 'close') { + if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { // Close the tab this.destroy() } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { From f423be15100329aa7a244bd8f124f1e236a805f9 Mon Sep 17 00:00:00 2001 From: Clem Date: Sat, 22 Apr 2023 14:36:43 +0200 Subject: [PATCH 4/7] Revert "resolves Eugeny/tabby#7751, Eugeny/tabby#8062, Eugeny/tabby#6617 add configurable behavior when session ends" This reverts commit 268af8806cefc8dba071ead3a03870acdcf39a8e. --- tabby-core/src/configDefaults.yaml | 1 - .../src/components/terminalTab.component.ts | 8 ++-- .../src/components/serialTab.component.ts | 21 ++--------- tabby-ssh/src/components/sshTab.component.ts | 37 +++++++++---------- .../src/components/telnetTab.component.ts | 25 ++++--------- .../terminalSettingsTab.component.pug | 18 --------- tabby-terminal/src/config.ts | 1 - 7 files changed, 32 insertions(+), 79 deletions(-) diff --git a/tabby-core/src/configDefaults.yaml b/tabby-core/src/configDefaults.yaml index 3b829ab1..fd51a672 100644 --- a/tabby-core/src/configDefaults.yaml +++ b/tabby-core/src/configDefaults.yaml @@ -25,7 +25,6 @@ terminal: paneResizeStep: 0.1 focusFollowsMouse: false identification: null - behaviorOnSessionEnds: 'keep-open' hotkeys: profile: __nonStructural: true diff --git a/tabby-local/src/components/terminalTab.component.ts b/tabby-local/src/components/terminalTab.component.ts index 5fb09beb..325264d8 100644 --- a/tabby-local/src/components/terminalTab.component.ts +++ b/tabby-local/src/components/terminalTab.component.ts @@ -28,6 +28,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent this.sessionOptions = this.profile.options this.logger = this.log.create('terminalTab') + this.session = new Session(this.injector) const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY @@ -55,9 +56,6 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } initializeSession (columns: number, rows: number): void { - - const session = new Session(this.injector) - if (this.profile.options.runAsAdministrator && this.uac?.isAvailable) { this.profile = { ...this.profile, @@ -65,13 +63,13 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } } - session.start({ + this.session!.start({ ...this.profile.options, width: columns, height: rows, }) - this.setSession(session, this.config.store.terminal.behaviorOnSessionEnds.endsWith('close')) + this.attachSessionHandlers(true) this.recoveryStateChangedHint.next() } diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index 944df11b..d648dac4 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -82,25 +82,12 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.session?.resize(this.size.columns, this.size.rows) }) this.attachSessionHandler(this.session!.destroyed$, () => { - if (this.frontend) { - // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { - // Close the tab - this.destroy() - } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { - // Automatically reconnect the session + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open) { this.reconnect() - } else { - // Reconnect Offer - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open) { - this.reconnect() - } - }) } - } - + }) }) super.attachSessionHandlers() } diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index 14296ad5..11af1368 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -157,27 +157,24 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem protected attachSessionHandlers (): void { const session = this.session! this.attachSessionHandler(session.destroyed$, () => { - if (this.frontend) { - + if ( + // Ctrl-D + this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 || + this.recentInputs.endsWith('exit\r') + ) { + // User closed the session + this.destroy() + } else if (this.frontend) { + // Session was closed abruptly this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`) - - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { - // Close the tab - this.destroy() - } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { - // Automatically reconnect the session - this.reconnect() - } else { - // Reconnect Offer - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) - } + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) } } }) diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index 58e4e7fe..440e3432 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -48,23 +48,14 @@ export class TelnetTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (this.config.store.terminal.behaviorOnSessionEnds == 'close') { - // Close the tab - this.destroy() - } else if (this.config.store.terminal.behaviorOnSessionEnds.startsWith('reconnect-or-')) { - // Automatically reconnect the session - this.reconnect() - } else { - // Reconnect Offer - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) - } + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) } } }) diff --git a/tabby-terminal/src/components/terminalSettingsTab.component.pug b/tabby-terminal/src/components/terminalSettingsTab.component.pug index b0265c6d..9005f12b 100644 --- a/tabby-terminal/src/components/terminalSettingsTab.component.pug +++ b/tabby-terminal/src/components/terminalSettingsTab.component.pug @@ -225,24 +225,6 @@ div.mt-4 (ngModelChange)='config.save()', ) -.mt-4 - h3(translate) Closing - - .form-line - .header - .title(translate) Tab's behavior when session ends - .description(*ngIf='config.store.terminal.behaviorOnSessionEnds.startsWith("reconnect-or")', translate) Automatically reconnect the Serial, Telnet or SSH session - - select.form-control( - [(ngModel)]='config.store.terminal.behaviorOnSessionEnds', - (ngModelChange)='config.save()' - ) - option(ngValue='keep-open', translate) Keep open - option(ngValue='reconnect-or-keep-open', translate) Reconnect, otherwise keep open - option(ngValue='reconnect-or-close', translate) Reconnect, otherwise close - option(ngValue='close', translate) Close - - div.mt-4(*ngIf='hostApp.platform === Platform.Windows') h3(translate) Windows diff --git a/tabby-terminal/src/config.ts b/tabby-terminal/src/config.ts index 35bd77ca..8c4276b0 100644 --- a/tabby-terminal/src/config.ts +++ b/tabby-terminal/src/config.ts @@ -24,7 +24,6 @@ export class TerminalConfigProvider extends ConfigProvider { hideCloseButton: false, hideTabOptionsButton: false, rightClick: 'menu', - behaviorOnSessionEnds: 'keep-open', pasteOnMiddleClick: true, copyOnSelect: false, copyAsHTML: true, From 6498c4f92332ec75be8cfe2ea38c0dfb069846d2 Mon Sep 17 00:00:00 2001 From: Clem Date: Sat, 22 Apr 2023 15:23:59 +0200 Subject: [PATCH 5/7] ref: implement recentInputs on baseTerminalTab --- tabby-serial/src/components/serialTab.component.ts | 9 +++++---- tabby-ssh/src/components/sshTab.component.ts | 14 +++++--------- tabby-telnet/src/components/telnetTab.component.ts | 9 +++++---- .../src/api/baseTerminalTab.component.ts | 6 ++++++ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index d648dac4..f9293be4 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -47,10 +47,6 @@ export class SerialTabComponent extends BaseTerminalTabComponent } }) - this.frontendReady$.pipe(first()).subscribe(() => { - this.initializeSession() - }) - super.ngOnInit() setImmediate(() => { @@ -58,6 +54,11 @@ export class SerialTabComponent extends BaseTerminalTabComponent }) } + protected onFrontendReady (): void { + this.initializeSession() + super.onFrontendReady() + } + async initializeSession () { const session = new SerialSession(this.injector, this.profile) this.setSession(session) diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index 11af1368..ea2df932 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -30,7 +30,6 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem sftpPath = '/' enableToolbar = true activeKIPrompt: KeyboardInteractivePrompt|null = null - private recentInputs = '' private reconnectOffered = false constructor ( @@ -71,17 +70,14 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem } }) - this.frontendReady$.pipe(first()).subscribe(() => { - this.initializeSession() - this.input$.subscribe(data => { - this.recentInputs += data - this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32) - }) - }) - super.ngOnInit() } + protected onFrontendReady (): void { + this.initializeSession() + super.onFrontendReady() + } + async setupOneSession (injector: Injector, profile: SSHProfile, multiplex = true): Promise { let session = await this.sshMultiplexer.getSession(profile) if (!multiplex || !session || !profile.options.reuseSession) { diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index 440e3432..ac9cf429 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -36,13 +36,14 @@ export class TelnetTabComponent extends BaseTerminalTabComponent } }) - this.frontendReady$.pipe(first()).subscribe(() => { - this.initializeSession() - }) - super.ngOnInit() } + protected onFrontendReady (): void { + this.initializeSession() + super.onFrontendReady() + } + protected attachSessionHandlers (): void { const session = this.session! this.attachSessionHandler(session.destroyed$, () => { diff --git a/tabby-terminal/src/api/baseTerminalTab.component.ts b/tabby-terminal/src/api/baseTerminalTab.component.ts index f781afd6..bfc406ad 100644 --- a/tabby-terminal/src/api/baseTerminalTab.component.ts +++ b/tabby-terminal/src/api/baseTerminalTab.component.ts @@ -129,6 +129,7 @@ export class BaseTerminalTabComponent

extends Bas protected output = new Subject() protected binaryOutput = new Subject() protected sessionChanged = new Subject() + protected recentInputs = '' private bellPlayer: HTMLAudioElement private termContainerSubscriptions = new SubscriptionContainer() private sessionHandlers = new SubscriptionContainer() @@ -415,6 +416,11 @@ export class BaseTerminalTabComponent

extends Bas this.frontend!.write('\r\n\r\n') } } + + this.input$.subscribe(data => { + this.recentInputs += data + this.recentInputs = this.recentInputs.substring(this.recentInputs.length - 32) + }) } async buildContextMenu (): Promise { From f2a62413daaf0797030d77b8fd93d6cd52d125bb Mon Sep 17 00:00:00 2001 From: Clem Date: Sat, 22 Apr 2023 15:51:29 +0200 Subject: [PATCH 6/7] ref(tabby-local): use setSession method in terminalTab component --- tabby-local/src/components/terminalTab.component.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tabby-local/src/components/terminalTab.component.ts b/tabby-local/src/components/terminalTab.component.ts index 325264d8..2f256502 100644 --- a/tabby-local/src/components/terminalTab.component.ts +++ b/tabby-local/src/components/terminalTab.component.ts @@ -28,7 +28,6 @@ export class TerminalTabComponent extends BaseTerminalTabComponent this.sessionOptions = this.profile.options this.logger = this.log.create('terminalTab') - this.session = new Session(this.injector) const isConPTY = isWindowsBuild(WIN_BUILD_CONPTY_SUPPORTED) && this.config.store.terminal.useConPTY @@ -56,6 +55,9 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } initializeSession (columns: number, rows: number): void { + + const session = new Session(this.injector) + if (this.profile.options.runAsAdministrator && this.uac?.isAvailable) { this.profile = { ...this.profile, @@ -63,13 +65,13 @@ export class TerminalTabComponent extends BaseTerminalTabComponent } } - this.session!.start({ + session.start({ ...this.profile.options, width: columns, height: rows, }) - this.attachSessionHandlers(true) + this.setSession(session, true) this.recoveryStateChangedHint.next() } From b1acfe251970b1c19921e93b1e60f85c21549a4f Mon Sep 17 00:00:00 2001 From: Clem Date: Sun, 23 Apr 2023 10:50:39 +0200 Subject: [PATCH 7/7] resolves Eugeny/tabby#7751, Eugeny/tabby#8062, Eugeny/tabby#6617 add behavior when session ends --- tabby-core/src/api/profileProvider.ts | 1 + tabby-core/src/services/profiles.service.ts | 1 + .../src/components/terminalTab.component.ts | 10 +++++- .../src/components/serialTab.component.ts | 23 +++++++++--- .../components/editProfileModal.component.pug | 12 +++++++ tabby-ssh/src/components/sshTab.component.ts | 36 ++++++++++--------- .../src/components/telnetTab.component.ts | 29 ++++++++++----- .../src/api/baseTerminalTab.component.ts | 16 ++++++--- 8 files changed, 95 insertions(+), 33 deletions(-) diff --git a/tabby-core/src/api/profileProvider.ts b/tabby-core/src/api/profileProvider.ts index 8fdbc2e6..a6e6bdd6 100644 --- a/tabby-core/src/api/profileProvider.ts +++ b/tabby-core/src/api/profileProvider.ts @@ -14,6 +14,7 @@ export interface Profile { icon?: string color?: string disableDynamicTitle: boolean + behaviorOnSessionEnd: 'auto'|'keep'|'reconnect'|'close' weight: number isBuiltin: boolean diff --git a/tabby-core/src/services/profiles.service.ts b/tabby-core/src/services/profiles.service.ts index 97d3488d..ef26e683 100644 --- a/tabby-core/src/services/profiles.service.ts +++ b/tabby-core/src/services/profiles.service.ts @@ -24,6 +24,7 @@ export class ProfilesService { isBuiltin: false, isTemplate: false, terminalColorScheme: null, + behaviorOnSessionEnd: 'auto', } constructor ( diff --git a/tabby-local/src/components/terminalTab.component.ts b/tabby-local/src/components/terminalTab.component.ts index 2f256502..b14d03b6 100644 --- a/tabby-local/src/components/terminalTab.component.ts +++ b/tabby-local/src/components/terminalTab.component.ts @@ -71,7 +71,7 @@ export class TerminalTabComponent extends BaseTerminalTabComponent height: rows, }) - this.setSession(session, true) + this.setSession(session) this.recoveryStateChangedHint.next() } @@ -127,4 +127,12 @@ export class TerminalTabComponent extends BaseTerminalTabComponent super.ngOnDestroy() this.session?.destroy() } + + /** + * Return true if the user explicitly exit the session. + * Always return true for terminalTab as the session can only be ended by the user + */ + protected isSessionExplicitlyTerminated (): boolean { + return true + } } diff --git a/tabby-serial/src/components/serialTab.component.ts b/tabby-serial/src/components/serialTab.component.ts index f9293be4..3e332f81 100644 --- a/tabby-serial/src/components/serialTab.component.ts +++ b/tabby-serial/src/components/serialTab.component.ts @@ -83,12 +83,21 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.session?.resize(this.size.columns, this.size.rows) }) this.attachSessionHandler(this.session!.destroyed$, () => { - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open) { + if (this.frontend) { + // Session was closed abruptly + this.write('\r\n' + colors.black.bgWhite(' SERIAL ') + ` session closed\r\n`) + + if (this.profile.behaviorOnSessionEnd === 'reconnect') { this.reconnect() + } else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) { + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open) { + this.reconnect() + } + }) } - }) + } }) super.attachSessionHandlers() } @@ -117,4 +126,10 @@ export class SerialTabComponent extends BaseTerminalTabComponent this.session?.serial?.update({ baudRate: rate }) this.profile.options.baudrate = rate } + + protected isSessionExplicitlyTerminated (): boolean { + return super.isSessionExplicitlyTerminated() || + this.recentInputs.endsWith('close\r') || + this.recentInputs.endsWith('quit\r') + } } diff --git a/tabby-settings/src/components/editProfileModal.component.pug b/tabby-settings/src/components/editProfileModal.component.pug index 2a70ce5a..7869099b 100644 --- a/tabby-settings/src/components/editProfileModal.component.pug +++ b/tabby-settings/src/components/editProfileModal.component.pug @@ -65,6 +65,18 @@ .description(translate) Connection name will be used instead toggle([(ngModel)]='profile.disableDynamicTitle') + .form-line + .header + .title(translate) When a session ends + .description(*ngIf='profile.behaviorOnSessionEnd == "auto"', translate) Only close the tab when session is explicitly terminated + select.form-control( + [(ngModel)]='profile.behaviorOnSessionEnd', + ) + option(ngValue='auto', translate) Auto + option(ngValue='keep', translate) Keep + option(*ngIf='profile.type == "serial" || profile.type == "telnet" || profile.type == "ssh"', ngValue='reconnect', translate) Reconnect + option(ngValue='close', translate) Close + .mb-4 .col-12.col-lg-8(*ngIf='this.profileProvider.settingsComponent') diff --git a/tabby-ssh/src/components/sshTab.component.ts b/tabby-ssh/src/components/sshTab.component.ts index ea2df932..97bc2da5 100644 --- a/tabby-ssh/src/components/sshTab.component.ts +++ b/tabby-ssh/src/components/sshTab.component.ts @@ -153,24 +153,22 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem protected attachSessionHandlers (): void { const session = this.session! this.attachSessionHandler(session.destroyed$, () => { - if ( - // Ctrl-D - this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 || - this.recentInputs.endsWith('exit\r') - ) { - // User closed the session - this.destroy() - } else if (this.frontend) { + if (this.frontend) { // Session was closed abruptly this.write('\r\n' + colors.black.bgWhite(' SSH ') + ` ${this.sshSession?.profile.options.host}: session closed\r\n`) - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) + + if (this.profile.behaviorOnSessionEnd === 'reconnect') { + this.reconnect() + } else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) { + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) + } } } }) @@ -262,4 +260,10 @@ export class SSHTabComponent extends BaseTerminalTabComponent implem onClick (): void { this.sftpPanelVisible = false } + + protected isSessionExplicitlyTerminated (): boolean { + return super.isSessionExplicitlyTerminated() || + this.recentInputs.charCodeAt(this.recentInputs.length - 1) === 4 || + this.recentInputs.endsWith('exit\r') + } } diff --git a/tabby-telnet/src/components/telnetTab.component.ts b/tabby-telnet/src/components/telnetTab.component.ts index ac9cf429..d3ae2cf4 100644 --- a/tabby-telnet/src/components/telnetTab.component.ts +++ b/tabby-telnet/src/components/telnetTab.component.ts @@ -49,14 +49,20 @@ export class TelnetTabComponent extends BaseTerminalTabComponent this.attachSessionHandler(session.destroyed$, () => { if (this.frontend) { // Session was closed abruptly - if (!this.reconnectOffered) { - this.reconnectOffered = true - this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') - this.input$.pipe(first()).subscribe(() => { - if (!this.session?.open && this.reconnectOffered) { - this.reconnect() - } - }) + this.write('\r\n' + colors.black.bgWhite(' TELNET ') + ` ${this.session?.profile.options.host}: session closed\r\n`) + + if (this.profile.behaviorOnSessionEnd === 'reconnect') { + this.reconnect() + } else if (this.profile.behaviorOnSessionEnd === 'keep' || this.profile.behaviorOnSessionEnd === 'auto' && !this.isSessionExplicitlyTerminated()) { + if (!this.reconnectOffered) { + this.reconnectOffered = true + this.write(this.translate.instant(_('Press any key to reconnect')) + '\r\n') + this.input$.pipe(first()).subscribe(() => { + if (!this.session?.open && this.reconnectOffered) { + this.reconnect() + } + }) + } } } }) @@ -121,4 +127,11 @@ export class TelnetTabComponent extends BaseTerminalTabComponent }, )).response === 0 } + + protected isSessionExplicitlyTerminated (): boolean { + return super.isSessionExplicitlyTerminated() || + this.recentInputs.endsWith('close\r') || + this.recentInputs.endsWith('quit\r') + } + } diff --git a/tabby-terminal/src/api/baseTerminalTab.component.ts b/tabby-terminal/src/api/baseTerminalTab.component.ts index bfc406ad..f6a5a619 100644 --- a/tabby-terminal/src/api/baseTerminalTab.component.ts +++ b/tabby-terminal/src/api/baseTerminalTab.component.ts @@ -771,11 +771,12 @@ export class BaseTerminalTabComponent

extends Bas } }) - if (destroyOnSessionClose) { - this.attachSessionHandler(this.session.closed$, () => { + this.attachSessionHandler(this.session.closed$, () => { + const behavior = this.profile.behaviorOnSessionEnd + if (destroyOnSessionClose || behavior === 'close' || behavior === 'auto' && this.isSessionExplicitlyTerminated()) { this.destroy() - }) - } + } + }) this.attachSessionHandler(this.session.destroyed$, () => { this.setSession(null) @@ -841,4 +842,11 @@ export class BaseTerminalTabComponent

extends Bas cb(this) } } + + /** + * Return true if the user explicitly exit the session + */ + protected isSessionExplicitlyTerminated (): boolean { + return false + } }