diff --git a/src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx b/src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx index 733ae805e16..8da354166f7 100644 --- a/src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx +++ b/src/renderer/src/components/dashboard-popout/AgentTerminalPreview.tsx @@ -227,7 +227,8 @@ export function AgentTerminalPreview({ macOptionAsAlt: macOptionAsAltRef.current, keybindings: useAppStore.getState().keybindings, terminalInput: terminalInputRef.current, - kittyKeyboardActive: () => kittyKeyboardModes.flags > 0 + kittyKeyboardActive: () => kittyKeyboardModes.flags > 0, + terminalShortcutPolicy: settingsRef.current?.terminalShortcutPolicy }) }) } diff --git a/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.test.ts b/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.test.ts index 4baee390db0..b76c7c14699 100644 --- a/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.test.ts +++ b/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.test.ts @@ -24,6 +24,7 @@ function contextFor( keybindings: undefined, terminalInput: LOCAL_MAC, kittyKeyboardActive: () => overrides.kitty === true, + terminalShortcutPolicy: 'orca-first', ...overrides } } @@ -110,6 +111,34 @@ describe('resolvePreviewShortcutAction', () => { ).toEqual({ type: 'splitActivePane', direction: 'vertical' }) }) + // Why: a terminal-first user remapped terminal.closePane away, so only the + // tab.close alias still matches Ctrl+W — which the shell owns as a word-kill. + // The pane yields it; the preview must not swallow it. + it('follows the terminal shortcut policy for the tab.close pane-close alias', () => { + const keybindings = { + 'terminal.closePane': ['Mod+Shift+W'], + 'tab.close': ['Ctrl+W'] + } as unknown as PreviewShortcutContext['keybindings'] + const chord = (): KeyboardEvent => keydown({ key: 'w', code: 'KeyW', ctrlKey: true }) + expect( + resolvePreviewShortcutAction( + chord(), + contextFor({ clientPlatform: 'linux', terminalInput: null, keybindings }) + ) + ).toEqual({ type: 'closeActivePane' }) + expect( + resolvePreviewShortcutAction( + chord(), + contextFor({ + clientPlatform: 'linux', + terminalInput: null, + keybindings, + terminalShortcutPolicy: 'terminal-first' + }) + ) + ).toBeNull() + }) + it('leaves ordinary typing to xterm', () => { expect( resolvePreviewShortcutAction(keydown({ key: 'a', code: 'KeyA' }), contextFor()) diff --git a/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.ts b/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.ts index b2956452ec5..e235c8d4992 100644 --- a/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.ts +++ b/src/renderer/src/components/dashboard-popout/preview-terminal-shortcuts.ts @@ -5,7 +5,11 @@ import { } from '@/components/terminal-pane/terminal-shortcut-policy' import { getLayoutBaseCharacterForCode } from '@/lib/keyboard-layout/layout-base-character' import type { DashboardCardTerminalInput } from '../../../../shared/dashboard-snapshot' -import type { KeybindingOverrides } from '../../../../shared/keybindings' +import { + normalizeTerminalShortcutPolicy, + type KeybindingOverrides, + type TerminalShortcutPolicy +} from '../../../../shared/keybindings' export type PreviewShortcutContext = { clientPlatform: NodeJS.Platform @@ -17,6 +21,8 @@ export type PreviewShortcutContext = { terminalInput: DashboardCardTerminalInput | null /** Live kitty-protocol flags mirrored from this pty's output. */ kittyKeyboardActive: () => boolean + /** The user's setting; terminal-first yields the tab.close alias to the shell. */ + terminalShortcutPolicy: TerminalShortcutPolicy | null | undefined } /** @@ -44,6 +50,9 @@ export function resolvePreviewShortcutAction( getLayoutBaseCharacterForCode, () => context.terminalInput?.windowsShiftEnterEncoding ?? 'alt-enter', // Why: byte protocols follow the pty's host, which differs from the client OS on remote runtimes. - () => hostPlatform === 'win32' + () => hostPlatform === 'win32', + // Why: without it a terminal-first user's remapped tab.close chord — Ctrl+W + // is a shell word-kill — reaches the shell in the pane but is swallowed here. + normalizeTerminalShortcutPolicy(context.terminalShortcutPolicy) ) }