Skip to content

Mermaid diagrams in Markdown (#462): installed Mermaid in an isolated, script-free, offline Chromium page - #502

Merged
raiseCatError merged 2 commits into
devfrom
feature/v024-mermaid
Oct 22, 2026
Merged

raiseCatError merged 2 commits into
devfrom
feature/v024-mermaid

Conversation

@raiseCatError

Copy link
Copy Markdown
Owner

Refs #462

Mermaid fences in Markdown are drawn as real diagrams in NMSh's rendered views when a Mermaid the person installed and the real browser are available. The fence text stays the only source; anything not drawn shows the fence as written with (diagram not drawn: reason). Design, acceptance criteria and limits: docs/design/diagrams.md.

Renderer

Official mermaid (MIT), the copy the person installed (npm install -g mermaid, or NMSH_MERMAID), located like Chromium and never bundled or downloaded. Its single-file bundle contains EPL-2.0 code (ELK), so vendoring it into this GPL-3.0-only package is left to the maintainer. Rejected: mermaid as a dependency (122 MB + ~20 deps), mermaid-cli (Puppeteer + its own Chromium), beautiful-mermaid / Rust re-implementations (partial syntax that could be drawn wrongly).

Security boundary

  • Runs in NMSh's existing lazy Chromium (no Puppeteer, no second browser). The renderer is a preview page with its own throwaway context, and every network request is failed. A CSP sets default-src 'none', and page scripts are disabled.
  • Mermaid runs in a DevTools isolated world, which shares the DOM but no JS with the page. Timers are shimmed to microtasks because real timers don't fire while page scripts are off. The diagram text is passed only as a callFunctionOn argument.
  • Config is securityLevel: strict with HTML labels off, maxTextSize and maxEdges set, and these keys plus dompurifyConfig, themeCSS and fonts are locked via secure. The reviewer confirmed that directives and front matter cannot loosen them.
  • The SVG is treated as untrusted. It is parsed inertly, sized from its viewBox (capped), drawn only through <img>, captured as a PNG (≤4096 px a side, ≤8.4 MP) and decoded by NMSh's bounded decoder. It never reaches the terminal, clipboard or files.
  • The Chromium preview embeds drawings only as a strict data:image/png;base64 image.

Integration

  • Where diagrams are drawn:
    • Preview (both NMSh's renderer and the Chromium preview)
    • Split's preview pane
    • The viewer
    • Hybrid's inactive blocks (the active block stays exact, editable source)
  • Larger view: i opens a drawing in the image viewer (zoom/pan).
  • Setting: Settings → General → Mermaid diagrams.
    • On request (default): D, or F1 › Diagrams / Draw diagrams while editing. Opening a document starts nothing.
    • Automatic
    • Off
  • Theme: follows the NMSh theme (light/dark, text/accent/line colours; grey under NO_COLOR).

Resources

  • One renderer page: it closes when no document shows diagrams or after 60 s idle, and the engine stops Chromium 60 s later.
  • Pacing: work starts after a 250 ms pause, and text no longer shown is dropped (50 edits draw once).
  • Cache: keyed by content + theme, at most 24 drawings / 48 MB. Each document is capped at 24 diagrams.
  • Failures: an 8 s timeout terminates the script and replaces the page; crashes are reported. The typing path never waits.
  • Input bounds (checked before any browser work): 20k chars, 1k lines, 2k chars per line, nesting 32, 4 directives.
  • Measured (M2 Air, Chrome 156, Mermaid 12.2.0): the renderer opens in about 1 s. 17 diagram types (flowchart, sequence, class, state, ER, gantt, pie, journey, gitGraph, mindmap, timeline, quadrant, xychart, requirement, sankey, block, C4) took 44–505 ms each. The Chromium tree's physical footprint is about 485 MB while drawing.
  • PTY smoke of the built binary: no Chromium process before D, the diagram drawn after D, none after exit.

Tests

  • Unit and integration: tests/diagrams{Mermaid,Library,Service,Document}.test.ts, plus new cases in documentWorkspaceApp and browserPreviewMarkdown. They cover bounds, locked config, the locator, the deterministic queue/cancel/cache/timeout/crash/idle/close behaviour, fallbacks, 30/50/80/120 columns, NO_COLOR/Safe Glyphs, Hybrid/Split/Preview/Source, undo/redo, the viewport anchor, input returning to the composer, and that nothing is created at open.
  • Real browser: tests/diagramsChrome.test.ts uses real Chrome and real Mermaid. It covers the 17 types, syntax errors, and a hostile set that sent zero requests to a local server and ran no page script. It also covers hanging libraries (timeout), a crashed page, and repeated open/close. It skips with a reason when Chrome or Mermaid is missing.
  • CI: the workflow fetches the pinned mermaid 12.2.0 for the tests and checks its integrity (test-time only).
  • Independent review: no security or isolation issues. It found 4 bugs, all fixed in the second commit with regression tests that fail on the first commit: D in the Chromium preview, a drawing abandoned by a closing document being recorded as failed, the hybrid per-document cap re-queueing forever, and control characters in locator reasons.
  • Local runs: verify:fast plus the related suites.

Needs human testing

Physical Ghostty and Zed QA: NOT RUN. The checklist is in docs/testing/v024-editor-physical-qa.md.

Not in this PR (remaining #462)

  • #fragment link following
  • Outline refinements / outline rail
  • Pointer and hybrid <details> toggling
  • A /doctor line for Mermaid

…ted, script-free, offline Chromium page; pixels only; on request by default, bounded and cached; source fallback with the reason
…oned by a closing document is forgotten not failed, the per-document cap holds in hybrid, reasons carry no terminal controls
@raiseCatError
raiseCatError merged commit 47339ff into dev Oct 22, 2026
21 of 25 checks passed
@raiseCatError
raiseCatError deleted the feature/v024-mermaid branch October 22, 2026 09:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant