Skip to content

fix: smooth streaming markdown rendering#1187

Open
jamesx0416 wants to merge 1 commit intopingdotgg:mainfrom
jamesx0416:t3code/fix-streaming-markdown-rendering
Open

fix: smooth streaming markdown rendering#1187
jamesx0416 wants to merge 1 commit intopingdotgg:mainfrom
jamesx0416:t3code/fix-streaming-markdown-rendering

Conversation

@jamesx0416
Copy link
Contributor

@jamesx0416 jamesx0416 commented Mar 18, 2026

What Changed

  • Added a small streaming markdown repair helper for assistant responses in apps/web/src/streaming-markdown.ts
  • Applied that helper only while a response is still streaming in apps/web/src/components/ChatMarkdown.tsx
  • Added test coverage for incomplete emphasis, inline code, fenced code blocks, markdown links, autolinks, and strikethrough

Why

Streaming assistant output is often syntactically incomplete mid-token, which causes react-markdown to show raw markdown markers like **, backticks, [](), ~~, or <...> until the response finishes. This change temporarily appends closing delimiters for a narrow set of supported constructs during streaming so partial responses render more cleanly without changing the final stored message.

The helper is intentionally scoped as a streaming UX repair layer rather than a full markdown parser.

UI Changes

This only affects in-flight assistant message rendering. There are no durable layout changes, and I did not capture before/after screenshots or video for this interaction polish.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Fix streaming markdown rendering by closing incomplete markdown constructs

  • Adds finalizeStreamingMarkdown in streaming-markdown.ts that scans in-progress text and appends closing delimiters for unclosed fenced code blocks, inline code spans, link destinations, autolinks, and emphasis markers.
  • ChatMarkdown applies finalizeStreamingMarkdown via useMemo when isStreaming is true, so React Markdown always receives syntactically balanced input.
  • Test suite in streaming-markdown.test.ts covers bold, inline code, fenced blocks, nested emphasis, nested parentheses in links, strikethrough, autolinks, and intraword underscores.
📊 Macroscope summarized 4ce9181. 3 files reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted

🗂️ Filtered Issues

@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7a3de469-a24e-42df-b651-cd538da2327c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 18, 2026
Comment on lines +206 to +207
const openingMatch = line.match(FENCE_OPEN_PATTERN);
if (openingMatch) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Medium src/streaming-markdown.ts:206

When an unclosed inline code span is followed by a fence-like pattern on the next line (e.g., `some code\n```python), the function incorrectly enters fence mode and resets inlineState, losing track of the unclosed inline code delimiter. The original backtick remains unclosed in the output, causing incorrect markdown rendering. Consider checking inlineState.inlineCodeDelimiter == null before processing fence open patterns.

     const openingMatch = line.match(FENCE_OPEN_PATTERN);
-    if (openingMatch) {
+    if (openingMatch && inlineState.inlineCodeDelimiter == null) {
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file apps/web/src/streaming-markdown.ts around lines 206-207:

When an unclosed inline code span is followed by a fence-like pattern on the next line (e.g., `` `some code\n[code fence]python ``), the function incorrectly enters fence mode and resets `inlineState`, losing track of the unclosed inline code delimiter. The original backtick remains unclosed in the output, causing incorrect markdown rendering. Consider checking `inlineState.inlineCodeDelimiter == null` before processing fence open patterns.

Evidence trail:
apps/web/src/streaming-markdown.ts lines 205-217 (REVIEWED_COMMIT): Fence open pattern handling that resets `inlineState` without checking if there's an unclosed inline code delimiter. Lines 229-231: Suffix generation that relies on `inlineState.inlineCodeDelimiter` to add closing backticks - but this value is null after the reset.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@macroscopeapp fix it for me

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fix it for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant