Make copy actually copy on Wayland (main-process clipboard + OSC 52) - #55
Conversation
Copying out of the terminal did nothing on Linux/Wayland. Two separate holes:
- Ctrl+C copy used navigator.clipboard.writeText in the renderer. Chromium
gates that on window focus + a user gesture, and it's effectively dead under
Ozone/Wayland. The trailing .catch(() => {}) ate the rejection, so it failed
in total silence.
- OSC 52 (how Claude Code itself copies) wasn't wired up at all — xterm doesn't
do it for you, so those copies just evaporated.
Both now go through the main-process clipboard over IPC, which doesn't care about
focus or gestures. Paste was never affected, so it's left alone.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Confirmed working. Built this branch and ran it on Ubuntu / GNOME Shell 49 / native Wayland (Ozone), Electron 41.0.3 — the same box where stock 0.0.30 copies nothing. Both paths now land in the system clipboard and paste cleanly into other apps:
Paste was unaffected throughout. No regressions spotted in normal terminal use. |
…18) navigator.clipboard.writeText is gated on focus/user-activation and silently fails on Linux/Wayland (Ozone). Replace with a main-process clipboard.writeText via a new clipboard-write-text IPC. Also register an xterm OSC 52 handler so programs inside the terminal (notably Claude Code's copy-to-clipboard) can set the system clipboard; xterm doesn't wire OSC 52 itself. Port of upstream PR doctly#55 (ymajoros). Closes upstream issue #54 from our fork's perspective; the upstream PR will close it fully when merged.
…-back refusal
Two review follow-ups on the OSC 52 handler.
writeClipboard is an ipcRenderer.invoke, so it returns a promise. The code it
replaced ended in .catch(() => {}); without it an IPC failure surfaces as an
unhandled rejection. Restored.
The handler's security property — that a read-back query ("<selection>;?") is
consumed but never answered — lived in a bare `b64 === '?'` check that reads
like an unimplemented case. Answering it would write the user's clipboard back
into the terminal, letting any program in the session exfiltrate whatever was
last copied. Extracted the parse into decodeOsc52Payload() so the refusal is
documented and covered by tests that say why, rather than being one condition
away from someone "completing" it.
Pure refactor otherwise: verified the extracted helper produces identical
(handled, written) results to the previous inline logic across write, query,
empty, separator-less, multi-byte and malformed-base64 payloads.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Reviewed this and I'm happy with the approach — pushed two small follow-ups to the branch (6368f2e) rather than sending you back and forth. Also merged current 1. 2. Pinned the read-back refusal. This was the part I wanted to protect. Otherwise a pure refactor — I checked the extracted helper returns identical On the wider question of whether OSC 52 writes should sit behind a setting: I considered it and decided no. The capability is the feature here — it's how Claude Code copies — so a setting defaulting off just reintroduces the bug, and defaulting on is a knob nobody turns. The exfiltration direction is already refused, xterm.js bounds the payload at 10MB, and these terminals run agents with full shell access, so gating clipboard writes while handing out Thanks for the fix — the Wayland diagnosis and the main-process routing were both right. Merging once you've had a chance to look over my two commits. |
Fixes #54.
Copy was a black hole on Linux/Wayland — paste fine, copy gone. Two reasons, both about the renderer's rocky relationship with the clipboard:
1.
Ctrl+Cwent through the renderer.navigator.clipboard.writeText(...).catch(() => {})rejects on Ozone/Wayland (Chromium wants window focus + a user gesture) and the empty.catchswallowed it whole. Textbook silent failure.2. OSC 52 wasn't handled at all. That's the sequence Claude Code uses to copy. xterm doesn't register a handler for it on its own, so those copies went nowhere.
What this does
Adds a small
clipboard-write-textIPC that calls the main-processclipboard.writeText. The main process has none of the renderer's focus/gesture hangups, so it just writes on Wayland. Then:Ctrl+C(with a selection) uses it instead ofnavigator.clipboard?read-back query, which we don't answer)Paste was never broken, so it's untouched.
The diff (3 files, ~30 lines)
main.js— importclipboard, add the IPC handlerpreload.js— exposewriteClipboardpublic/terminal-manager.js— use it forCtrl+C, add the OSC 52 handlerterminal.parser.registerOscHandleris stable xterm API andallowProposedApi: trueis already set, so nothing new is required capability-wise.Testing
Ubuntu / GNOME 49 / Wayland, native Ozone.
Ctrl+C, and right-click Copy all dead, no error.Ctrl+C-with-selection both land in the system clipboard and paste cleanly into other apps.I only have the Linux box, so I'd love a second pair of eyes confirming macOS/Windows still behave — though main-process clipboard is the boring-and-reliable option on every platform, so I'd be surprised if it regressed anything.