From 783b21dc932c5b3ecbdea4cd584effb39ff5bc41 Mon Sep 17 00:00:00 2001 From: suraj-markup Date: Mon, 2 Mar 2026 00:03:14 +0530 Subject: [PATCH] feat(terminal): add Cmd+C/V and Ctrl+Shift+C/V clipboard shortcuts Intercept keyboard shortcuts in xterm.js so users can copy selected text with Cmd+C (Mac) or Ctrl+Shift+C (Linux/Win), and paste from system clipboard with Cmd+V / Ctrl+Shift+V. Without text selected, Cmd+C still sends SIGINT as expected. Co-Authored-By: Claude Opus 4.6 --- .../web/src/components/DirectTerminal.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/web/src/components/DirectTerminal.tsx b/packages/web/src/components/DirectTerminal.tsx index 9d7f505c1..a4c1221c3 100644 --- a/packages/web/src/components/DirectTerminal.tsx +++ b/packages/web/src/components/DirectTerminal.tsx @@ -228,6 +228,44 @@ export function DirectTerminal({ } }); + // Intercept Cmd+C/V (Mac) and Ctrl+Shift+C/V (Linux/Win) for clipboard + terminal.attachCustomKeyEventHandler((e: KeyboardEvent) => { + if (e.type !== "keydown") return true; + const isMac = navigator.platform.toUpperCase().includes("MAC"); + + if (isMac && e.metaKey && !e.ctrlKey && !e.altKey) { + if (e.code === "KeyC" && terminal.hasSelection()) { + navigator.clipboard?.writeText(terminal.getSelection()).catch(() => {}); + return false; + } + if (e.code === "KeyV") { + navigator.clipboard?.readText().then((text) => { + if (text && websocket.readyState === WebSocket.OPEN) { + terminal.paste(text); + } + }).catch(() => {}); + return false; + } + } + + if (!isMac && e.ctrlKey && e.shiftKey) { + if (e.code === "KeyC" && terminal.hasSelection()) { + navigator.clipboard?.writeText(terminal.getSelection()).catch(() => {}); + return false; + } + if (e.code === "KeyV") { + navigator.clipboard?.readText().then((text) => { + if (text && websocket.readyState === WebSocket.OPEN) { + terminal.paste(text); + } + }).catch(() => {}); + return false; + } + } + + return true; + }); + // Handle window resize const handleResize = () => { if (fit && websocket.readyState === WebSocket.OPEN) {