✨ feat(markdown): implement streaming animation for markdown blocks#475
✨ feat(markdown): implement streaming animation for markdown blocks#475
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Reviewer's GuideImplements a streaming character-by-character reveal animation for markdown blocks by introducing a queue-based block state machine, a configurable rehype plugin for per-character spans with staggered animations, and wiring it into StreamdownRender with new styling, while removing the old global animated flag and simplifying block rendering. Sequence diagram for streaming markdown rendering and animationsequenceDiagram
actor User
participant StreamdownRender
participant useStreamQueue
participant rehypeStreamAnimated
participant DOM
User->>StreamdownRender: provide_markdown_children
StreamdownRender->>StreamdownRender: useMarkdownContent
StreamdownRender->>StreamdownRender: remend_content
StreamdownRender->>StreamdownRender: marked_lexer_to_BlockInfo
StreamdownRender->>useStreamQueue: useStreamQueue(blocks)
useStreamQueue-->>StreamdownRender: getBlockState charDelay queueLength
loop for_each_block
StreamdownRender->>useStreamQueue: getBlockState(index)
useStreamQueue-->>StreamdownRender: BlockState
alt state_is_streaming
StreamdownRender->>rehypeStreamAnimated: configure(baseCharCount charDelay)
else state_is_animating
StreamdownRender->>rehypeStreamAnimated: configure(baseCharCount0 charDelay)
else state_is_revealed
StreamdownRender->>rehypeStreamAnimated: not_used
end
StreamdownRender->>DOM: render_StreamdownBlock_with_plugins
end
rehypeStreamAnimated->>DOM: wrap_chars_in_span_stream_char
DOM-->>User: sees_character_by_character_reveal
Class diagram for streaming markdown rendering componentsclassDiagram
direction LR
class StreamdownRender {
+children string
+render()
+useMarkdownContent(children)
+useMarkdownComponents()
+useMarkdownRehypePlugins()
+useMarkdownRemarkPlugins()
+useStreamQueue(blocks)
}
class StreamdownBlock {
+children string
+render()
}
class BlockInfo {
+content string
+startOffset number
}
class BlockState {
<<enumeration>>
revealed
animating
streaming
queued
}
class UseStreamQueueReturn {
+charDelay number
+queueLength number
+getBlockState(index)
}
class useStreamQueue {
+useStreamQueue(blocks)
-revealedCount number
-minRevealedRef number
-prevBlocksLenRef number
-timerRef Timeout
+getBlockState(index)
}
class StreamAnimatedOptions {
+baseCharCount number
+charDelay number
}
class rehypeStreamAnimated {
+rehypeStreamAnimated(options)
-hasClass(node, cls)
-wrapText(node)
-shouldSkip(node)
}
class styles_animated {
<<style>>
+stream_char opacity0
+stream_char animationFadeIn
}
StreamdownRender --> StreamdownBlock : renders
StreamdownRender --> BlockInfo : computes_blocks
StreamdownRender --> useStreamQueue : uses
StreamdownRender --> rehypeStreamAnimated : configures_plugins
StreamdownRender --> UseStreamQueueReturn : receives_state
useStreamQueue --> BlockInfo : manages_queue_for
useStreamQueue --> BlockState : returns
rehypeStreamAnimated --> StreamAnimatedOptions : configured_by
styles_animated --> rehypeStreamAnimated : targets_stream_char
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
👍 @Innei |
commit: |
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- The
countCharshelper is duplicated in bothStreamdownRenderanduseStreamQueue; consider extracting this into a shared utility to keep the character-counting logic consistent and easier to maintain. - There are now two sources of character delay (
STREAM_CHAR_DELAYinStreamdownRenderand the computedcharDelayfromuseStreamQueueused instaggerPlugins); it would be clearer to unify these so the streaming and animating states derive delay from a single, well-defined configuration. - In
rehypeStreamAnimated, you are building inlinestylestrings foranimation-delay; if more style properties are added later this string-based approach can get brittle—consider using a structuredstyleobject (or merging with existing styles) to avoid accidentally overwriting other inline styles.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `countChars` helper is duplicated in both `StreamdownRender` and `useStreamQueue`; consider extracting this into a shared utility to keep the character-counting logic consistent and easier to maintain.
- There are now two sources of character delay (`STREAM_CHAR_DELAY` in `StreamdownRender` and the computed `charDelay` from `useStreamQueue` used in `staggerPlugins`); it would be clearer to unify these so the streaming and animating states derive delay from a single, well-defined configuration.
- In `rehypeStreamAnimated`, you are building inline `style` strings for `animation-delay`; if more style properties are added later this string-based approach can get brittle—consider using a structured `style` object (or merging with existing styles) to avoid accidentally overwriting other inline styles.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- Introduced `useStreamQueue` hook to manage streaming state and delays for markdown blocks. - Enhanced `StreamdownRender` to utilize streaming animations with staggered effects. - Updated styles for animated streaming characters. - Added `rehypeStreamAnimated` plugin to handle character-level animations in markdown rendering. This update improves the user experience by providing a more dynamic and engaging way to display markdown content. Signed-off-by: Innei <tukon479@gmail.com>
…components - Refactored the `StreamdownBlock` and `StreamdownRender` components for improved readability and maintainability. - Removed unnecessary memoization in `StreamdownBlock`. - Streamlined the use of `useEffect` and `useMemo` hooks in `StreamdownRender` to enhance performance. - Ensured consistent handling of previous character counts and stream offsets. This refactor lays the groundwork for future enhancements in markdown rendering. Signed-off-by: Innei <tukon479@gmail.com>
- Introduced `revealed` option in `rehypeStreamAnimated` plugin to control character visibility during streaming. - Updated `StreamdownRender` to utilize stable plugin references for improved performance. - Added utility functions for deep comparison of plugins to prevent unnecessary re-renders. - Enhanced styles for revealed characters to ensure smooth transitions. This update improves the markdown rendering experience by providing more control over streaming animations and optimizing component performance. Signed-off-by: Innei <tukon479@gmail.com>
|
❤️ Great PR @Innei ❤️ |
# [5.2.0](v5.1.1...v5.2.0) (2026-03-03) ### ✨ Features * **markdown**: Implement streaming animation for markdown blocks, closes [#475](#475) ([2b3d230](2b3d230))
|
🎉 This PR is included in version 5.2.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
Commits
86812c94✨ feat(markdown): implement streaming animation for markdown blocksd6ed6e0e♻️ refactor(markdown): simplify StreamdownBlock and StreamdownRender componentsTest plan
Summary by Sourcery
Add per-block streaming animation for markdown rendering with dynamic timing and simplified components.
New Features:
Enhancements: