Clean Copy for Multiline and Soft-Wrapped Output

Open 💬 11 comments Opened Feb 19, 2026 by Ragnoroct
💡 Likely answer: A maintainer (fcoury-oai, contributor) responded on this thread — see the highlighted reply below.

What variant of Codex are you using?

CLI

What feature would you like to see?

Feature request: better multiline and wordwrap copy in the Codex CLI TUI.

Problems: wrapped lines copy with a 2-space indent that breaks things like HEREDOCs, and long single-line commands get split by TUI wordwrap so pasted commands fail.

Expected behavior: wrapping should be visual only, and the constant indent should not appear in copied text.

Possible solutions: (A) a copy mode that removes indentation and uses native terminal wrap, (B) a clean-copy keybind that strips indent and rejoins soft wraps, (C) add stable markers so external clipboard hooks can reconstruct logical lines.

Additional information

_No response_

View original on GitHub ↗

11 Comments

Ragnoroct · 5 months ago

I checked for duplicates and found #9252; this one covers the remaining copy behavior: no indents and no soft-wrap artifacts, so long commands/URLs and heredocs paste exactly.

NichUK · 4 months ago

I came to report exactly this. Copy and Paste for commands/links that Codex produces is unusable without manual repair which is a complete waste of time, and quite annoying. :)
A clean copy on click would be best, since we can't do keyboard select yet on Windows CLI. Otherwise selection only of the command/link text and automatic removal of the line-breaks would be ideal for those who don't use a mouse unless they have to.

br3ndonland · 4 months ago

This clean copy behavior would be helpful to have. It's something I think is particularly well-implemented in OpenCode that I miss when I switch to Codex.

For comparison, in OpenCode the "highlighted text to clipboard" behavior works basically like this:

  1. You highlight text in the TUI with your mouse.
  2. On mouse release, Selection.copy() calls renderer.getSelectedText() to extract the logical text from the terminal buffer.
  3. The text is written to the system clipboard via Clipboard.copy().
  4. A "Copied to clipboard" toast appears and the selection is cleared.

Relevant code:

| File | Role |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| packages/opencode/src/cli/cmd/tui/util/selection.ts | Reads highlighted text from the terminal renderer, copies it, shows a toast |
| packages/opencode/src/cli/cmd/tui/util/clipboard.ts | Cross-platform clipboard write (OSC 52 for SSH/tmux, pbcopy on macOS, xclip/wl-copy on Linux, PowerShell on Windows) |

unsupervisednn · 3 months ago

Was this broken recently?

xiaodream551-a11y · 1 month ago

Adding a concrete table/link case that appears to belong to this issue, and also overlaps with the right-edge wrapping class in #22109.

Observed case:

  • Codex CLI renders a Markdown table in assistant output.
  • The rightmost column contains a long link/URL.
  • When that link soft-wraps at the terminal edge, selecting/copying just the link with the mouse is difficult.
  • The selection commonly crosses into the next rendered table row, so the copied text includes table framing such as | or content from the row below.

Small shape of the problem:

| Name | Link |
| ---- | ---- |
| a | https://example.com/some/really/long/path/that/wraps/near/the/right/edge |
| b | https://example.com/another/path |

If the first URL is in the last visible table column and wraps, a normal terminal text selection can pick up part of the next row's border/cell when trying to select the wrapped URL only.

Expected behavior:

  • Copying a wrapped URL/link from rendered Codex CLI output should preserve the logical link text only.
  • Soft-wrap indentation, Markdown table borders, and adjacent row content should not leak into the copied text.

Environment where this was reported from:

  • codex-cli 0.133.0
  • macOS arm64: Darwin 25.5.0
  • Ghostty 1.3.1 (TERM=xterm-ghostty)

This is slightly narrower than the general long-command case because Markdown table layout makes the accidental copied artifacts look like table borders or the next row, but the underlying ask is the same: a clean copy path for logical text instead of terminal-buffer soft-wrap artifacts.

fcoury-oai contributor · 1 month ago

Many of the issues here can be solved by activating raw mode before copying (alt-r by default but can be reassigned using /keymap).

We'll be addressing the remaining issues.

fcoury-oai contributor · 1 month ago

If you could try alt-r for your issue here and send some feedback, that would be much appreciated.

dan-bronstein-zipline · 1 month ago
If you could try alt-r for your issue here and send some feedback, that would be much appreciated.

v0.133.0, xterm-256 in terminator

@fcoury-oai Raw mode only works if the response is printed while the TUI is in raw mode. Otherwise, linebreaks will be inserted into the answer wherever the line gets wrapped, regardless if you switch into raw mode before copying.

Reproducible simply by having codex parrot back a very long line i.e. XXXX..... and switching between modes

fcoury-oai contributor · 1 month ago
@fcoury-oai Raw mode only works if the response is printed while the TUI is in raw mode. Otherwise, linebreaks will be inserted into the answer wherever the line gets wrapped, regardless if you switch into raw mode before copying.

