fix(tui): render thinking preview markdown and stabilize activity rows#200
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThinking and subagent activity rendering now uses static muted activity details and fence-aware Markdown previews. Complete top-level HTML comments are suppressed, fenced and incomplete content is preserved, and unchanged previews reuse cached rendering. ChangesTerminal rendering
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant ThinkingStream
participant PreviewRenderer
participant RichLayout
ThinkingStream->>PreviewRenderer: pass bounded thinking preview
PreviewRenderer->>PreviewRenderer: process fence-aware lines and suppress complete top-level comments
PreviewRenderer->>RichLayout: return cached or rendered preview
RichLayout->>ThinkingStream: render preview layout or spinner-only output
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/pythinker_code/ui/shell/visualize/_blocks.py (1)
1148-1166: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winUncached markdown re-render on every Live tick for the legacy thinking preview.
_render_thinking_preview(regex substitution +render_agent_bodymarkdown parse) now runs unconditionally inside_compose_thinking_stream, which is invoked on everycompose()call. Unlike_compose_composing, which caches preview text via_build_preview_cached, this path recomputes the markdown render even whenpendinghasn't changed between refresh ticks (e.g. driven by the spinner's own animation cadence). For long-running thinking streams with frequent Live refreshes, this adds avoidable Markdown-parsing work per frame.Consider caching on
pending/previewsimilar to the_preview_text_cache_keypattern already used for the composing path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/pythinker_code/ui/shell/visualize/_blocks.py` around lines 1148 - 1166, The _compose_thinking_stream method re-renders the unchanged thinking preview on every Live refresh. Add pending/preview-based caching, following the existing _preview_text_cache_key pattern used by _compose_composing, so _render_thinking_preview is only called when the preview content changes while preserving the current spinner and rendering behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/pythinker_code/ui/shell/visualize/_blocks.py`:
- Around line 1148-1166: The _compose_thinking_stream method re-renders the
unchanged thinking preview on every Live refresh. Add pending/preview-based
caching, following the existing _preview_text_cache_key pattern used by
_compose_composing, so _render_thinking_preview is only called when the preview
content changes while preserving the current spinner and rendering behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 01752e5b-b0d9-4fe5-b501-32ef588c2eca
⛔ Files ignored due to path filters (3)
docs/en/release-notes/changelog.mdis excluded by!docs/**docs/superpowers/plans/2026-07-11-tui-thinking-markdown-and-activity-motion.mdis excluded by!docs/**docs/superpowers/specs/2026-07-11-tui-thinking-markdown-and-activity-motion-design.mdis excluded by!docs/**
📒 Files selected for processing (5)
CHANGELOG.mdsrc/pythinker_code/ui/shell/visualize/_activity_tree.pysrc/pythinker_code/ui/shell/visualize/_blocks.pytests/ui_and_conv/test_activity_tree.pytests/ui_and_conv/test_streaming_content_block.py
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
The markdown render + regex parse for the thinking preview can run many times per second on Live refresh ticks when no new content has arrived. Introduce a cache key (the preview string) so markdown parsing only runs when the pending thinking text actually changes, not on every spinner animation. Includes tests verifying cache behavior and output equivalence.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/ui_and_conv/test_streaming_content_block.py`:
- Around line 700-713: Make
test_stream_mode_cached_preview_matches_uncached_render deterministic by
freezing or mocking the elapsed/status clock used by _ContentBlock.compose
before both compose calls, so timing data remains identical while still
comparing full exported output; preserve the existing content and cache-key
assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2a17c042-5ec8-41a0-a78f-b9ef751a8f45
📒 Files selected for processing (2)
src/pythinker_code/ui/shell/visualize/_blocks.pytests/ui_and_conv/test_streaming_content_block.py
The output-equivalence test compared full Console.export_text() across two compose() calls, whose status line embeds elapsed time and token rate. Freeze time.monotonic so the two renders are deterministic and only the cached preview governs the comparison; guards against a rare timing-boundary flake.
Summary
Two narrow TUI rendering fixes in the interactive shell, kept surgical (2 source files):
_ContentBlock._compose_thinking_streambuilt aplain
Text(preview, …), so the live "Thinking…" preview leaked Markdown emphasis markers(
**bold**) and raw HTML comments. It now renders through the existingrender_agent_body, witha fence-aware, line-anchored filter that removes only lines that are entirely a top-level
HTML comment.
render_activity_treecalledshimmer_texton eachrunningdetail, making one tree row look specially animated. Every detail now uses the stablemuted style; the lifecycle-marker running pulse (
blink_visible) is unchanged, and the bottomverb spinner (
activity_status_line) remains the only coral shimmer.Behavior (all covered by tests)
<!-- … -->comment is hidden.<!-- …with no close) are preserved as readable text; nothingraises out of the Live loop.
<!-- … -->inside a fenced code block renders verbatim.only removes whole-line comments and never deletes visible prose.
including reduced-motion and no-color fallbacks.
Notable edge case
A line of two adjacent complete comments with zero prose between them (
<!-- a --><!-- b -->) isleft intact rather than stripped. This is a deliberate safe-direction narrowing — under-stripping a
marker beats deleting visible prose — and is pinned by a regression test. It replaced an earlier
non-greedy regex (
.*?) that backtracked across an embedded closer and silently deleted the middleof a mixed line; the current tempered token
(?:(?!-->).)*?cannot cross an embedded-->.Verification
make check-pythinker-code: ruffAll checks passed!, pyright0 errors, 0 warnings, 0 informations, ty clean.test; the two changed modules pass 128/128). On the source branch the full package run was
6927 passed, 9 skipped, 1 xfailed(unit) and65 passed, 4 skipped(e2e).git diff --check: clean.Scope
CHANGELOG.md(+ synceddocs/en/release-notes/changelog.md) carry the Unreleased entry; the designspec and implementation plan are included under
docs/superpowers/. No new dependency, telemetry,endpoint, config key, or persistence migration. Auth, approvals, persistence, providers, tool
execution, scheduling, wire events,
shimmer_text, and palette tokens are untouched.Summary by CodeRabbit