Skip to content

feat: add animated spinner and streaming token output - #1

Merged
mrvgao merged 3 commits into
mainfrom
feat/streaming-output-and-animated-spinner
Apr 10, 2026
Merged

feat: add animated spinner and streaming token output#1
mrvgao merged 3 commits into
mainfrom
feat/streaming-output-and-animated-spinner

Conversation

@lyang36

@lyang36 lyang36 commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replace the static single-frame "Thinking..." text with a background-threaded AnimatedSpinner that cycles braille frames (~80ms interval), giving continuous visual feedback while the model processes
  • Stream TextDelta tokens directly to stdout as they arrive instead of buffering through MarkdownStreamState, making the response appear incrementally (like Claude's web UI)
  • Add 8ms TTY pacing between token writes so buffered HTTP chunks don't render as a wall of text
  • Make find_stream_safe_boundary flush after every complete line and add a 240-char fallback for long unwrapped lines

Test plan

  • Run physmind interactively and verify the spinner animates while waiting for the model
  • Verify the spinner stops cleanly when the first response token arrives (no visual glitch)
  • Confirm response text streams incrementally rather than appearing all at once
  • Test with piped output (physmind -p "hello" | cat) to confirm pacing is skipped for non-TTY
  • Verify cargo test passes

Made with Cursor

lyang36 added 3 commits April 10, 2026 04:59
Replace the static single-frame "Thinking..." spinner with a background-
threaded AnimatedSpinner that cycles braille frames at ~80ms, providing
visual feedback while the model processes.

Stream TextDelta tokens directly to stdout as they arrive instead of
buffering through MarkdownStreamState, so the response appears
incrementally. Add 8ms TTY pacing between tokens to prevent buffered
HTTP chunks from rendering as a wall of text. The spinner is
automatically stopped via a global AtomicBool flag when the first
text or thinking token arrives.

Also make find_stream_safe_boundary flush after every complete line
(instead of only on blank lines) and add a 240-char fallback for
long lines without newlines.

Made-with: Cursor
Apply rustfmt across the workspace to fix CI formatting check.
Re-enable MarkdownStreamState for TextDelta streaming so output
renders with proper ANSI formatting (bold, bullets, code blocks)
while still flushing per-line for incremental display.

Made-with: Cursor
The text content block was hardcoded to index 1 (reserving 0 for
reasoning), which collided with tool-call index 0+1 and broke
assertions when no reasoning content was present. Now text_index()
returns 0 when there is no reasoning block and 1 when there is,
and tool-call indices are offset from text_index()+1 accordingly.

Fixes openai_streaming_requests_opt_into_usage_chunks and
stream_message_normalizes_text_and_multiple_tool_calls tests.

Made-with: Cursor
@lyang36
lyang36 requested a review from mrvgao April 10, 2026 05:17
@mrvgao
mrvgao merged commit efe1fc7 into main Apr 10, 2026
4 checks passed
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.

2 participants