Skip to content

fix: Revert back to mermaid.js for diagram rendering & update styling#2949

Merged
bezbac merged 1 commit into
mainfrom
feature/lfe-9772-architecture-diagram-is-broken-now
May 15, 2026
Merged

fix: Revert back to mermaid.js for diagram rendering & update styling#2949
bezbac merged 1 commit into
mainfrom
feature/lfe-9772-architecture-diagram-is-broken-now

Conversation

@bezbac
Copy link
Copy Markdown
Member

@bezbac bezbac commented May 15, 2026

Greptile Summary

Reverts from the beautiful-mermaid server-side renderer back to native mermaid.js with client-side async rendering, theme-aware configuration driven by CSS custom properties, and a skeleton loading state while the diagram resolves.

  • components/Mermaid.tsx is a full rewrite: the component is now a "use client" component that lazily imports mermaid, caches rendered SVGs by a key that encodes chart content + resolved theme + current CSS variable values, and renders a pulse-animation skeleton until the SVG is ready.
  • style.css adjusts .mermaid-diagram svg to width: auto; margin-inline: auto so diagrams centre rather than stretch to full container width.
  • package.json / pnpm-lock.yaml swap beautiful-mermaid for mermaid ^11.15.0.

Confidence Score: 3/5

The rendering logic is mostly sound but has a race condition that can silently produce wrong diagrams when a user switches themes while a chart is still rendering.

The chartHash used as the mermaid DOM element ID is derived only from chart content, so two simultaneous renders of the same chart triggered by a theme change share an identical element ID and can interfere with each other inside mermaid's temporary DOM node logic. Additionally, securityLevel: "loose" strips sanitisation from diagram labels — acceptable for a fully-controlled static site but worth an explicit decision.

components/Mermaid.tsx — the new rendering pipeline and its caching/ID strategy need attention.

Security Review

  • XSS via unsanitised labels (components/Mermaid.tsx, line 50): securityLevel: "loose" disables mermaid's built-in HTML sanitisation. Combined with htmlLabels: true, any HTML placed in a diagram label (including <script> or event handlers) would execute in the reader's browser. The attack surface is limited to users who can commit to the docs repo, but the blast radius is every visitor to any page containing a mermaid diagram.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Mermaid component mounts\n(chart, resolvedTheme)"] --> B{Cache hit?}
    B -- Yes --> C[setRenderedSvg immediately]
    B -- No --> D[Show skeleton UI]
    D --> E["hashString(chart)\nimport('mermaid')"]
    E --> F["mermaid.initialize(getMermaidConfig)\nCSS vars → themeVariables"]
    F --> G["mermaid.render('mermaid-diagram-{hash}', chart)"]
    G --> H{Success?}
    H -- Yes --> I[Store in renderedSvgCache]
    I --> J[setRenderedSvg → hide skeleton]
    H -- No --> K[setError → show raw chart in pre]
    L["resolvedTheme changes"] --> M["useEffect re-runs\nnew cache key = theme + CSS vars + chart"]
    M --> B
Loading
Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
components/Mermaid.tsx:100-103
**Dynamic import inside a function**

`import("mermaid")` is placed inside `renderMermaidChart` rather than at the top of the module. Per the team's import-placement rule, imports should be lifted to module scope. A dynamic import can still be top-level via a module-level `const mermaidPromise = import("mermaid")` singleton, which would also avoid re-initiating the lazy load on every cache miss.

### Issue 2 of 3
components/Mermaid.tsx:100-104
**Concurrent renders share the same mermaid DOM element ID**

`hashString` hashes only the chart content, not the theme. When a user switches themes while a diagram is still rendering, two separate `renderMermaidChart` calls are in flight simultaneously (one for each cache key), but both call `mermaid.render("mermaid-diagram-<same-hash>", chart)` with identical IDs. Mermaid v10+ creates a temporary DOM node with that ID; the second in-flight call will collide with the node created by the first, producing unpredictable output or an error. Including the `themeMode` (or a counter) in the element ID would make each render independent.

