Skip to content

fix(terminal): copy selected text to clipboard automatically - #101

Merged
Lexus2016 merged 2 commits into
Lexus2016:mainfrom
Its-My-Work:fix/terminal-copy
Sep 13, 2026
Merged

Lexus2016 merged 2 commits into
Lexus2016:mainfrom
Its-My-Work:fix/terminal-copy

Conversation

@Its-My-Work

Copy link
Copy Markdown
Contributor

Summary

Right-click on a terminal pane never offered "Copy" — xterm.js clears the text selection on the right mousedown before the browser's context menu opens, and Ctrl+C can't be repurposed for copying since it's already sent through as SIGINT to whatever's running in the pane. In practice there was no way to copy terminal output at all short of it being echoed elsewhere.

xterm v4's copyOnSelect option was removed upstream; the documented replacement is wiring onSelectionChange() yourself. This copies the selection to the clipboard (via the existing copyText() helper, which already falls back to execCommand for non-secure-context/tunnel use) the moment a selection is made, matching how most terminal apps behave.

Test plan

  • Open a terminal pane, click-drag to select some output, confirm it lands on the system clipboard without pressing anything else
  • Confirm Ctrl+C still sends SIGINT as before (unrelated to this change)

🤖 Generated with Claude Code

https://claude.ai/code/session_011E2bJQ2sL9LgEWosjvydTR

Its-My-Work and others added 2 commits September 12, 2026 21:19
Right-click on a terminal pane never offered "Copy" — xterm.js clears
the text selection on the right mousedown before the browser's context
menu opens, and Ctrl+C is unavailable for copying since it's sent
through as SIGINT to the running process instead. The only way out was
selecting then relying on Ctrl+C, which just killed whatever CLI was
running.

xterm v4's copyOnSelect option was removed upstream; the replacement is
wiring onSelectionChange() ourselves. Copy the selection to the
clipboard (via the existing copyText() helper, which already falls back
to execCommand for non-secure-context/tunnel use) as soon as one is
made, matching how most terminal apps behave.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011E2bJQ2sL9LgEWosjvydTR
…change

xterm fires onSelectionChange from the selection service's refresh on every
animation frame while the mouse is still dragging, so the previous wiring was
a clipboard write per frame — and over plain http copyText() falls back to a
focused off-screen textarea, which stole focus from the pane mid-drag. The
existing mouseup handler already knows whether a selection was dragged out;
the copy now happens there, once, when the drag ends.

test/composer-terminal.test.js pins the new shape and asserts that
term.onSelectionChange is not used.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Lexus2016

Copy link
Copy Markdown
Owner

Thanks — right diagnosis, and copyText() is the right helper (it already has the execCommand fallback for http/tunnel use).

One change, pushed to your fix/terminal-copy branch as a follow-up commit (maintainer edit): the copy now happens in the existing mouseup handler instead of term.onSelectionChange. Checked against the vendored xterm.js: _fireOnSelectionChange is called from the selection service's _refresh, i.e. on every animation frame while the mouse is still dragging. That was a clipboard write per frame — and over plain http copyText() falls back to a focused off-screen <textarea>, which stole focus from the pane mid-drag. mouseup already knows whether a selection was dragged out, so the copy runs there, once, when the drag ends. test/composer-terminal.test.js pins the new shape and asserts onSelectionChange is not used.

npm test → 80 files, exit 0.

@Lexus2016
Lexus2016 merged commit ea47299 into Lexus2016:main Sep 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants