-
-
Notifications
You must be signed in to change notification settings - Fork 307
fix: code blocks invisible in dark mode #763
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
Closed
Closed
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
3a415cc
fix: code blocks invisible in dark mode due to CSS/class mismatch
WolfieLeader 072f8dc
chore: remove unused PACKAGE_MANAGERS import
WolfieLeader 55a2054
fix: address review feedback in CodeBlock
WolfieLeader d8e2889
refactor: use twMerge for mermaid div class composition
WolfieLeader File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,57 +155,71 @@ export function CodeBlock({ | |
| const code = children?.props.children | ||
|
|
||
| const [codeElement, setCodeElement] = React.useState( | ||
| <pre ref={ref} className={`shiki h-full github-light dark:aurora-x`}> | ||
| <pre | ||
| ref={ref} | ||
| className="h-full p-4 bg-white dark:bg-gray-950 text-gray-800 dark:text-gray-200" | ||
| > | ||
| <code>{lang === 'mermaid' ? <svg /> : code}</code> | ||
| </pre>, | ||
| ) | ||
|
|
||
| React[ | ||
| typeof document !== 'undefined' ? 'useLayoutEffect' : 'useEffect' | ||
| ](() => { | ||
| let cancelled = false | ||
| ;(async () => { | ||
| const themes = ['github-light', 'aurora-x'] | ||
| const langStr = lang || 'plaintext' | ||
| try { | ||
| const themes = ['github-light', 'aurora-x'] | ||
| const langStr = lang || 'plaintext' | ||
|
|
||
| const { highlighter, effectiveLang } = await getHighlighter(langStr) | ||
| // Trim trailing newlines to prevent empty lines at end of code block | ||
| const trimmedCode = (code || '').trimEnd() | ||
| const { highlighter, effectiveLang } = await getHighlighter(langStr) | ||
| // Trim trailing newlines to prevent empty lines at end of code block | ||
| const trimmedCode = (code || '').trimEnd() | ||
|
|
||
| const htmls = await Promise.all( | ||
| themes.map(async (theme) => { | ||
| const output = highlighter.codeToHtml(trimmedCode, { | ||
| lang: effectiveLang, | ||
| theme, | ||
| transformers: [transformerNotationDiff()], | ||
| }) | ||
| const htmls = await Promise.all( | ||
| themes.map(async (theme) => { | ||
| const output = highlighter.codeToHtml(trimmedCode, { | ||
| lang: effectiveLang, | ||
| theme, | ||
| transformers: [transformerNotationDiff()], | ||
| }) | ||
|
|
||
| if (lang === 'mermaid') { | ||
| const preAttributes = extractPreAttributes(output) | ||
| let svgHtml = genSvgMap.get(trimmedCode) | ||
| if (!svgHtml) { | ||
| const mermaid = await getMermaid() | ||
| const { svg } = await mermaid.render('foo', trimmedCode) | ||
| genSvgMap.set(trimmedCode, svg) | ||
| svgHtml = svg | ||
| if (lang === 'mermaid') { | ||
| const preAttributes = extractPreAttributes(output) | ||
| let svgHtml = genSvgMap.get(trimmedCode) | ||
| if (!svgHtml) { | ||
| const mermaid = await getMermaid() | ||
| const { svg } = await mermaid.render('foo', trimmedCode) | ||
| genSvgMap.set(trimmedCode, svg) | ||
| svgHtml = svg | ||
| } | ||
| const cls = twMerge(preAttributes.class, 'py-4 bg-neutral-50') | ||
| return `<div class='${cls}'>${svgHtml}</div>` | ||
| } | ||
| return `<div class='${preAttributes.class} py-4 bg-neutral-50'>${svgHtml}</div>` | ||
| } | ||
|
|
||
| return output | ||
| }), | ||
| ) | ||
| return output | ||
| }), | ||
| ) | ||
|
|
||
| setCodeElement( | ||
| <div | ||
| className={twMerge( | ||
| isEmbedded ? 'h-full [&>pre]:h-full [&>pre]:rounded-none' : '', | ||
| )} | ||
| dangerouslySetInnerHTML={{ __html: htmls.join('') }} | ||
| ref={ref} | ||
| />, | ||
| ) | ||
| if (!cancelled) { | ||
| setCodeElement( | ||
| <div | ||
| className={twMerge( | ||
| isEmbedded ? 'h-full [&>pre]:h-full [&>pre]:rounded-none' : '', | ||
| )} | ||
| dangerouslySetInnerHTML={{ __html: htmls.join('') }} | ||
| ref={ref} | ||
| />, | ||
| ) | ||
| } | ||
| } catch (err) { | ||
| console.warn('Shiki highlighting failed:', err) | ||
| } | ||
|
Comment on lines
+215
to
+217
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a real fallback UI in the error path. When highlighting/rendering fails, the catch path only logs. For Mermaid blocks, the initial state can stay visually empty (placeholder Suggested fix } catch (err) {
console.warn('Shiki highlighting failed:', err)
+ if (!cancelled) {
+ setCodeElement(
+ <pre
+ ref={ref}
+ className="h-full p-4 bg-white dark:bg-gray-950 text-gray-800 dark:text-gray-200"
+ >
+ <code>{code || ''}</code>
+ </pre>,
+ )
+ }
}🤖 Prompt for AI Agents |
||
| })() | ||
| }, [code, lang]) | ||
| return () => { | ||
| cancelled = true | ||
| } | ||
| }, [code, lang, isEmbedded]) | ||
|
|
||
| return ( | ||
| <div | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
🧩 Analysis chain
🌐 Web query:
For Mermaid v11.11.0, what does the firstidargument ofmermaid.render(id, text)control, and is it required/recommended to be unique per rendered diagram instance?💡 Result:
In Mermaid v11.11.0, the first argument
idinawait mermaid.render(id, text)is used as the DOMidof the generated root<svg>element. Mermaid’s internal renderer literally does.append('svg').attr('id', id). [1]It’s effectively required (the parameter is not optional) and it’s strongly recommended to be unique per rendered diagram instance in the same document, because Mermaid also derives other temporary element IDs/selectors from it (e.g.
'd' + id,'i' + id) and will remove any existing elements with the sameidbefore rendering—so reusing anidcan overwrite/remove a previously-rendered diagram or otherwise cause collisions. [2]Sources
[1] https://raw.githubusercontent.com/mermaid-js/mermaid/9c85521/packages/mermaid/src/mermaidAPI.ts
[2] https://raw.githubusercontent.com/mermaid-js/mermaid/9c85521/packages/mermaid/src/mermaidAPI.ts
🏁 Script executed:
Repository: TanStack/tanstack.com
Length of output: 776
🏁 Script executed:
Repository: TanStack/tanstack.com
Length of output: 167
Use a unique Mermaid render ID instead of a constant.
The hardcoded
'foo'ID inmermaid.render('foo', trimmedCode)at line 192 will cause duplicate SVG IDs and broken marker/style references when multiple Mermaid blocks are rendered on the same page. According to Mermaid's API, theidparameter becomes the DOMidof the root<svg>element, and reusing the same ID can cause collisions, overwrites, and derived ID conflicts within Mermaid's internal selectors.Generate a unique ID for each render call:
Suggested fix
🤖 Prompt for AI Agents