Skip to content

fix(tui): render thinking preview markdown and stabilize activity rows#200

Merged
elkaix merged 6 commits into
mainfrom
feat/tui-thinking-markdown-activity-motion
Jul 11, 2026
Merged

fix(tui): render thinking preview markdown and stabilize activity rows#200
elkaix merged 6 commits into
mainfrom
feat/tui-thinking-markdown-activity-motion

Conversation

@elkaix

@elkaix elkaix commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Two narrow TUI rendering fixes in the interactive shell, kept surgical (2 source files):

  1. Thinking preview renders clean Markdown. _ContentBlock._compose_thinking_stream built a
    plain Text(preview, …), so the live "Thinking…" preview leaked Markdown emphasis markers
    (**bold**) and raw HTML comments. It now renders through the existing render_agent_body, with
    a fence-aware, line-anchored filter that removes only lines that are entirely a top-level
    HTML comment.
  2. Activity rows stay visually stable. render_activity_tree called shimmer_text on each
    running detail, making one tree row look specially animated. Every detail now uses the stable
    muted style; the lifecycle-marker running pulse (blink_visible) is unchanged, and the bottom
    verb spinner (activity_status_line) remains the only coral shimmer.

Behavior (all covered by tests)

  • Complete Markdown emphasis renders without delimiters.
  • A line that is entirely a top-level <!-- … --> comment is hidden.
  • Malformed/incomplete comments (<!-- … with no close) are preserved as readable text; nothing
    raises out of the Live loop.
  • A <!-- … --> inside a fenced code block renders verbatim.
  • A line mixing prose with inline comments is left fully intact — the filter is line-anchored, so it
    only removes whole-line comments and never deletes visible prose.
  • Running activity-tree details render static muted; the verb spinner keeps its coral shimmer,
    including reduced-motion and no-color fallbacks.

Notable edge case

A line of two adjacent complete comments with zero prose between them (<!-- a --><!-- b -->) is
left 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 middle
of a mixed line; the current tempered token (?:(?!-->).)*? cannot cross an embedded -->.

Verification

  • Static gate make check-pythinker-code: ruff All checks passed!, pyright 0 errors, 0 warnings, 0 informations, ty clean.
  • Focused TUI suites for the changed modules: all passing (RED→GREEN confirmed for every added
    test; the two changed modules pass 128/128). On the source branch the full package run was
    6927 passed, 9 skipped, 1 xfailed (unit) and 65 passed, 4 skipped (e2e).
  • git diff --check: clean.

Scope

CHANGELOG.md (+ synced docs/en/release-notes/changelog.md) carry the Unreleased entry; the design
spec 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

  • New Features
    • Improved terminal rendering for thinking and subagent activity with clean, complete Markdown previews.
    • Thinking previews now hide top-level HTML comments while preserving inline comments, incomplete markup, and fenced code blocks.
    • Activity-tree visuals are more stable, with the coral shimmer reserved for the active verb spinner.
  • Bug Fixes
    • Fixed “running” activity row details to remain static and consistently muted (no animated/shimmering changes across renders).

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56de1289-b5b5-4658-a197-0297f7bd8fe1

📥 Commits

Reviewing files that changed from the base of the PR and between 4baefb7 and 2ee6e98.

📒 Files selected for processing (1)
  • tests/ui_and_conv/test_streaming_content_block.py

📝 Walkthrough

Walkthrough

Thinking 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.

Changes

Terminal rendering

Layer / File(s) Summary
Static activity row details
src/pythinker_code/ui/shell/visualize/_activity_tree.py, tests/ui_and_conv/test_activity_tree.py, CHANGELOG.md
Running activity details now render with a stable muted style, with regression coverage for consistent colors across render times and an Unreleased changelog entry.
Fence-aware thinking previews
src/pythinker_code/ui/shell/visualize/_blocks.py, tests/ui_and_conv/test_streaming_content_block.py
Legacy thinking previews suppress complete top-level HTML comments, preserve fenced and incomplete content, render Markdown, use spinner-only output when empty, and cache unchanged preview results.

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
Loading

Suggested labels: bug

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it omits the required Related Issue section and the checklist from the template. Add a Related Issue entry and include the checklist items from the template, especially the issue link and changelog/docs confirmation.
Docstring Coverage ⚠️ Warning Docstring coverage is 39.13% which is insufficient. The required threshold is 70.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title follows conventional commits and matches the PR's TUI rendering fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tui-thinking-markdown-activity-motion

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Uncached markdown re-render on every Live tick for the legacy thinking preview.

_render_thinking_preview (regex substitution + render_agent_body markdown parse) now runs unconditionally inside _compose_thinking_stream, which is invoked on every compose() call. Unlike _compose_composing, which caches preview text via _build_preview_cached, this path recomputes the markdown render even when pending hasn'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/preview similar to the _preview_text_cache_key pattern 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

📥 Commits

Reviewing files that changed from the base of the PR and between 595aa49 and 81522bc.

⛔ Files ignored due to path filters (3)
  • docs/en/release-notes/changelog.md is excluded by !docs/**
  • docs/superpowers/plans/2026-07-11-tui-thinking-markdown-and-activity-motion.md is excluded by !docs/**
  • docs/superpowers/specs/2026-07-11-tui-thinking-markdown-and-activity-motion-design.md is excluded by !docs/**
📒 Files selected for processing (5)
  • CHANGELOG.md
  • src/pythinker_code/ui/shell/visualize/_activity_tree.py
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • tests/ui_and_conv/test_activity_tree.py
  • tests/ui_and_conv/test_streaming_content_block.py

@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.18919% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pythinker_code/ui/shell/visualize/_blocks.py 88.57% 2 Missing and 2 partials ⚠️

📢 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 81522bc and 4baefb7.

📒 Files selected for processing (2)
  • src/pythinker_code/ui/shell/visualize/_blocks.py
  • tests/ui_and_conv/test_streaming_content_block.py

Comment thread tests/ui_and_conv/test_streaming_content_block.py Outdated
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.
@elkaix
elkaix merged commit b26897f into main Jul 11, 2026
38 checks passed
@elkaix
elkaix deleted the feat/tui-thinking-markdown-activity-motion branch July 17, 2026 20:04
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.

1 participant