Skip to content

fix(markdown): wrap long lines in code blocks like pi's TUI - #1369

Merged
agegr merged 2 commits into
mainfrom
fix/issue-1360-wrap-code
Oct 29, 2026
Merged

agegr merged 2 commits into
mainfrom
fix/issue-1360-wrap-code

Conversation

@agegr

@agegr agegr commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Problem

Long lines in code fences scrolled sideways, by over 2,000px on a phone, which makes model output in ``` blocks hard to read (#1360). The issue asked for a wrap toggle; instead fences now wrap by default, as pi's TUI does (it passes every rendered line, code blocks included, through wrapTextWithAnsi). No toggle, no setting.

Change

  • components/MermaidBlock.tsx: CodeBlock gives react-syntax-highlighter a small renderer (renderCodeLines()) that makes each line a flex row of its number and its text. The text wraps (pre-wrap, overflow-wrap: anywhere for URLs and paths with no space), and a wrapped line continues under its text, never under the number. The text span keeps the row's classes, so the middle lines of a block comment keep their color. The streamed <pre> wraps the same way. Copy still copies the source text.
  • app/globals.css: the row rules (flex: 1 1 0, so layout does not first measure every line unwrapped); code fences leave the sideways scroll-edge fade.
  • components/ChatWindow.tsx: extension dialogs widen for code that would scroll sideways ([Feature] 扩展弹窗尺寸可配置:长内容(如格式化 SQL)在 560px 宽弹窗里显示局促 #947, fix(chat): let extension dialogs and the custom panel be widened (#947) #1032). With wrapping it never does, so the fit measures fences unwrapped for a moment (.extension-dialog-measuring) and still grows where the screen allows; code wraps only past that.
  • Everything that renders Markdown through CodeBlock follows (custom and compaction messages, skills and subagent viewers, the file viewer's Markdown preview, the Mermaid source view). The file viewer's source view and tool cards are unchanged.
  • Side effect: each line is now exactly lineHeight tall (about 1px less than before, when the <pre>'s theme font and the code font mixed), the same as while streaming.
  • e2e/extension-dialog.mjs, demo/ mirror, docs/agents/sessions.md, tests in components/MermaidBlock.test.mjs.

Verification

  • Headless Chrome on production builds: no fence scrolls sideways at 1280 or 390px, light and dark (main: 2,214–2,737px); continuation lines start at the text column; indentation, empty lines, CRLF and tabs keep their rows; colors match main character for character across tsx / html / markdown samples; mouse copy and Cmd+C give byte-identical text to main (no line numbers); Find-in-page works.
  • Layout cost with flex: 1 1 0: 75 fences / ~2,300 lines ≈ 220 ms vs 217 ms on main (4× CPU throttle).
  • Extension dialogs: same widths as main (680px at 1280, 350px at 390); the new e2e assertion fails on main and passes here.
  • tsc --noEmit (root and demo), eslint, npm test 2,872 tests, 0 fail; full e2e/run.mjs passes except the minimap step that already fails on local macOS runs.
  • Not verified: Firefox and Safari.

Fixes #1360

🤖 Generated with Claude Code

Code fences rendered every line on one row (`white-space: pre` from the
highlighter, `overflow-x: auto` on the streamed <pre>), so a long line,
URL or path made the block scroll sideways, by thousands of pixels on a
phone. pi's TUI wraps every rendered line, code blocks included.

CodeBlock now renders each line as a flex row of its number and its text
(`renderCodeLines()`, `.markdown-code-line`): the text wraps (`pre-wrap`,
`overflow-wrap: anywhere` for tokens without spaces) and a wrapped line
goes on under its text, never under the number. The text keeps the row's
own classes, which color the later lines of a multi-line token such as a
block comment. The streamed <pre> wraps the same way. No toggle: wrapping
is the one behavior, as in the TUI. Copy still copies the source text.
Each line is now exactly `lineHeight` tall (1px less than before, when
the <pre>'s theme font and the code font mixed), as in the streamed <pre>,
so a block no longer grows when its stream ends.

Extension dialogs widen to show code that would scroll sideways (#947);
with wrapping it never does, so the dialog fit now measures fences
unwrapped for a moment and still grows where the screen allows, the code
wrapping only past that. The code-block scroll-edge fade is dropped, since
fences no longer scroll sideways. demo/ mirrors the change.

Fixes #1360

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@agegr
agegr merged commit 98228f6 into main Oct 29, 2026
4 checks passed
@agegr
agegr deleted the fix/issue-1360-wrap-code branch October 29, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: wrap long lines in ``` blocks

1 participant