Skip to content

Copying TUI-displayed text includes fake line breaks from terminal wrapping #217

@aebrer

Description

@aebrer

Summary

When text wraps due to terminal width in the TUI, selecting and copying it with the terminal's native selection (mouse drag, etc.) includes artificial line breaks at each wrap boundary. The copied text is fragmented with \n characters that don't exist in the original content. Users should be able to copy the actual text as it is, without these wrap-induced breaks.

Current Behavior

  • The TUI wraps text via wrapTextWithAnsi() in packages/tui/src/utils.ts to fit the viewport width
  • When a user selects wrapped text in their terminal emulator and copies it, the clipboard receives the text with line breaks inserted at every wrap point
  • The only existing copy mechanism is the /copy command (handleCopyCommand() in interactive-mode.ts), which copies the last assistant message's original text — but this doesn't help when the user wants to copy any arbitrary text (user messages, code blocks, status text, etc.)

Proposed Behavior

Provide a way to copy displayed text from the TUI that preserves the original unwrapped content, without terminal-width line breaks.

Acceptance Criteria

  • Users can copy text from any message/region in the TUI without wrap-induced \n characters
  • The copied text matches the original source text (before wrapTextWithAnsi was applied)
  • The solution works across common terminal emulators (not dependent on specific terminal soft-wrap support)

Context

  • Text wrapping implementation: packages/tui/src/utils.ts (wrapTextWithAnsi, wrapSingleLine)
  • TUI Text component: packages/tui/src/components/text.ts
  • Current copy command: packages/coding-agent/src/modes/interactive/interactive-mode.ts (handleCopyCommand)

Technical Notes

Possible approaches to explore:

  1. TUI text selection mode — Allow keyboard/mouse navigation to select a region of the TUI; the selection logic maps back to the original Text component's source string and copies that via copyToClipboard()
  2. Per-message copy commands — Extend /copy or add keybindings to copy specific messages (e.g., copy the message under cursor)
  3. Terminal soft-wrap hints — Some terminals support distinguishing soft wraps from hard wraps via escape sequences, but this is not universal enough to be the primary solution

The Text component already caches the original this.text separately from the wrapped cachedLines, which could help map rendered lines back to source text.

Research note: During assessment, look up how other TUI tools (e.g., tmux, vim, neovim, kakoune, helix, lazygit) handle text selection and copying without capturing wrap-induced line breaks. Some may use internal selection buffers, OSC 52 sequences, or terminal soft-wrap signaling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions