Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/renderer/terminal/TerminalSessionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
21 changes: 12 additions & 9 deletions src/renderer/terminal/terminalKeybindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
51 changes: 39 additions & 12 deletions src/test/renderer/terminalKeybindings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down
Loading