Repository navigation
How Markdown Diagrams Work
This chapter explains how mermaid diagrams in the Markdown preview and the rich editor are drawn, and why the mermaid library leaves memory when the last document with diagrams closes. The rest of the Markdown editor is in How the Markdown Editor Works.
Mermaid is the largest library the app uses. Imported into the window, it stays until the app quits, because JavaScript cannot unload a module. People open a few documents with diagrams, close them and keep working, so the library should go with them.
Mermaid is never imported by the window. It runs in a hidden frame:
sequenceDiagram
participant P as Preview or rich editor
participant M as mermaid.ts (window)
participant F as mermaid-frame page
P->>M: renderMermaid(user, source, dark)
M->>F: create the hidden iframe on the first diagram
F-->>M: gm-mermaid-ready
M->>F: gm-mermaid-render (source, config with theme colors)
F->>F: import mermaid once, render
F-->>M: gm-mermaid-result (svg or error)
M-->>P: cached result
P->>M: releaseMermaid(user) on close or when its last diagram is gone
M->>M: 3 s after the last user: remove the iframe, clear the cache
-
The frame page is
src/routes/mermaid-frame/+page.svelte, prerendered asmermaid-frame.html. It importsmermaidFrame.ts, the only module that imports mermaid, and draws one diagram per message. -
Users. Every
MarkdownPreviewDomandRichMarkdownEditorthat asks for a diagram becomes a user.releaseMermaidruns from theirdestroyand when a preview renders without diagrams. A hidden tab unmounts its preview, so switching to a tab without diagrams also lets go. -
Unloading. When the last user goes,
RELEASE_DELAY_MS(3 s) passes, so moving a tab or switching the Markdown view does not reload the frame. Then the iframe is removed, the cache cleared and the queue reset. A request still queued getsDiagram closedinstead of bringing the frame back (generation). - Theme colors are read in the window, where the CSS tokens are, and sent with each request as mermaid config.
-
Messages use
postMessage(..., "*"), and both sides check the sending window (event.source) instead of the origin, whichtauri://pages may report asnull. A reply that cannot be sent still answers with an error, and the window gives up after 30 s, so one bad diagram never blocks the others.
The drawn SVG is sanitized by sanitizeSvg in the window like before; the frame only produces text.
Measured on the release app: four Markdown files with four diagrams each, closed after scrolling, then 60 s of waiting. With mermaid in the window the app kept 342 MB (median of three); with the frame it kept 264 MB, and the frame was gone every time. Part of what remains is WebKit keeping freed memory, which only Clear Cache gives back.
| File | What it does |
|---|---|
ui/web/src/lib/markdown/mermaid.ts |
Users, the frame backend, the cache, unloading |
ui/web/src/lib/markdown/mermaidFrame.ts |
Draws in the frame |
src/routes/mermaid-frame/ |
The frame page; markdownContext.ts passes its URL to configureDiagrams
|
ui/web/src/lib/markdown/previewDom.ts, richNodeViews.ts
|
Ask for diagrams and release them |
src/lib/mcp/perf.ts |
mermaidLoaded and markdownDiagramErrors in get_ui_performance
|
A frame instead of a worker. Mermaid measures text in a real document, which a worker does not have.
A short delay before unloading. Three seconds covers moving a tab between groups and switching Editor and Preview, which unmount and mount a preview right away.
Results are strings. Only text crosses between the frame and the window, so nothing in the window keeps the frame's objects alive after it is removed.
Memory stayed high after closing Markdown files with diagrams.
- The issue: opening a few documents with mermaid diagrams took the app from about 125 MB to over 400 MB, and closing all of them still left about 340 MB.
- Why it happened: mermaid was imported into the window, and an imported module stays until the app quits; its drawn diagrams were cached there too.
- The fix and why we chose it: mermaid moved into a frame that is removed with its cache when the last document with diagrams closes. An earlier try with the single-file build in headless Chrome had not freed memory, so this one was measured in the app's real WebKit before it was kept.
ui/web/src/lib/markdown/mermaid.test.ts: nothing loads before the first diagram, each source draws once, the frame and cache go after the last user, a quick redraw keeps the frame, releasing an unknown user does nothing, and a queued request does not bring the frame back. The frame itself was checked in the release app with get_ui_performance (mermaidLoaded, markdownDiagrams, markdownDiagramErrors).
- Update this page when
mermaid.ts, the frame page or the release rules change, and Markdown Editor if users notice.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode