From 2ae628c858178b333db11a41be70263130f19d71 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 17 Mar 2026 13:32:57 -0400 Subject: [PATCH] =?UTF-8?q?feat:=20MobaXTerm-style=20clipboard=20=E2=80=94?= =?UTF-8?q?=20select=20to=20copy,=20right-click=20to=20paste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Highlight text auto-copies to clipboard via onSelectionChange. Right-click on terminal pastes from clipboard by writing to SSH stdin. Disables xterm.js default right-click word-select behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/composables/useTerminal.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/src/composables/useTerminal.ts b/frontend/src/composables/useTerminal.ts index f5c883f..0b70345 100644 --- a/frontend/src/composables/useTerminal.ts +++ b/frontend/src/composables/useTerminal.ts @@ -66,6 +66,7 @@ export function useTerminal(sessionId: string): UseTerminalReturn { scrollback: 10000, allowProposedApi: true, convertEol: true, + rightClickSelectsWord: false, }); terminal.loadAddon(fitAddon); @@ -86,6 +87,24 @@ export function useTerminal(sessionId: string): UseTerminalReturn { }); }); + // MobaXTerm-style clipboard: highlight to copy, right-click to paste + const selectionDisposable = terminal.onSelectionChange(() => { + const sel = terminal.getSelection(); + if (sel) { + navigator.clipboard.writeText(sel).catch(() => {}); + } + }); + + function handleRightClickPaste(e: MouseEvent): void { + e.preventDefault(); + e.stopPropagation(); + navigator.clipboard.readText().then((text) => { + if (text) { + Call.ByName(`${SSH}.Write`, sessionId, text).catch(() => {}); + } + }).catch(() => {}); + } + // Listen for SSH output events from the Go backend (base64 encoded) let cleanupEvent: (() => void) | null = null; @@ -121,6 +140,9 @@ export function useTerminal(sessionId: string): UseTerminalReturn { terminal.open(container); fitAddon.fit(); + // Right-click paste on the terminal's DOM element + terminal.element?.addEventListener("contextmenu", handleRightClickPaste); + // Subscribe to SSH output events for this session // Wails v3 Events.On callback receives a CustomEvent object with .data property cleanupEvent = Events.On(`ssh:data:${sessionId}`, (event: any) => { @@ -175,6 +197,8 @@ export function useTerminal(sessionId: string): UseTerminalReturn { terminal.write(pendingData); pendingData = ""; } + terminal.element?.removeEventListener("contextmenu", handleRightClickPaste); + selectionDisposable.dispose(); if (cleanupEvent) { cleanupEvent(); cleanupEvent = null;