From a0643414d673b89e01c08bc455d6949f19228bd2 Mon Sep 17 00:00:00 2001 From: "mdj2812-homelab-bot[bot]" <300142459+mdj2812-homelab-bot[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 17:39:50 +0800 Subject: [PATCH] fix: OSC 52 clipboard with execCommand fallback for user gesture requirement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit navigator.clipboard.writeText() requires transient user activation, but OSC 52 clipboard sequences arrive asynchronously from the PTY server via WebSocket — outside the browser's gesture window. Register an additional OSC 52 handler after the ClipboardAddon that uses document.execCommand('copy') with a hidden textarea, which does not require user activation. Fixes clipboard integration for Neovim, tmux, and other terminal programs that rely on OSC 52 sequences. --- html/src/components/terminal/xterm/index.ts | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/html/src/components/terminal/xterm/index.ts b/html/src/components/terminal/xterm/index.ts index b332154b2..5ab9ee915 100644 --- a/html/src/components/terminal/xterm/index.ts +++ b/html/src/components/terminal/xterm/index.ts @@ -163,6 +163,28 @@ export class Xterm { terminal.loadAddon(fitAddon); terminal.loadAddon(overlayAddon); terminal.loadAddon(clipboardAddon); + + // Workaround: navigator.clipboard.writeText() requires user activation, + // but OSC 52 sequences arrive asynchronously via PTY, outside the gesture + // window. Use execCommand('copy') as a fallback that doesn't require activation. + terminal.parser.registerOscHandler(52, (data: string) => { + const args = data.split(';'); + if (args.length < 2 || args[1] === '?' || !args[1]) return false; + try { + const text = atob(args[1]); + const ta = document.createElement('textarea'); + ta.value = text; + ta.style.cssText = 'position:fixed;opacity:0'; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); + return true; + } catch (_) { + return false; + } + }); + terminal.loadAddon(webLinksAddon); terminal.open(parent);