Skip to content

fix(chat): keep StreamingMarkdown render path after stream finishes [v0.5.3] - #10

Merged
A-PachecoT merged 1 commit into
mainfrom
fix/streaming-finished-h2-regression
May 21, 2026
Merged

A-PachecoT merged 1 commit into
mainfrom
fix/streaming-finished-h2-regression

Conversation

@A-PachecoT

Copy link
Copy Markdown
Member

Summary

  • Fixes end-of-stream heading shrink regression: h2 "Estimación rápida" (and any other heading) collapsed from 1.35em to browser default ~21px the moment the stream finished.
  • Root cause: Message.tsx swapped from <StreamingMarkdown> (inline BLOCK_STYLES with fontSize: 1.35em on h2) to <MessageContent> (which relies on Tailwind prose class). @tailwindcss/typography is not installed in consuming apps (TimelyAI / Landing / InboxAI) — so prose is a no-op and headings fall back to the browser default.
  • Fix: render <StreamingMarkdown> for all assistant messages and toggle the isStreaming prop based on isStreamingThis. User messages still use <MessageContent> (user input rarely has markdown).

Before / after

Before: during stream → <StreamingMarkdown> with cf-stream-root cf-stream-active, h2 inline-styled 1.35em. Stream finishes → component swaps to <MessageContent> which renders <h2>Estimación rápida</h2> with only the prose class. prose resolves to nothing (plugin not installed) → h2 reverts to UA default (~21px / 1.5em bold) → visible shrink.

After: during stream → same. Stream finishes → same component stays mounted; only .cf-stream-active class drops. h2 keeps its inline fontSize: 1.35em, char spans freeze in place. Zero layout shift, zero size change.

- {isStreamingThis ? (
-   <StreamingMarkdown content={message.content} isStreaming isUser={isUser} />
- ) : (
-   <MessageContent content={message.content} format=\"markdown\" isUser={isUser} />
- )}
+ {message.role === \"assistant\" ? (
+   <StreamingMarkdown content={message.content} isStreaming={isStreamingThis} isUser={isUser} />
+ ) : (
+   <MessageContent content={message.content} format=\"markdown\" isUser={isUser} />
+ )}

Why not install @tailwindcss/typography?

That's a bigger change touching every consumer's tailwind config and potentially clashing with brand styles. The StreamingMarkdown inline BLOCK_STYLES are already the canonical rendering for streamed assistant output — we just keep using them post-stream too. Cheap, surgical, behavior-preserving.

Files

  • src/components/chat-widget/Message.tsx — single-branch swap
  • src/__tests__/components/Message.streaming.test.tsx — 4 tests rewritten around the new contract (StreamingMarkdown stays mounted; only cf-stream-active toggles)
  • package.json — 0.5.2 → 0.5.3
  • CHANGELOG.md — [0.5.3] entry

Test plan

  • npx tsc --noEmit — clean (no new TS errors)
  • npx vitest run src/__tests__/components/Message.streaming.test.tsx src/__tests__/components/StreamingMarkdown.debug.test.tsx — 8/8 pass
  • After Cloudflare auto-deploy of Storybook (ui.cofoundy.dev), curl https://ui.cofoundy.dev/iframe.html?id=chat-message--streaming-finished&viewMode=story and grep for font-size:1.35em in the h2 attribute — should be present (was absent before this fix)
  • Visual check in Chromatic: Chat/Message → Streaming Finished should look identical to Streaming Chunky mid-stream w.r.t. heading size

Out of scope

  • StreamingMarkdown.tsx, useStreamingDrip.ts, streaming CSS — unchanged (already correct)
  • @tailwindcss/typography install — deliberately deferred

… preserves h2 styling [v0.5.3]

Message.tsx previously swapped from <StreamingMarkdown> to <MessageContent>
at end-of-stream. MessageContent relies on Tailwind's `prose` class, but
@tailwindcss/typography isn't installed in consuming apps — so h2 collapsed
from 1.35em (StreamingMarkdown inline BLOCK_STYLES) to browser default ~21px,
producing a visible heading-shrink at end-of-stream (e.g. "Estimación rápida").

Fix: render <StreamingMarkdown> for ALL assistant messages and toggle the
`isStreaming` prop based on isStreamingThis. User messages keep <MessageContent>
(user input rarely has markdown). The .cf-stream-root wrapper stays mounted;
only .cf-stream-active drops at completion — chars freeze in place, zero
layout shift, no h2 regression.

Tests updated:
- "renders MessageContent for user messages" (was: not streaming)
- "renders StreamingMarkdown with cf-stream-active class when isStreamingThis"
- "renders StreamingMarkdown WITHOUT cf-stream-active when streaming a different message"
- "keeps StreamingMarkdown rendered when stream finishes — only drops cf-stream-active"

8/8 tests pass. tsc --noEmit clean.
@A-PachecoT
A-PachecoT merged commit ab8d4ef into main May 21, 2026
1 of 2 checks passed
@A-PachecoT
A-PachecoT deleted the fix/streaming-finished-h2-regression branch May 21, 2026 01:27
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