diff --git a/src/renderer/terminal/TerminalSessionManager.ts b/src/renderer/terminal/TerminalSessionManager.ts index 4e01f009d..b755f2ce5 100644 --- a/src/renderer/terminal/TerminalSessionManager.ts +++ b/src/renderer/terminal/TerminalSessionManager.ts @@ -48,6 +48,7 @@ const SLOW_INPUT_HANDLER_MS = 16; const SLOW_INPUT_LOG_THROTTLE_MS = 2_000; const IS_MAC_PLATFORM = typeof navigator !== 'undefined' && /Mac|iPod|iPhone|iPad/.test(navigator.platform); +const IS_WINDOWS_PLATFORM = typeof navigator !== 'undefined' && /Win/i.test(navigator.platform); // Keys registered as global app shortcuts (Cmd on macOS, Ctrl on Linux/Windows). // Used to let these combos pass through xterm to the window-level shortcut handler @@ -381,8 +382,7 @@ export class TerminalSessionManager { return false; // Prevent xterm from processing the copy shortcut } - // Handle Ctrl+Shift+V paste on Linux - if (shouldPasteToTerminal(event, IS_MAC_PLATFORM)) { + if (shouldPasteToTerminal(event, IS_MAC_PLATFORM, IS_WINDOWS_PLATFORM)) { event.preventDefault(); event.stopImmediatePropagation(); event.stopPropagation(); diff --git a/src/renderer/terminal/terminalKeybindings.ts b/src/renderer/terminal/terminalKeybindings.ts index f24de697a..2f9c568ae 100644 --- a/src/renderer/terminal/terminalKeybindings.ts +++ b/src/renderer/terminal/terminalKeybindings.ts @@ -66,11 +66,14 @@ export function shouldKillLineFromTerminal(event: KeyEventLike, isMacPlatform: b } /** - * Detect Ctrl+Shift+V paste shortcut on Linux. - * Linux terminals use Ctrl+Shift+V as the standard paste shortcut, - * unlike Windows/macOS which use Ctrl+V/Cmd+V. + * Paste shortcut for the embedded terminal: Linux uses Ctrl+Shift+V; Windows uses Ctrl+V; + * macOS uses Cmd+V and is left to the browser/Electron default. */ -export function shouldPasteToTerminal(event: KeyEventLike, isMacPlatform: boolean): boolean { +export function shouldPasteToTerminal( + event: KeyEventLike, + isMacPlatform: boolean, + isWindowsPlatform: boolean +): boolean { if (event.type !== 'keydown') return false; if (event.key.toLowerCase() !== 'v') return false; @@ -79,11 +82,11 @@ export function shouldPasteToTerminal(event: KeyEventLike, isMacPlatform: boolea const alt = event.altKey === true; const shift = event.shiftKey === true; - // Ctrl+Shift+V is the standard paste shortcut in Linux terminals - // Only apply on non-Mac platforms (Linux/Windows with Linux-style terminals) - if (!isMacPlatform && ctrl && shift && !meta && !alt) { - return true; + if (isMacPlatform) return false; + + if (isWindowsPlatform) { + return ctrl && !meta && !alt; } - return false; + return ctrl && shift && !meta && !alt; } diff --git a/src/test/renderer/terminalKeybindings.test.ts b/src/test/renderer/terminalKeybindings.test.ts index f76deea6c..734bc129a 100644 --- a/src/test/renderer/terminalKeybindings.test.ts +++ b/src/test/renderer/terminalKeybindings.test.ts @@ -97,47 +97,74 @@ describe('TerminalSessionManager - Shift+Enter to Ctrl+J mapping', () => { ).toBe(false); }); - it('detects Ctrl+Shift+V paste on Linux only', () => { + it('detects paste shortcuts by platform', () => { const isMac = true; const isNotMac = false; + const isWin = true; + const isNotWin = false; - // Ctrl+Shift+V on Linux should trigger paste + // Linux: Ctrl+Shift+V expect( - shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true, shiftKey: true }), isNotMac) + shouldPasteToTerminal( + makeEvent({ key: 'v', ctrlKey: true, shiftKey: true }), + isNotMac, + isNotWin + ) ).toBe(true); - // Ctrl+Shift+V on macOS should NOT trigger paste (macOS uses Cmd+V) + // Linux: Ctrl+V must not paste (reserved for TTY) + expect(shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true }), isNotMac, isNotWin)).toBe( + false + ); + + // Windows: Ctrl+V and Ctrl+Shift+V + expect(shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true }), isNotMac, isWin)).toBe( + true + ); expect( - shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true, shiftKey: true }), isMac) - ).toBe(false); + shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true, shiftKey: true }), isNotMac, isWin) + ).toBe(true); - // Ctrl+V alone should NOT trigger (that's SIGINT in terminals) - expect(shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true }), isNotMac)).toBe(false); + // macOS: no match (Cmd+V elsewhere) + expect( + shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true, shiftKey: true }), isMac, isNotWin) + ).toBe(false); // Additional modifiers should NOT trigger expect( shouldPasteToTerminal( makeEvent({ key: 'v', ctrlKey: true, shiftKey: true, altKey: true }), - isNotMac + isNotMac, + isNotWin ) ).toBe(false); expect( shouldPasteToTerminal( makeEvent({ key: 'v', ctrlKey: true, shiftKey: true, metaKey: true }), - isNotMac + isNotMac, + isNotWin ) ).toBe(false); + expect( + shouldPasteToTerminal(makeEvent({ key: 'v', ctrlKey: true, altKey: true }), isNotMac, isWin) + ).toBe(false); + // Wrong key should NOT trigger expect( - shouldPasteToTerminal(makeEvent({ key: 'c', ctrlKey: true, shiftKey: true }), isNotMac) + shouldPasteToTerminal( + makeEvent({ key: 'c', ctrlKey: true, shiftKey: true }), + isNotMac, + isNotWin + ) ).toBe(false); // keyup should NOT trigger expect( shouldPasteToTerminal( makeEvent({ type: 'keyup', key: 'v', ctrlKey: true, shiftKey: true }), - isNotMac + isNotMac, + isNotWin ) ).toBe(false); });