Skip to content

fix(platform): keep cursor visible during typewriter buffer drain#551

Merged
Israeltheminer merged 2 commits into
mainfrom
fix/550-streaming-cursor-mid-block
Feb 24, 2026
Merged

fix(platform): keep cursor visible during typewriter buffer drain#551
Israeltheminer merged 2 commits into
mainfrom
fix/550-streaming-cursor-mid-block

Conversation

@Israeltheminer
Copy link
Copy Markdown
Collaborator

@Israeltheminer Israeltheminer commented Feb 24, 2026

Closes #550

Summary

  • showCursor in typewriter-text.tsx was tied to isStreaming, which flips to false the moment the server marks the message complete — even though the buffer still has seconds of text left to drain at 50 CPS. Fixed by tying showCursor to isTyping from useStreamBuffer instead, so the cursor stays visible until the last character is revealed.
  • Added isCurrentlyTyping check to IncrementalMarkdown's isLastElement logic to handle the mid-block case where endOffset > revealedLen (parser hasn't closed the element yet), which previously caused no cursor to be placed.
  • Added a "cursor during partial reveal (mid-stream)" test suite covering mid-paragraph, post-anchor, mid-list, mid-heading, and multi-reveal cases — all previously untested.

Test plan

  • Send a long prompt (e.g. "Write a 500-word essay on the Roman Empire") and confirm the cursor stays visible for the entire duration of the typewriter animation, including after the server finishes generating
  • Confirm cursor disappears cleanly once the last character is revealed
  • Run npm run test:ui --workspace=@tale/platform — all 16 tests pass

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved cursor visibility and placement during mid-block streaming reveals.
    • Fixed cursor visibility timing to persist until the final character is displayed.
  • Tests

    • Added comprehensive test coverage for mid-stream cursor rendering scenarios.

The cursor was tied to isStreaming, which flips to false the moment
the server marks the message as complete — even though the typewriter
buffer still has seconds of text left to reveal at 50 CPS.

Fix: tie showCursor to isTyping from useStreamBuffer so the cursor
stays visible until the last character is revealed.

Also add isCurrentlyTyping check to IncrementalMarkdown's isLastElement
logic to handle mid-block reveal (endOffset > revealedLen), and add
tests for the partial-reveal cursor case that was previously untested.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Feb 24, 2026

Greptile Summary

Fixed cursor visibility during typewriter buffer drain by switching from isStreaming to isTyping state. The cursor now stays visible until the last character is revealed, even after the server marks the message complete.

  • Changed showCursor in typewriter-text.tsx to use isTyping from useStreamBuffer hook, which tracks the actual animation state rather than server streaming state
  • Added isCurrentlyTyping logic in incremental-markdown.tsx to correctly identify when the reveal position is mid-block (startOffset < revealedLen && endOffset > revealedLen), fixing cursor placement before the parser closes the element
  • Added comprehensive test coverage for mid-stream cursor scenarios (mid-paragraph, post-anchor, mid-list, mid-heading, and multi-reveal cases)

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix correctly addresses the root cause of the cursor disappearing prematurely. The logic change is straightforward: using isTyping (which tracks buffer drain state) instead of isStreaming (which tracks server state) ensures cursor visibility matches the actual animation. The isCurrentlyTyping addition properly handles the mid-block edge case with sound logic. Test coverage is comprehensive and validates all key scenarios.
  • No files require special attention

Important Files Changed

Filename Overview
services/platform/app/features/chat/components/typewriter-text.tsx Changed showCursor to use isTyping from useStreamBuffer instead of isStreaming, ensuring cursor stays visible during buffer drain
services/platform/app/features/chat/components/incremental-markdown.tsx Added isCurrentlyTyping check to handle mid-block cursor placement when parser hasn't closed the element yet
services/platform/app/features/chat/components/tests/incremental-markdown.test.tsx Added comprehensive test suite for cursor behavior during partial reveal (mid-paragraph, post-anchor, mid-list, mid-heading scenarios)

Last reviewed commit: 1c89fea

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

This PR addresses cursor visibility issues during the buffer-drain phase of streaming responses. It modifies IncrementalMarkdown to detect when text is being actively typed mid-block by considering both start and end offsets of node positions, introducing startOffset in the cursor wrapper and an isCurrentlyTyping condition. It also updates TypewriterText to tie cursor visibility to the isTyping flag from the stream buffer rather than the message streaming status. Test coverage for mid-stream cursor scenarios is added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: keeping the cursor visible during typewriter buffer drain, which is the primary objective of the PR.
Linked Issues check ✅ Passed All code changes directly address the requirements from issue #550: cursor visibility now depends on isTyping instead of isStreaming, isCurrentlyTyping logic is added to IncrementalMarkdown, and test coverage for mid-stream partial reveals is provided.
Out of Scope Changes check ✅ Passed All changes in the PR are directly scoped to fixing the streaming cursor bug: test coverage expansion, cursor visibility logic update, and mid-block detection support. No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/550-streaming-cursor-mid-block

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@services/platform/app/features/chat/components/__tests__/incremental-markdown.test.tsx`:
- Around line 153-247: Tests never exercise the new isCurrentlyTyping branch
because StreamingMarkdown slices content before parsing (revealedContent =
content.slice(0, revealedLength)), so parser AST node endOffset can never be >
revealedLen; to fix add a targeted test that triggers isCurrentlyTyping either
by (A) testing the lower-level helper that checks isCurrentlyTyping/endOffset
directly (call the function that uses endOffset and revealedLen) or (B) mocking
the markdown parser used by StreamingMarkdown to return an AST node with
endOffset > revealedLen (so isCurrentlyTyping becomes true), referencing
StreamingMarkdown, revealedContent/revealedLen, endOffset and isCurrentlyTyping
to locate the code under test.

In `@services/platform/app/features/chat/components/incremental-markdown.tsx`:
- Around line 221-241: Remove the unreachable mid-block guard and its use:
delete the isCurrentlyTyping computation (the const isCurrentlyTyping = ...
block) and update the isLastElement expression in incremental-markdown.tsx to no
longer reference isCurrentlyTyping, relying instead on the existing endOffset
=== revealedLen and the trim-based check; ensure revealedLenRef/current and
hasCursorEligibleChild logic remain unchanged and run the existing cursor tests
to confirm behavior.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1afa693 and 1c89fea.

📒 Files selected for processing (3)
  • services/platform/app/features/chat/components/__tests__/incremental-markdown.test.tsx
  • services/platform/app/features/chat/components/incremental-markdown.tsx
  • services/platform/app/features/chat/components/typewriter-text.tsx

- Hide ThinkingAnimation when text is streaming with no active tools;
  typewriter text already signals progress, showing both is redundant
- Expose isDraining from useStreamBuffer so cursor stays visible during
  the post-stream drain phase, not just while the server is sending
- Skip cursor injection into empty elements (e.g. trailing <p> from \n)
  to prevent a stray cursor appearing on a blank line after content
- Update thinking-animation test to assert the new null-return behavior
@Israeltheminer Israeltheminer merged commit 664f342 into main Feb 24, 2026
17 checks passed
@Israeltheminer Israeltheminer deleted the fix/550-streaming-cursor-mid-block branch February 24, 2026 12:13
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.

fix(platform): streaming cursor disappears mid-response after anchor advances

1 participant