Skip to content

Commit cba0e69

Browse files
committed
fix(mothership): keep isAnimating latched so completed messages don't flash
The streamed-text reveal latches `mode` and `animated` via `keepStreamingTree` to avoid the streaming→static handoff flash, but `isAnimating` was still wired to `isRevealing`, which flips false the instant the reveal catches up. Streamdown treats `isAnimating: false` as "streaming over" and rebuilds the whole message without the per-word animation spans — that DOM rebuild is a visible flash when a message finishes. Wire `isAnimating` to the same `keepStreamingTree` latch so all three Streamdown props stay constant across completion. Content is stable once revealed, so a permanently-true `isAnimating` has no new tokens to fade and never re-animates.
1 parent eb1009d commit cba0e69

1 file changed

Lines changed: 13 additions & 2 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/chat-content/chat-content.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ function ChatContentInner({
294294
const streamedContent = useSmoothText(displayContent, isStreaming)
295295
const isRevealing = isStreaming || streamedContent.length < displayContent.length
296296

297+
/**
298+
* One-way latch: once a message has streamed in this mount, keep rendering it
299+
* through Streamdown's streaming/animation pipeline for the rest of its life.
300+
* Drives `mode`, `animated`, AND `isAnimating` together — all three must stay
301+
* constant across the completion boundary. Streamdown removes the per-word
302+
* `<span>` wrappers (and re-parses the whole message) the instant `isAnimating`
303+
* goes false, so wiring `isAnimating` to `isRevealing` (which flips at
304+
* completion) reintroduces the streaming→static flash this latch exists to
305+
* prevent. Content is stable once revealed, so a permanently-true
306+
* `isAnimating` never re-fades anything.
307+
*/
297308
const streamedThisSession = useRef(false)
298309
if (isStreaming) streamedThisSession.current = true
299310
const keepStreamingTree = isRevealing || streamedThisSession.current
@@ -372,7 +383,7 @@ function ChatContentInner({
372383
<Streamdown
373384
mode={keepStreamingTree ? undefined : 'static'}
374385
animated={keepStreamingTree ? STREAM_ANIMATION : false}
375-
isAnimating={isRevealing}
386+
isAnimating={keepStreamingTree}
376387
components={MARKDOWN_COMPONENTS}
377388
>
378389
{group.markdown}
@@ -398,7 +409,7 @@ function ChatContentInner({
398409
<Streamdown
399410
mode={keepStreamingTree ? undefined : 'static'}
400411
animated={keepStreamingTree ? STREAM_ANIMATION : false}
401-
isAnimating={isRevealing}
412+
isAnimating={keepStreamingTree}
402413
components={MARKDOWN_COMPONENTS}
403414
>
404415
{streamedContent}

0 commit comments

Comments
 (0)