-
-
Notifications
You must be signed in to change notification settings - Fork 219
โจ feat: Update Markdown aslike Streamdown #388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reviewer's GuideThis PR refactors several memoized components with custom comparators for render optimization, overhauls the markdown rendering pipeline to split content into blocks with a skeleton fallback, introduces animated-content preprocessing via a new parseIncompleteMarkdown utility, refactors markdown component hooks from useMemo to useCallback for stability, and updates CodeBlock to a streamlined memoized version with simplified rendering logic and adds โmarkedโ as a dependency. Sequence diagram for Markdown block rendering with skeleton fallbacksequenceDiagram
participant SyntaxMarkdown
participant MarkdownRenderer
participant MarkdownHooks
participant Skeleton
SyntaxMarkdown->>MarkdownRenderer: For each block, render MarkdownRenderer
MarkdownRenderer->>MarkdownHooks: Render block content
MarkdownHooks-->>MarkdownRenderer: If loading, show Skeleton
MarkdownRenderer-->>SyntaxMarkdown: Rendered block or Skeleton
Class diagram for updated Markdown rendering componentsclassDiagram
class SyntaxMarkdown {
+parseMarkdownIntoBlocks(markdown: string): string[]
+render(): JSX.Element[]
}
class MarkdownRenderer {
+render(): JSX.Element
}
class CodeBlock {
+render(): JSX.Element
}
SyntaxMarkdown --> MarkdownRenderer
MarkdownRenderer --> CodeBlock
Class diagram for new parseIncompleteMarkdown utilityclassDiagram
class parseIncompleteMarkdown {
+parseIncompleteMarkdown(text: string): string
+handleIncompleteLinksAndImages(text: string): string
+handleIncompleteBold(text: string): string
+handleIncompleteDoubleUnderscoreItalic(text: string): string
+handleIncompleteSingleAsteriskItalic(text: string): string
+handleIncompleteSingleUnderscoreItalic(text: string): string
+handleIncompleteInlineCode(text: string): string
+handleIncompleteStrikethrough(text: string): string
+handleIncompleteBlockKatex(text: string): string
+handleIncompleteInlineKatex(text: string): string
+handleIncompleteBoldItalic(text: string): string
}
Class diagram for updated memoized Highlighter componentsclassDiagram
class Line {
+render(): JSX.Element
}
class Span {
+render(): JSX.Element
}
Line --> Span
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
TestGru AssignmentSummary
Files
Tip You can |
|
๐ @canisminor1990 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- Many of the custom memo comparators only check โchildrenโ and ignore other props, which can lead to stale renders when those props changeโplease update the comparators to include all relevant props or remove them if unnecessary.
- Splitting the markdown into separate blocks via marked.lexer may break nested or cross-block constructs (like footnotes or multi-line formatting) โ verify that context is preserved or adjust the parsing to handle edge cases.
- The new parseIncompleteMarkdown utility is very regex-heavy and could impact performance on larger inputsโconsider refactoring or memoizing its logic to reduce overhead.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Many of the custom memo comparators only check โchildrenโ and ignore other props, which can lead to stale renders when those props changeโplease update the comparators to include all relevant props or remove them if unnecessary.
- Splitting the markdown into separate blocks via marked.lexer may break nested or cross-block constructs (like footnotes or multi-line formatting) โ verify that context is preserved or adjust the parsing to handle edge cases.
- The new parseIncompleteMarkdown utility is very regex-heavy and could impact performance on larger inputsโconsider refactoring or memoizing its logic to reduce overhead.
## Individual Comments
### Comment 1
<location> `src/hooks/useMarkdown/useMarkdownComponents.tsx:46` </location>
<code_context>
- const memoSection = useMemo(() => {
- return (props: any) => <Section showFootnotes={showFootnotes} {...props} />;
- }, [showFootnotes]);
+ const memeP = useCallback(({ children, ...rest }: any) => {
+ const hasImage = typeof children === 'object';
+ return hasImage ? children : <p {...rest}>{children}</p>;
+ }, []);
</code_context>
<issue_to_address>
The logic for detecting images in memeP may be too broad.
The current check will match arrays and React elements, not just images. Please update the logic to specifically identify image elements to avoid incorrect handling of other content types.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
const memeP = useCallback(({ children, ...rest }: any) => {
const hasImage = typeof children === 'object';
return hasImage ? children : <p {...rest}>{children}</p>;
}, []);
=======
const memeP = useCallback(({ children, ...rest }: any) => {
// Helper to check if a React element is an image
const isImageElement = (el: any) =>
el && el.type === 'img';
let hasImage = false;
if (Array.isArray(children)) {
hasImage = children.every(isImageElement);
} else {
hasImage = isImageElement(children);
}
return hasImage ? children : <p {...rest}>{children}</p>;
}, []);
>>>>>>> REPLACE
</suggested_fix>
### Comment 2
<location> `src/hooks/useMarkdown/parseIncompleteMarkdown.ts:285` </location>
<code_context>
+
+// Parses markdown text and removes incomplete tokens to prevent partial rendering
+export const parseIncompleteMarkdown = (text: string): string => {
+ if (!text || typeof text !== 'string') {
+ return text;
+ }
+
</code_context>
<issue_to_address>
Returning non-string values from parseIncompleteMarkdown may cause downstream issues.
Downstream consumers may receive non-string values if invalid input is passed. Recommend returning an empty string for invalid input to ensure consistent output type.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
if (!text || typeof text !== 'string') {
return text;
}
=======
if (!text || typeof text !== 'string') {
return '';
}
>>>>>>> REPLACE
</suggested_fix>
### Comment 3
<location> `src/Markdown/components/CodeBlock.tsx:43` </location>
<code_context>
+export const CodeBlock = memo<CodeBlockProps>(
</code_context>
<issue_to_address>
Memoization only on children may miss prop changes affecting rendering.
Include all props that impact rendering in the memoization comparator to prevent outdated renders when those props change.
</issue_to_address>Help me be more useful! Please click ๐ or ๐ on each comment and I'll use the feedback to improve your reviews.
| const memeP = useCallback(({ children, ...rest }: any) => { | ||
| const hasImage = typeof children === 'object'; | ||
| return hasImage ? children : <p {...rest}>{children}</p>; | ||
| }, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: The logic for detecting images in memeP may be too broad.
The current check will match arrays and React elements, not just images. Please update the logic to specifically identify image elements to avoid incorrect handling of other content types.
| const memeP = useCallback(({ children, ...rest }: any) => { | |
| const hasImage = typeof children === 'object'; | |
| return hasImage ? children : <p {...rest}>{children}</p>; | |
| }, []); | |
| const memeP = useCallback(({ children, ...rest }: any) => { | |
| // Helper to check if a React element is an image | |
| const isImageElement = (el: any) => | |
| el && el.type === 'img'; | |
| let hasImage = false; | |
| if (Array.isArray(children)) { | |
| hasImage = children.every(isImageElement); | |
| } else { | |
| hasImage = isImageElement(children); | |
| } | |
| return hasImage ? children : <p {...rest}>{children}</p>; | |
| }, []); |
| if (!text || typeof text !== 'string') { | ||
| return text; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Returning non-string values from parseIncompleteMarkdown may cause downstream issues.
Downstream consumers may receive non-string values if invalid input is passed. Recommend returning an empty string for invalid input to ensure consistent output type.
| if (!text || typeof text !== 'string') { | |
| return text; | |
| } | |
| if (!text || typeof text !== 'string') { | |
| return ''; | |
| } |
| export const CodeBlock = memo<CodeBlockProps>( | ||
| ({ fullFeatured, enableMermaid, highlight, mermaid, children, animated, ...rest }) => { | ||
| const code = useCode(children); | ||
|
|
||
| if (!code) return; | ||
| if (!code) return; | ||
|
|
||
| if (enableMermaid && code.lang === 'mermaid') | ||
| return ( | ||
| <PreMermaid fullFeatured={fullFeatured} {...mermaid} {...rest}> | ||
| {code.content} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): Memoization only on children may miss prop changes affecting rendering.
Include all props that impact rendering in the memoization comparator to prevent outdated renders when those props change.
commit: |
|
โค๏ธ Great PR @canisminor1990 โค๏ธ |
## [Version 2.10.0](v2.9.5...v2.10.0) <sup>Released on **2025-08-20**</sup> #### โจ Features - **misc**: Update Markdown aslike Streamdown. #### ๐ Bug Fixes - **misc**: Fix markdown emphasis spacing (fixed. <br/> <details> <summary><kbd>Improvements and Fixes</kbd></summary> #### What's improved * **misc**: Update Markdown aslike Streamdown, closes [#388](#388) ([3865fa0](3865fa0)) #### What's fixed * **misc**: Fix markdown emphasis spacing (fixed, closes [#387](#387) ([bbf5940](bbf5940)) </details> <div align="right"> [](#readme-top) </div>
|
๐ This PR is included in version 2.10.0 ๐ The release is available on: Your semantic-release bot ๐ฆ๐ |
๐ป ๅๆด็ฑปๅ | Change Type
๐ ๅๆด่ฏดๆ | Description of Change
refs: https://streamdown.ai/
๐ ่กฅๅ ไฟกๆฏ | Additional Information
Summary by Sourcery
Improve markdown rendering and syntax highlighting by splitting content into discrete blocks, auto-completing incomplete markdown tokens, and optimizing render performance through extensive memoization and useCallback stabilizations.
New Features:
Enhancements:
fallback wrapper for stability.