### Issue 3 of 3
components/Mermaid.tsx:50
`securityLevel: "loose"` combined with `htmlLabels: true` disables mermaid's built-in HTML sanitisation, allowing arbitrary HTML (including `<script>` tags) inside diagram labels to execute in the browser. While the current docs content is static, a single malicious commit to a diagram file would give XSS on every reader's browser. The default `"strict"` level sandboxes labels in an iframe and is the safer choice for a public site.

```suggestion
    securityLevel: "strict",
```

Reviews (1): Last reviewed commit: "fix: Revert back to mermaid.js for diagr..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Context used:

  • Rule used - Move imports to the top of the module instead of p... (source)

Learned From
langfuse/langfuse-python#1387

@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
langfuse-docs Ready Ready Preview, Comment May 15, 2026 1:06pm

Request Review

@github-actions
Copy link
Copy Markdown

@claude review

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label May 15, 2026
Comment thread components/Mermaid.tsx Outdated
Comment thread components/Mermaid.tsx Outdated
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — straightforward library swap in a single component; the two inline comments are render-cycle UX nits and don't block.

Extended reasoning...

Overview

This PR reverts components/Mermaid.tsx from the beautiful-mermaid library back to mermaid (v11.15.0), and refactors the component into a client-side, theme-aware renderer with module-level result/in-flight caches, a SHA-256-keyed deterministic diagram id, a skeleton placeholder, and a code-block fallback on render failure. The CSS change adjusts the rendered SVG to center horizontally rather than stretch to full width. package.json/pnpm-lock.yaml reflect the dependency swap.

Security risks

None of meaningful concern. securityLevel: "loose" is set on mermaid — this is required for HTML labels but it does mean any author-controlled diagram source can inject HTML; however, the diagram source comes from in-repo MDX content, not end-user input, so the trust boundary is the same as any other code in the docs repo. The cache key SHA hash is non-cryptographic in purpose (just an id) so the SHA-256 choice is fine. dangerouslySetInnerHTML is used on mermaid's own rendered SVG, which is the standard pattern. No auth, crypto, or permissions code is touched.

Level of scrutiny

Low-to-moderate. This is a docs-site (langfuse-docs) component change — not production app code, no CODEOWNERS-protected paths, no shared infrastructure. The change is self-contained to one component plus its dependency and a small CSS rule. The previous-state library swap pattern is reversible and Vercel preview deployments will visually verify the result before merge.

Other factors

The bug hunter found two issues, both explicitly classified as nits: (1) a one-frame skeleton flash on warm-cache remount because the cache lookup is in useEffect instead of a lazy useState initializer, and (2) a double initial render because next-themes returns resolvedTheme=undefined on the first pass and the cache key includes themeMode. Both are UX/perf optimizations on a correctly-functioning rewrite — not correctness bugs — so they don't warrant blocking. Inline comments are already attached for the author to consider.

Comment thread components/Mermaid.tsx Outdated
Comment thread components/Mermaid.tsx
@bezbac bezbac force-pushed the feature/lfe-9772-architecture-diagram-is-broken-now branch from ca4a053 to 4b8b282 Compare May 15, 2026 09:31
Comment thread components/Mermaid.tsx Outdated
Comment thread components/Mermaid.tsx Outdated
@bezbac bezbac force-pushed the feature/lfe-9772-architecture-diagram-is-broken-now branch from 4b8b282 to c5a8c43 Compare May 15, 2026 13:03
Comment thread components/Mermaid.tsx
@bezbac bezbac added this pull request to the merge queue May 15, 2026
@dosubot dosubot Bot added the auto-merge This PR is set to be merged label May 15, 2026
Merged via the queue into main with commit 9bbb204 May 15, 2026
17 checks passed
@bezbac bezbac deleted the feature/lfe-9772-architecture-diagram-is-broken-now branch May 15, 2026 13:44
@dosubot dosubot Bot removed the auto-merge This PR is set to be merged label May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant