Turn an outline into one standalone, interactive mind map. A small typed JSON file goes in, a single HTML file built on markmap comes out, and a validator checks the input first.
The output is deterministic: the template is a static asset and a Node.js CLI fills it in, validates and escapes the content, so the same JSON always gives the same bytes. That makes it safe to drive from an AI agent: the model writes the outline, never the HTML. SKILL.md is the ready-made instruction file for that use, a Claude Code skill that tells an agent how to turn a document into the JSON and call the CLI.
The document above is examples/example.html, produced by the CLI from examples/example.mindmap.json. Regenerate the screenshot with npm run capture:preview so it never drifts from the template.
Every output is a single self-contained HTML file: no build step, no script fetched at view time (it opens offline and behind corporate web filters), with search, level folding, pitch mode, dark mode, palette/font/size controls, an in-page markdown editor that regenerates the tree, and SVG/PNG export built in.
Needs Node.js 18 or later. Run it without installing anything:
npx markmap-forge render input.json output.htmlor install it globally with npm install -g markmap-forge. To work on the code, clone this repository and run npm install.
markmap-forge validate <input.json> [--json]
markmap-forge render <input.json> [output.html] [--json]
markmap-forge deliver <input.json> [output.html] [--json]From a clone, run node bin/markmap-forge.mjs in place of markmap-forge.
deliver validates, renders, and prints a SHA-256 receipt for both the JSON source and the HTML artifact.
See schemas/mindmap.schema.json and examples/example.mindmap.json:
{
"schema_version": 1,
"source_name": "Human-readable original title or filename",
"generation_date": "2026-08-29",
"markdown": "# Title\n## Pillar\n### Entity\n- detail\n"
}markdown is a strict outline: # title (exactly one), ## pillars, ### entities, - nested bullets. No tables, no stray arrows/pipes, no heading-level skips — the validator enforces all three as hard errors.
One self-contained HTML file (no build step, no server) with:
- Live search with match count and Ctrl+F
- Expand/Collapse and one button per detected heading depth
- Dark/light theme with persisted state (localStorage)
- SVG export
- PNG export: a full-resolution, high-DPI (2x) raster of the whole map, distinct from the raw SVG export. It is rendered from a clone of the SVG without
<foreignObject>, which Chrome refuses to export from a canvas. - Pitch Mode (fullscreen focus-and-zoom on click): it engages AutoFit, so expanded nodes are framed instantly.
- Decorative animated links: a toggleable, subtle flowing dash along the branches. Purely visual, since a mind map's branches are hierarchy, not data flow.
- Overlap-safe fitting: the floating menu panels are fixed overlays, so every "fit" pans the tree clear of them once the fit animation has settled, instead of leaving a node hidden underneath a panel.
Rendering is powered by markmap + d3, inlined into the generated HTML from pinned copies in vendor/browser/ (d3 7.9.0, markmap-lib and markmap-view 0.18.12, licences alongside). Up to 0.3.0 they were loaded from cdn.jsdelivr.net, which corporate web filters block: the map then rendered empty. Each file is now ~0.7 MB instead of ~40 KB, the price of working anywhere. The only remaining network request is Google Fonts, and every font stack has a local fallback.
markmap's KaTeX and highlight.js plugins are disabled for the same reason (they fetch scripts, styles and fonts from the CDN at view time): a formula shows as its source text, a code block as plain monospace.
After bumping a library version in lib/browser-libs.mjs, regenerate the vendored files with npm run vendor:browser (checks each npm tarball's integrity, minifies with esbuild, copies the licence).
npm testUnit tests cover the validator and the renderer (no <script src> in the output, libraries inlined in order, $ sequences in content kept verbatim). npm run test:live additionally renders examples/example.mindmap.json and loads it in a real headless Chrome (via raw CDP — no puppeteer dependency) to assert the interactive JS actually works: no node renders underneath the fixed menu panels after a fit, dark-mode/PNG-export both run without console errors, and the page makes no network request other than web fonts, even when the content holds a formula or a code block. Needs a local Chrome (macOS, Linux or Windows; Edge also works on Windows); skips with a message if none is found.
npm run render:exampleRenders examples/example.mindmap.json (a mind map describing this tool itself) to examples/example.html.
MIT
