{{ message }}
Repository navigation
fix(muya): give every mermaid render its own id (#5023) - #5465
Merged
Merged
Conversation
1 task done
`mermaid.run()` names each render `mermaid-${Date.now()}` and its renderers
then resolve that name with a document-wide lookup, so two diagrams whose
renders start in the same millisecond draw into a single `<svg>`. A document
that mounts several mermaid blocks at once hits this on open: the earlier
block ends up holding both diagrams superimposed and the later one renders
empty.
Render through `mermaid.render(id, code, target)` instead, minting the id from
a counter so concurrent renders can never collide. Passing the code straight
to the renderer also drops the sanitize round-trip that only existed to stage
it for `run()`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mount twelve mermaid blocks at once and assert each preview holds exactly its own three nodes under a unique svg id. Twelve makes the millisecond id collision near-certain, and the per-diagram node labels name both the block that lost its diagram and the one that received it when the assertion fails. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Jocs
force-pushed
the
fix/mermaid-concurrent-render-id-5023
branch
from
September 19, 2026 15:58
32e520b to
2d505bd
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.

Fixes #5023.
The bug
Opening a document that holds several mermaid blocks renders some of them wrong: one block shows two different diagrams drawn on top of each other, another one is blank. Clicking the block to reveal its code and collapsing it again re-renders that single diagram correctly, which is why the damage looks like it "heals" itself.
Root cause
Every
DiagramPreviewrenders itself as it mounts, so a document with N mermaid blocks starts N renders back to back.mermaid.run()does not let the caller name the render — it derives the id fromDate.now()(utils.InitIDGeneratorfalls back to() => Date.now()wheneverdeterministicIdsis off, which is the default), so two renders that begin in the same millisecond get the same id. Mermaid then resolves that id document-wide:Renders are serialized by mermaid's own queue, so by the time the second diagram of a colliding pair draws, the first one's finished
<svg>is already sitting in the document carrying that id.select('body').select('[id=…]')returns that earlier element, so the second diagram is painted into the first block — and the second block is handed back its own untouched, empty<svg>.Hence the two reported symptoms at once: superimposed diagrams in the earlier block, a blank one later.
The fix
Render through
mermaid.render(id, code, target)and mint the id from a module counter, so concurrent renders can never share one. Everything else about the render is unchanged —targetis still passed so text is measured in the same place as before, andbindFunctionsis still applied.The
sanitize(code) → innerHTML → removeAttribute('data-processed')dance goes away with it: it only existed to stage the code whererun()would pick it up, andrender()takes the code directly.Verification
New e2e spec
packages/muya/e2e/tests/diagrams/mermaid.spec.ts→concurrent mermaid diagrams: mounts 12 diagrams with per-diagram node labels and asserts each preview holds exactly its own three nodes and a unique svg id.Before the fix (develop, 12 diagrams), previews land like this:
Also reproduced in the packaged renderer itself (
build:unpack+ a 24-diagram document driven throughlaunchWithMarkdown): 4 of 5 runs corrupted before the change, 5 of 5 clean after it.pnpm -C packages/muya test— 253 files / 1899 tests passpnpm -C packages/muya lint,lint:types,check-circular— cleandevelopin this environment; the new spec is red before the change and green after.🤖 Generated with Claude Code