Repository navigation
Description
What happened
When I copy a wrapped paragraph from an agent pane in the Terminal lens, every line break the agent drew stays in the copied text, followed by the agent's 2-space indent. Copying the same text with a drag in herdr's own TUI gives one clean line.
This breaks anything whitespace-sensitive. A shell command that the agent wrapped in the middle gets pasted as two lines, the second starting with --flag, so it runs only the first half or fails.
Reproduction (verified with real drags in both clients)
- In an agent pane (Claude Code 2.1.316 here), get a reply longer than one row. I used a synthetic one:
"Summary line. The build command is npx example-cli@latest build -p ios --profile beta --auto-submit and it should be copied exactly as one line without any extra spaces. Summary line. …" - Drag over the reply in Ovis's Terminal lens (copy on select).
- Drag over the same reply in herdr's TUI (Ghostty).
Ovis clipboard:
'Summary line. The build command is npx example-cli@latest build -p ios --profile beta --auto-submit and it should be copied exactly as one line without any\n extra spaces. Summary line. The build command is npx example-cli@latest build -p ios --profile beta --auto-submit and it should be copied exactly as one\n line.'
herdr TUI clipboard (same pane, same reply):
'Summary line. The build command is npx example-cli@latest build -p ios --profile beta --auto-submit and it should be copied exactly as one line without any extra spaces. Summary line. The build command is npx example-cli@latest build -p ios --profile beta --auto-submit and it should be copied exactly as one line.'
How often
Every time a selection crosses a wrapped line of agent output.
Versions
Ovis 0.4.21, herdr 0.9.5, Claude Code 2.1.316. Desktop Chrome (installed app) on Linux X11.
Analysis
The agent wraps the text itself. Claude Code is on the alternate screen, breaks the paragraph at the pane width and pads each continuation row with 2 spaces. Those are real cells in the grid, not a soft wrap. herdr pane read --source visible shows the continuation rows starting with .
Both of Ovis's copy sources return the cells as drawn:
- The immediate copy on release is
term.getSelection()(src/lib/useTerminalMount.ts:786,visibleText, throughcopySelection()), so xterm.js gets the grid rows, newline and indent included. - The text that replaces it is
fetchPaneSelection→GET /api/pane/selection→ herdr'spane.selection.read(server/herdr/client.ts:713). Called directly on the same rows, it returns the same thing:'Summary line. … exactly as one line\n without any extra spaces. … should be copied\n exactly as one lin'. herdr pane read --source recent-unwrappedkeeps the breaks and indent as well.
herdr's TUI copy reflows them, but only for agent output. For comparison, I drew the same layout from a plain script (alternate screen, 2-space padding, wrapped with \r\n) in a pane with no agent. There, a herdr TUI drag copies the indented, hard-broken text exactly like Ovis does. So the TUI joins an agent's wrapped rows and drops the continuation indent using logic that pane.selection.read (protocol 22, params pane_id/anchor/cursor/content_revision) doesn't expose. Copying from Ovis therefore gives a different result than copying from herdr for the same selection.
Suggested fix
- Preferred: use the same copy text herdr's TUI produces, for example through an option on
pane.selection.read(or a new RPC) that returns the TUI's reflowed text, and copy that. This might need a herdr change. - Ovis-side fallback: when the pane runs a detected agent, post-process the copied range the way the TUI does. Join a row onto the previous one when the previous row reaches the wrap edge and the row starts with the block's continuation indent, then strip that indent.
- Either way, the immediate
getSelection()copy and the herdr text that replaces it should both get this treatment, or the clipboard keeps the broken version when the delayed write is denied.
No duplicate found (searched: copy spaces / indentation / line break / wrapped lines / leading spaces / whitespace / newline).
Activity
fix(terminal): copy an agent's wrapped reply as one line, as herdr's …
chore(release): 0.4.22 (#2473)