diff --git a/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts b/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts index 75b44ebbe7b..cb6f676eae6 100644 --- a/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts +++ b/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.test.ts @@ -107,6 +107,43 @@ describe('resolveTerminalShortcutAction', () => { }) }) + it('forwards Ctrl+Enter as the kitty CSI-u chord so TUIs can cue instead of send', () => { + // Why: xterm.js collapses Ctrl+Enter to a bare CR; intercept upstream and + // emit the kitty sequence (modifier code 5 = Ctrl) so probing TUIs receive + // the distinct chord on every platform. + expect( + resolveTerminalShortcutAction(event({ key: 'Enter', code: 'Enter', ctrlKey: true }), true) + ).toEqual({ type: 'sendInput', data: '\x1b[13;5u' }) + expect( + resolveTerminalShortcutAction(event({ key: 'Enter', code: 'Enter', ctrlKey: true }), false) + ).toEqual({ type: 'sendInput', data: '\x1b[13;5u' }) + // Windows uses the same kitty sequence for now: no TUI is known to treat the + // CSI-u Ctrl+Enter form as inert (cf. the Shift+Enter Codex-on-PowerShell case). + expect( + resolveTerminalShortcutAction( + event({ key: 'Enter', code: 'Enter', ctrlKey: true }), + false, + 'false', + 0, + true + ) + ).toEqual({ type: 'sendInput', data: '\x1b[13;5u' }) + + // Modifier combos that are NOT plain Ctrl+Enter must keep falling through. + expect( + resolveTerminalShortcutAction( + event({ key: 'Enter', code: 'Enter', ctrlKey: true, shiftKey: true }), + true + ) + ).toBeNull() + expect( + resolveTerminalShortcutAction( + event({ key: 'Enter', code: 'Enter', ctrlKey: true, metaKey: true }), + true + ) + ).toBeNull() + }) + it('translates Cmd+←/→ on macOS to readline start/end-of-line (Ctrl+A/E)', () => { expect( resolveTerminalShortcutAction( diff --git a/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts b/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts index d2d84976983..e0ac8a9cd4a 100644 --- a/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts +++ b/src/renderer/src/components/terminal-pane/terminal-shortcut-policy.ts @@ -103,6 +103,23 @@ export function resolveTerminalShortcutAction( return { type: 'sendInput', data: isWindows ? '\x1b\r' : '\x1b[13;2u' } } + if ( + event.ctrlKey && + !event.metaKey && + !event.altKey && + !event.shiftKey && + event.key === 'Enter' + ) { + // Why: xterm.js collapses Ctrl+Enter to a bare CR, so TUIs that expect + // modified Enter chords never receive the distinct input and treat it as + // plain Enter. Forward the kitty CSI-u sequence directly (modifier code + // 5 = Ctrl; cf. 2 = Shift above) so cue/queue behavior reaches the TUI. + // Sibling of the Shift+Enter case; a Windows fallback is not added yet + // because, unlike #2418's Codex-on-PowerShell inertness, no Windows TUI is + // known to drop the CSI-u form for Ctrl+Enter. + return { type: 'sendInput', data: '\x1b[13;5u' } + } + if ( event.ctrlKey && !event.metaKey && diff --git a/tests/e2e/terminal-shortcuts.spec.ts b/tests/e2e/terminal-shortcuts.spec.ts index de15695de16..9d13159f0f9 100644 --- a/tests/e2e/terminal-shortcuts.spec.ts +++ b/tests/e2e/terminal-shortcuts.spec.ts @@ -495,6 +495,16 @@ test.describe('Terminal Shortcuts', () => { ) }) + test('Ctrl+Enter writes the kitty modified-enter chord for terminal TUIs', async ({ + orcaPage, + electronApp + }) => { + await installMainProcessPtyWriteSpy(electronApp) + await waitForActivePanePtyId(orcaPage) + + await pressAndExpectWrite(orcaPage, electronApp, 'Control+Enter', '\x1b[13;5u') + }) + test('plain Ctrl+C sends ETX under kitty keyboard reporting', async ({ orcaPage, electronApp