Skip to content

feat(tui): persist thinking block across streaming with markdown body#31

Merged
hakula139 merged 7 commits intomainfrom
feat/tui-thinking-block
Apr 24, 2026
Merged

feat(tui): persist thinking block across streaming with markdown body#31
hakula139 merged 7 commits intomainfrom
feat/tui-thinking-block

Conversation

@hakula139
Copy link
Copy Markdown
Owner

@hakula139 hakula139 commented Apr 24, 2026

Summary

The thinking block evaporated the moment the model switched to prose — append_stream_token cleared the transient buffer before routing the first text chunk, and commit_streaming did the same at turn end, so a thinking-only turn (think → tool call) also left nothing behind. The reasoning is now committed into an AssistantThinking block so it stays in the transcript.

  • Flush thinking_buffer into a committed block in both append_stream_token and commit_streaming (no more silent clears).
  • Redraw the block as a dim bar on every line — Thinking... label flush with the bar, body flush under it (no hanging indent).
  • Pipe the body through render_markdown so inline code, emphasis, and fenced blocks keep their styling; a selective patch dims only spans without an explicit fg, so prose reads muted while code / links / headings / syntax-highlighted fences stay at full color.

Test plan

  • cargo fmt --all --check
  • cargo build
  • cargo clippy --all-targets -- -D warnings
  • cargo test — 927 passed
  • pnpm lint — 0 errors

Thinking tokens used to evaporate the moment the model switched to
prose — `append_stream_token` cleared the transient buffer before
routing the first text chunk, and `commit_streaming` did the same at
turn end, so a thinking-only turn (think → tool call) left nothing
visible either. Flush the buffer into a committed
`AssistantThinking` block instead, so the reasoning stays in the
transcript for the rest of the session.

Rework the block's visual language while we're here: a dim `│ ` bar
on every line replaces the `◇ Thinking...` hanging indent, the
header sits flush against the bar, and the body runs through
`render_markdown` so inline code, emphasis, and fenced blocks keep
their styling. A selective patch dims plain-text spans (prose reads
muted) but leaves spans that already carry an explicit fg — inline
code, links, headings, syntax-highlighted fences — at full color.
@hakula139 hakula139 added the enhancement New feature or request label Apr 24, 2026
@hakula139 hakula139 self-assigned this Apr 24, 2026
@hakula139 hakula139 added the enhancement New feature or request label Apr 24, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…istantThinking

Codecov flagged one uncovered branch in  — the
fenced-code early-return only fires when the line's whole-line style
carries an fg, which none of the existing thinking-block tests
exercised. Add a focused test module that pins:

- Empty / whitespace-only body renders the header alone (guard skips
  the markdown pass — relevant during the zero-delta frame between a
  thinking block opening and its first chunk).
- Fenced code body keeps its highlight fg and still picks up the bar
  prefix — a regression patching thinking() onto highlighted lines
  would swap code colors for dim gray.
- Plain spans get dimmed while explicitly-colored spans (inline code)
  keep their fg — the split that lets reasoning read as muted while
  code stays legible.
`│` (U+2502) sits centered in its column while `▎` (U+258E — the
glyph tool blocks use) sits flush against the column's left edge.
Even though both are one column wide, the visible vertical line
lands at different horizontal positions, so the thinking block's
left border was offset from adjacent tool call / result bars.

Promote the `BAR` constant to the shared `blocks` module and point
`THINKING_PREFIX` at the same glyph. Tool blocks now import `BAR`
from `super` instead of defining their own copy, and a runtime test
pins that `THINKING_PREFIX` starts with `BAR` so a future glyph
change can't silently desync the two.
Comments added earlier in the PR narrated intent at length and
explained alternatives; bring them in line with CLAUDE.md — why, not
what; one line where possible; no "picked X over Y" rationale.
`content.trim()` ran across the whole string before splitting into
lines, stripping leading whitespace off the first line only. Tools
like `git diff --stat` that indent every line with a meaningful
space rendered with a misaligned first row.

Iterate `content.lines()` directly and drop whole blank surrounding
lines instead, keeping per-line indent intact. Regression test pins
the `git diff --stat` shape.
The helper rebuilt each line from spans only, silently dropping
`Line.style` on the way through. Fenced code blocks set their fg on
the whole-line style (not the inner spans), so assistant text with
an unknown-lang fence lost its color once wrapped in the icon
prefix. Preserve the input's whole-line style on the output.

Also collapses `AssistantThinking::render`'s body loop to reuse the
helper (same shape, less hand-rolled), renames `inner_width` →
`md_width` to match `render_assistant_markdown`, moves the
`thinking_prefix_shares_bar_glyph_with_tool_blocks` invariant test
under its own `// ── THINKING_PREFIX ──` section, and drops the
stale "used by thinking body" mention from the helper's doc plus
the redundant one-line doc on `commit_thinking_buffer`.

Adds a regression test pinning fenced-code fg preservation for
assistant text — same scenario that the thinking block already
covers post-review.
Codecov flagged the two `while` loops in `render_text_body` that
drop leading / trailing blank lines — none of the existing tests
feed content that survives the `trim().is_empty()` guard with
blanks on either end. Pin the stripping so a regression that
re-adds the old `content.trim()` behavior (swallowing per-line
indent) can't land unnoticed.
@hakula139 hakula139 merged commit 94a9948 into main Apr 24, 2026
4 checks passed
@hakula139 hakula139 deleted the feat/tui-thinking-block branch April 24, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant