fix(terminal): ship the copy clean as a trailing trim, without the shared dedent

#451 cleaned two things on copy. The trailing trim is right and every native
terminal does it. The shared leading-indent strip is this project's own rule,
and it is dropped here rather than shipped.

Measured against the shipped transform over 401,445 three-row windows across
1,010 tracked files in this repo, it fired on 73% of them: 92% inside a YAML
workflow, 76% over `git log` output, 48% in a TypeScript source. No width
threshold separates a margin from content because they are the same widths, a
live Claude Code pane's own margins measuring 2 and 5 columns while the most
common non-TUI shared run is 4. The failure modes are not symmetric either: a
wrong trailing trim costs nothing, while a wrong dedent silently deletes
information that was on the screen, with nothing in the clipboard to hint at
it, on git log bodies, on indented code read out of cat (semantic in Python),
on git diff context rows where the leading space is the marker, and on stack
traces.

It also could not be made self-consistent cheaply. Whether the first row joined
the measurement depended on the mousedown COLUMN, which the user never sees, so
one block of three rows produced three different clipboard results; and the
flag read getSelectionPosition().start, which is xterm's mousedown anchor and
is never normalised, so dragging UP through a block read it off the bottom row.
The PR's test stub hardcoded a downward drag, so its suite could not express
that case.

The transform, the wiring, the tests, the invariants, CLAUDE.md, the wiki page
and the changeset all move together. The test block now pins the ABSENCE as a
contract, with the git log, Python and git diff cases as its examples, so this
is not re-derived later. If it is ever revisited, the one qualification that
measured clean is painted trailing padding: zero false positives over all
401,445 windows.

Also from the review: the comments and invariant rule justifying the
padding-only clear described the pre-change code (the Ctrl+C gate reads the
CLEANED selection now, so such a selection falls through to the PTY on its own
and the clear is feedback rather than protection), the new 'Nothing to copy'
toast gained its zh-CN entry, and the invariants paragraph no longer repeats
its own opening sentence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-09-19 12:18:39 +02:00
parent c376534a50
commit 4c705094f7
8 changed files with 134 additions and 160 deletions
+13 -26
View File
@@ -2,30 +2,17 @@
"aicodeman": patch
---
fix(terminal): trim the padding and shared indent out of a copied selection
fix(terminal): trim the padding out of a copied selection
Copying several rows out of a pane put a wall of spaces on the clipboard and
repeated the program's own margin on every line. xterm hands back whole screen
rows and trims only the cells that were never written to, so the real spaces a
full-screen TUI paints across the unused part of a row count as content:
measured against Claude Code in a 282-column pane, single lines arrived
carrying 138 trailing spaces on top of a two-space transcript indent. Pasting
that into a chat client or an editor meant deleting the whitespace by hand,
while Windows Terminal, iTerm2 and GNOME Terminal all trim it for you.
A copy now drops the trailing run from every line. It also removes the leading
run, but only the one that every selected row shares and only when the
selection covers more than one row, so shell output is untouched, a single line
keeps the indentation you can see, and anything nested inside a copied block
keeps its relative indentation. A drag that starts inside a row keeps that
partial first line exactly as it was. An `Alt+drag` rectangular selection is
copied verbatim, because its columns lining up is the point of that gesture.
The main terminal's four copy paths go through it: the Ctrl+C chord,
right-click, the phone selection button and Auto Copy. The browser's own
Edit menu copy, a copy shortcut you disabled in settings, and the subagent
windows still copy the raw rows, as they did before. A selection holding nothing but padding is now
refused rather than copied as bare line breaks, and it clears the selection on
the way out so Ctrl+C goes straight back to interrupting. Auto Copy reads its
own toggle before it reads the selection, so a pane nobody is copying from
costs nothing on mouseup.
Copying out of a pane put a wall of spaces on the clipboard. xterm hands back
whole screen rows and trims only the cells that were never written to, so the
real spaces a full-screen program paints across the unused part of a row count
as content: measured against Claude Code in a 282-column pane, single lines
arrived carrying 138 trailing spaces. Pasting that into a chat client or an
editor meant deleting the whitespace by hand, while Windows Terminal, iTerm2 and
GNOME Terminal all trim it for you. A copy now drops the trailing run from every
line, on all four paths (the Ctrl+C chord, right-click, the phone selection
button and Auto Copy), while leading indentation is left exactly as it is. An
Alt+drag rectangular selection is copied verbatim, because its columns lining up
is the point of that gesture. A selection holding nothing but padding is refused
rather than copied as bare line breaks.