Yes, they get wrapped because that's what the Terminal is supposed to do, but when you copy and paste it they should be full paragraphs, that's the main difference.

dan-bronstein-zipline · 1 month ago
but when you copy and paste it they should be full paragraphs

I am seeing inconsistent results; sometimes line feeds are inserted where long words are being soft-wrapped in raw mode e.g.

Write out the numbers one to thirty as a single word

onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourteenfifteensixteenseventeeneighteennineteentwentytwentyonetwentytwotwentythreetwentyfourtwentyfivetwentysixtwentyseventwentyeighttwentyninethirty

Again

onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourteenfifteensixteenseventeeneighteennineteentwentytwentyonetwentytwotwentythreetwentyfourtwentyfivetwentysi
xtwentyseventwentyeighttwentyninethirty

The first output copy-pastes cleanly as one word from raw mode. The second one doesn't; the line break in the second output follows the soft-wrapping location as I resize the window

Rokurolize · 1 month ago

Note: This issue description was drafted with AI assistance. I personally encountered the underlying issue. If any AI-slop-like shortcomings remain, they are due to my prompting, and I welcome suggestions on how to communicate my requirements more clearly to Codex GPT-5.5 xhigh.

Adding a concrete raw-mode failure case for the clean-copy behavior discussed here.

Raw mode is intended to make terminal soft wrapping selection-friendly for source-like logical lines, but a mixed CJK + Markdown-link/URL line can still reconstruct with a hard newline inserted inside the source line. That means raw mode does not always produce the full-paragraph copy behavior described above.

Version / source checked:

  • npm latest checked on 2026-06-04 UTC: @openai/codex 0.137.0
  • Source-level repro against openai/codex commit 80b65e99457344f5f05fb4948eb35c6b1d320e3f (origin/main when checked on 2026-06-04 UTC, after the 0.137.0 npm publish)

This does not require a live model response. The repro uses the TUI VT100Backend and screen().contents() as a copy-like reconstruction path.

Minimal repro:

  1. Check out openai/codex at 80b65e99457344f5f05fb4948eb35c6b1d320e3f.
  2. Add this test inside the existing #[cfg(test)] mod tests in codex-rs/tui/src/insert_history.rs:
#[test]
fn vt100_raw_terminal_copy_reconstruction_preserves_mixed_cjk_url_line() {
    let width: u16 = 80;
    let height: u16 = 12;
    let backend = VT100Backend::new(width, height);
    let mut term = crate::custom_terminal::Terminal::with_options(backend).expect("terminal");
    term.set_viewport_area(Rect::new(0, height - 1, width, 1));

    let source = "検証は、抽出スクリプト再実行、SHA256取得、全体プリフライトまで通しました。参照先は製品公式の[導入ガイド](https://www.example.com/docs/product/intro/)、[設定サービス説明](https://www.example.com/docs/product/settings/service/)、[長い参照表](https://www.example.com/docs/mobile/reference/very_long_reference_details.html)を確認しています。";
    insert_history_lines_with_wrap_policy(
        &mut term,
        vec![Line::from(source)],
        HistoryLineWrapPolicy::Terminal,
    )
    .expect("insert raw mixed CJK/URL line");

    let copied_like_contents = term.backend().vt100().screen().contents();
    assert!(
        copied_like_contents.contains(source),
        "raw terminal-wrap history should preserve the source logical line for copy reconstruction; got:\n{copied_like_contents}"
    );
}
  1. Run from codex-rs/:
cargo test -p codex-tui vt100_raw_terminal_copy_reconstruction_preserves_mixed_cjk_url_line --lib -- --nocapture

Actual result on 80b65e99457344f5f05fb4948eb35c6b1d320e3f after adding only the test:

thread 'insert_history::tests::vt100_raw_terminal_copy_reconstruction_preserves_mixed_cjk_url_line' ... panicked
検証は、抽出スクリプト再実行、SHA256取得、全体プリフライトまで通しました。参照先は製品公式の[導入ガイド](https://www.example.com/docs/product/intro/)、[設定サービス説明](https://www.example.com/docs/product/settings/service/)、[長い参照表](https://www.example.com/docs/mobile/reference/very_long_reference_details.html)
を確認しています。
test insert_history::tests::vt100_raw_terminal_copy_reconstruction_preserves_mixed_cjk_url_line ... FAILED
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 2763 filtered out; finished in 0.00s

Expected result:

Raw mode should preserve this source logical line well enough that terminal-selection-style reconstruction contains the original line without inserting a hard newline before を確認しています。.

If terminal-native selection cannot be made reliable for mixed-width CJK plus URL/Markdown-link content, raw mode probably needs either an internal source-copy path or narrower wording about when terminal selection is source-faithful.

Related but narrower than #22109: that issue covers broader TUI right-edge history/live-output divergence; this case is specifically about raw-mode copy reconstruction, where raw mode is the proposed clean-copy path for this issue.