Skip to content

TUI: terminal-native copy includes visual line breaks from wrapped output #1853

@Hmbown

Description

@Hmbown

Report

"here is a problem that when you copy some text from tui, the line breaks used by tui for formatting will also be copied out"

When a user selects text from the rendered TUI with a mouse drag (terminal-native selection → host clipboard / pbcopy / xclip etc.), the clipboard ends up containing the visual line breaks the TUI uses for layout — not the underlying logical text. Pasting the result into another window reproduces the TUI's wrap column instead of the original paragraph.

This is distinct from the in-app /copy flow that goes through crates/tui/src/tui/clipboard.rs — that path writes whatever string we hand it, and is not the source of the line breaks. The breaks come from the terminal capturing what is on screen verbatim, and the TUI renders assistant/transcript content with hard-wrapped rows.

Repro

  1. Open deepseek and run any prompt that produces a long paragraph in the transcript.
  2. Drag-select the paragraph with the mouse and copy via the terminal's built-in copy (⌘C on macOS Terminal/iTerm, Ctrl+Shift+C on most Linux terminals).
  3. Paste into a plain editor — every visual row ends with a \n at the TUI's wrap column.

Expected: copied text reflows as one logical paragraph (no spurious newlines mid-sentence).

Likely cause

The transcript / assistant cells render through ratatui with word-wrap producing physical rows. Mouse selection in a terminal copies the screen cells row-by-row with row-terminator newlines — the terminal cannot distinguish "soft" wrap from a real \n. We control rendering, so the fix lives on our side.

Options to consider

  • REP / soft-wrap hint: some terminals (iTerm2, kitty, WezTerm) honor SGR/DECRC sequences or have selection-aware behavior, but support is patchy and not portable.
  • Mouse-select handoff: intercept mouse selection inside the TUI (we already capture mouse for scroll) and provide our own selection layer that copies the source string via ClipboardHandler::write_text (which already does OSC 52 + pbcopy / Set-Clipboard fallbacks). This is the most portable fix.
  • Discoverable copy path: surface a keybinding / hint (e.g. Ctrl+Shift+Y to copy the last assistant message, or a [copy] action on the active cell) so users can avoid terminal-native selection entirely. The plumbing exists in clipboard.rs:124 (write_text); we'd just need a UI affordance.
  • Bracketed-paste-style "copy mode": a modal selection (think tmux copy-mode) where arrow keys grow a selection over the logical buffer, then Enter copies the unwrapped text. Heaviest implementation but the most robust.

Acceptance

  • Copying an assistant paragraph from the TUI and pasting into a plain editor yields a single logical line (or the original paragraph structure), with no wrap-column newlines injected.
  • Works on macOS Terminal, iTerm2, and a mainstream Linux terminal (gnome-terminal or Alacritty) at minimum.
  • Behavior is documented in docs/ (probably docs/MODES.md or a new docs/COPY.md) so users know which gesture produces a clean copy.

Pointers

  • crates/tui/src/tui/clipboard.rs — existing clipboard write path (arboard → pbcopy/Set-Clipboard → OSC 52).
  • crates/tui/src/tui/ui.rs, crates/tui/src/tui/transcript*.rs — transcript rendering / wrap.
  • Mouse capture currently used for scroll; would need to coexist with any new selection mode.

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Projects

    Status
    In progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions