This is the eighth post in the LocalPapa Notes dev-log series. The last few orbited phone sensors; this one steps into a very "AI-era" need — building the Mermaid Online Editor.
Why this tool is especially useful in the LLM era
You've probably hit this: you ask ChatGPT or Claude "draw me a flowchart," and it hands back a chunk of Mermaid syntax — plain text full of graph TD; A-->B;. The syntax is right, but you can't see the diagram. You have to find somewhere to render it.
This tool is that "somewhere": paste the Mermaid text from the LLM on the left, and it becomes a diagram on the right, live. Better still, an AI agent can directly produce a "open and see the diagram" share link.
And — the diagram content is rendered 100% locally in your browser, never uploaded.
The hidden problem with live preview: race conditions
"Render as you type" sounds simple, but hides a classic trap: a race condition.
Mermaid's render() is asynchronous. When you type fast, you trigger several renders in a row; if the 2nd result comes back later than the 3rd, you get the maddening "old diagram overwriting the new one" on screen.
Our fix is to give each render an incrementing sequence number renderSeq: after await mermaid.render() returns, if the sequence is already stale (the user typed again), discard the result. Only ever let the latest render reach the screen.
Two more experience details:
- On a syntax error, deliberately keep the last successful diagram, showing the error beside it — rather than blanking the screen. You're still typing, the diagram's still there; that's what makes editing stable.
- Proactively clean up the temporary nodes Mermaid leaves in
document.body, so fast typing doesn't accumulate piles of invisible DOM.
Exports must "not warp once they leave the page"
Mermaid embeds its <style> into the SVG it outputs. Save it directly and colours often drop or sizes go wrong. So on export we do a few things:
- SVG: clone it, add
xmlns, derive explicit width/height from the viewBox, then serialize — so it's correct wherever it's opened.
- PNG: draw the SVG into a
<canvas>, first fill a white background (otherwise transparent backgrounds turn black in some viewers), scale by 1x/2x/3x × device pixel ratio, clamp back proportionally if the area exceeds a limit, then toBlob to download.
The goal is singular: the image you export looks exactly like the preview once it leaves this page.
Stuffing a whole diagram into a single URL
The share feature is this tool's highlight: sharing without a server.
We deflate your Mermaid code with pako, convert to base64url, add a pako: prefix, and encode it into the URL's # fragment. The recipient opens the link, and the frontend decodes, restores and renders it itself — the whole diagram is "hidden" in the URL, with no backend involved. (If pako fails to load, it falls back to plain base64 with a code: prefix, so the feature doesn't break.)
URLs can't be infinitely long, of course. So we added pragmatic limits: if decoded content exceeds 100KB, or the generated URL exceeds 8000 characters, we suggest exporting instead. A garbage hash doesn't throw either — it quietly falls back to the draft or a template. Don't let one bad link take down the whole tool.
Security: rendering someone else's diagram needs care
Share links mean you might render Mermaid code someone else gave you. Mermaid diagrams can actually hide scripts — a breeding ground for XSS.
So we enable securityLevel: 'strict' to block scripts embedded in diagrams, and use suppressErrorRendering to take error display back under the tool's own control. A tool that accepts external input should assume by default that the input is hostile.
Loading has to be robust too: pin the version, degrade gracefully
- Use
<script type="module"> with dynamic import() to fetch a pinned mermaid@11.4.1 from jsDelivr — so an upstream minor update can't quietly change syntax behaviour and break users' diagrams.
- Wrap the whole load in
try/catch; if the CDN goes down, the preview shows a friendly error and a "retry" button, and editing and copying the code still work — no full-page failure.
Finally we reuse tools-common.js's shared getTranslation / downloadBlob, listen for theme switches to re-render with the matching mermaid theme in dark/light, and debounce-autosave drafts to localStorage, with a load priority of "share link > draft > default template."
What we learned
- Live = you must handle races. For any "compute as you type" feature, sequence-discarding is almost mandatory.
- Export fidelity is a devil-in-the-details game. Embedded styles, white background, xmlns, dimensions — miss one and the diagram warps off-page.
- Server-less sharing means encoding state into the URL. Compression + hash fragment lets a single link carry all its content.
- Anything that accepts external input, assume malice first.
securityLevel: 'strict' isn't optional.
- Tools built for AI are multiplying. LLMs produce text; tools turn that text into something viewable and usable — this interface layer will only grow more important.
The same question, wired to AI
This tool connects the series' question to AI: when an LLM hands you text it has "drawn," who turns it into a diagram you can actually see?
The answer: a web page that runs 100% in your browser — and can even tuck the diagram into a link.
In the next dev log, we step into another long thread — turning the whole site into a knowledge pack AI agents can read (OKF). Want to follow the series? Bookmark LocalPapa Notes.