Bug Description
When a markdown file contains multiple mermaid code blocks, the rendered diagrams overlap/stack on top of each other instead of being displayed independently.
Steps to Reproduce
Create a markdown file with multiple mermaid code blocks and preview it in Wave:
# Wave Mermaid Multiple Diagrams Repro
## Diagram 1 - Flowchart
```mermaid
flowchart TD
A[Start] --> B{Decision}
B -->|Yes| C[Action 1]
B -->|No| D[Action 2]
C --> E[End]
D --> E
```
## Diagram 2 - Sequence
```mermaid
sequenceDiagram
participant Alice
participant Bob
Alice->>Bob: Hello
Bob-->>Alice: Hi back
Alice->>Bob: How are you?
Bob-->>Alice: Good thanks
```
## Diagram 3 - Flowchart
```mermaid
flowchart LR
X[Input] --> Y[Process]
Y --> Z[Output]
Z --> W{Valid?}
W -->|Yes| Done[Done]
W -->|No| X
```
Expected Behavior
Each mermaid diagram should render independently in its own section, with no visual overlap.
Actual Behavior
The diagrams overlap and stack on top of each other. Elements from Diagram 3 (flowchart LR) are rendered on top of Diagram 2 (sequence diagram). A single mermaid block renders fine — the issue only appears with 2+ diagrams.
Root Cause Analysis
After reading the source code (frontend/app/element/markdown.tsx), the issue appears to be a race condition in concurrent mermaid.run() calls:
- Each
<Mermaid> component calls mermaid.run() in its own useEffect, sharing a single module-level mermaidInstance
- When React renders multiple
Mermaid components, all useEffect hooks fire in the same microtask batch — there is no serialization/mutex
mermaid.run() internally uses Date.now() for SVG element IDs (when deterministicIds is not set). Concurrent calls within the same millisecond generate identical IDs, causing SVG elements to reference or overwrite each other
- The
useEffect also lacks a cleanup function, so re-renders during an in-flight mermaid.run() can cause stale DOM mutations
Suggested Fix
- Serialize
mermaid.run() calls with a promise queue/mutex so they never execute concurrently
- Consider enabling
deterministicIds: true in mermaid configuration
- Add a cleanup function to the
useEffect to handle component unmount during rendering
Environment
- Wave version: 0.14.4 (202603271721)
- OS: macOS 14.5 (Darwin 23.4.0)
Bug Description
When a markdown file contains multiple mermaid code blocks, the rendered diagrams overlap/stack on top of each other instead of being displayed independently.
Steps to Reproduce
Create a markdown file with multiple mermaid code blocks and preview it in Wave:
Expected Behavior
Each mermaid diagram should render independently in its own section, with no visual overlap.
Actual Behavior
The diagrams overlap and stack on top of each other. Elements from Diagram 3 (flowchart LR) are rendered on top of Diagram 2 (sequence diagram). A single mermaid block renders fine — the issue only appears with 2+ diagrams.
Root Cause Analysis
After reading the source code (
frontend/app/element/markdown.tsx), the issue appears to be a race condition in concurrentmermaid.run()calls:<Mermaid>component callsmermaid.run()in its ownuseEffect, sharing a single module-levelmermaidInstanceMermaidcomponents, alluseEffecthooks fire in the same microtask batch — there is no serialization/mutexmermaid.run()internally usesDate.now()for SVG element IDs (whendeterministicIdsis not set). Concurrent calls within the same millisecond generate identical IDs, causing SVG elements to reference or overwrite each otheruseEffectalso lacks a cleanup function, so re-renders during an in-flightmermaid.run()can cause stale DOM mutationsSuggested Fix
mermaid.run()calls with a promise queue/mutex so they never execute concurrentlydeterministicIds: truein mermaid configurationuseEffectto handle component unmount during renderingEnvironment