feat: MobaXTerm-style clipboard — select to copy, right-click to paste
All checks were successful
Build & Sign Wraith / Build Windows + Sign (push) Successful in 1m3s

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) <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell 2026-03-17 13:32:57 -04:00
parent b7742b0247
commit 2ae628c858

View File

@ -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;