Skip to content

feat(markdown): render Mermaid & PlantUML diagrams in the markdown preview#243

Open
architawr wants to merge 14 commits into
0-AI-UG:mainfrom
architawr:feat/markdown-diagrams
Open

feat(markdown): render Mermaid & PlantUML diagrams in the markdown preview#243
architawr wants to merge 14 commits into
0-AI-UG:mainfrom
architawr:feat/markdown-diagrams

Conversation

@architawr

Copy link
Copy Markdown
Contributor

Both changes requested in the #204 review — the rebase onto main and making PlantUML opt-in — were delivered in full before it was closed, and the feature is zero-cost when unused (Mermaid lazy-loads only when a diagram actually appears; PlantUML is off by default), so the added-surface-area concern doesn't really apply here.
Diagrams in committed markdown — architecture notes, READMEs, ADRs — are a standard, everyday documentation workflow (GitHub itself renders Mermaid in markdown natively), so previewing them in Cate's .md preview is a baseline, genuinely-needed capability, not a niche add-on — I'd really appreciate a reconsideration on the merits.
Re-submission of #204 (closed on scope grounds after the review was addressed; it couldn't be reopened, so opening fresh). The earlier review points are resolved and the branch is re-synced with the latest main (clean build + tests). Discussion context lives in #204.

What

Renders Mermaid and PlantUML diagrams inside the markdown file preview (the .md Preview toggle in the editor), modeled on the well-known VS Code extensions.

  • ```mermaid → rendered client-side as SVG.
  • ```plantuml / ```puml / ```uml → rendered as an image.
  • Ordinary code blocks render exactly as before.

How

  • Mermaid is bundled and lazy-loaded (kept out of the initial bundle via dynamic import()), rendered fully offline, theme-aware (re-renders on theme switch), with securityLevel: 'strict'.
  • PlantUML has three modes (Settings → Diagrams), defaulting to Off:
    • Off (default): PlantUML blocks aren't rendered — no network or process. Mermaid is unaffected.
    • Server: plantuml-encoder<img src="{server}/svg/…">. Opt-in because the diagram text is sent to the configured server (default https://www.plantuml.com/plantuml; point it at a local plantuml-server for private/offline rendering).
    • Local: a main-process IPC handler spawns java -jar <plantuml.jar> -tsvg -pipe and returns the SVG, shown as a data: image.
  • Both PlantUML modes render through an <img>, so no CSP change is required (img-src already allows https: and data:).
  • MarkdownPreview was extracted out of the oversized EditorPanel.tsx into a focused src/renderer/panels/markdown/ module.

Security / CSP

  • No app CSP change — production CSP stays script-src 'self'.
  • The bundled Mermaid render path contains no eval / new Function, so it runs under the strict prod CSP; output is DOMPurify-sanitized (securityLevel: 'strict'). PlantUML is rendered as an image, not injected markup, and is off unless explicitly enabled. The configured server URL is validated to be http(s).

Testing

  • Unit tests: fence detection + source extraction (incl. CRLF), PlantUML server-URL encoding round-trip, local-render spawn behavior (success / ENOENT / non-zero exit / timeout / non-SVG), and the local data-URL client.
  • Regression test: diagram components are not remounted when the preview re-renders.
  • tsc --noEmit clean · vitest 557 passed / 3 skipped · npm run build succeeds (re-verified after syncing with latest main).

Notes / scope

  • plantuml.jar is not bundled; Local mode uses a user-provided jar (brew install plantuml, etc.).
  • Out of scope: diagrams in the Agent chat, Mermaid theme overrides, syntax highlighting for ordinary code blocks.

Artur Karapetyan added 14 commits May 31, 2026 14:59
…aid loading state, atomic stdin, CRLF + url-scheme hardening, renderLocal tests
…s/plugins to stable module refs

Inline components/remarkPlugins literals were recreated every render, giving
diagram children unstable component identities; EditorPanel's whole-workspaces
subscription re-renders often, so MermaidDiagram/PlantUmlDiagram remounted
repeatedly (re-running async render + reloading the <img>). Adds a regression
test asserting no remount on parent re-render.
… theme tokens

Addresses review on 0-AI-UG#204.

Privacy: PlantUML now defaults to a new 'off' mode instead of 'server', so a
previewed markdown file never sends ```plantuml block text to a third-party
server (plantuml.com) without explicit consent. Server mode (which transmits
diagram text) and Local mode (java + jar, on-machine) are both opt-in via
Settings -> Diagrams. 'off' renders the source as a plain code block with a hint.

Theme: after rebasing onto main, the extracted MarkdownPreview still carried the
old hardcoded Tailwind classes (neutral/blue/pink). main (0-AI-UG#208/0-AI-UG#206) migrated the
preview to semantic theme tokens (border-strong, text-agent, bg-surface-3,
text-secondary, bg-hover-strong) — re-applied those so the preview tracks the
active theme. PlantUmlDiagram's fallback/error cards use the same tokens.

Adds a regression test pinning the privacy default to 'off'.
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