Skip to content

Terminal: copying agent output keeps the agent's line breaks and 2-space indent (herdr's TUI copies one clean line) #2558

Closed

Description

@matiemer

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)

  1. 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. …"
  2. Drag over the reply in Ovis's Terminal lens (copy on select).
  3. 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, through copySelection()), so xterm.js gets the grid rows, newline and indent included.
  • The text that replaces it is fetchPaneSelection → GET /api/pane/selection → herdr's pane.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-unwrapped keeps 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

  1. added a commit that references this issue on Nov 2, 2026
    c47806a
  2. added a commit that references this issue on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions