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,