Enhanced Markdown
High-performance Markdown preview extension for VS Code that renders
PlantUML, Mermaid and
D2 diagrams from fenced code blocks.
```plantuml
Alice -> Bob: hello
graph TD
A --> B
x -> y
## Design
- **Render once, cache forever** — diagrams are keyed by a SHA-1 content hash
(source + renderer config). Hits are served from an in-memory LRU plus a disk
cache in `globalStorage`, so reopening a document never re-runs the CLI.
- **Concurrency-limited pipeline** — a bounded queue runs at most N render
processes at once (default 4), with per-render timeouts.
- **Mermaid renders in the webview** — the bundled `mermaid.js` runs client-side
(no process, no install) and caches rendered SVG in `sessionStorage`, so
typing does not re-parse unchanged diagrams.
- **Placeholder → swap** — cache misses show a loading skeleton, then a single
debounced `markdown.preview.refresh` swaps in the finished SVG. No scroll
jumps in steady state (everything is cache hits).
- **Extensible** — renderers are a registry. Add a type by implementing
`DiagramRenderer` and mapping a fence language to it via
`enhancedMarkdown.languages`.
## Requirements
| Renderer | Required |
| --- | --- |
| PlantUML | `java` + `plantuml.jar` (configured below). Graphviz is **not** required for recent PlantUML releases — Smetana/ELK layout is built in. |
| Mermaid | Nothing (client engine, default). Optional `mmdc` for the `cli` engine. |
| D2 | `d2` on PATH (or a configured path). |
## Configuration
All settings live under `enhancedMarkdown.*`.
### PlantUML (local jar)
```jsonc
{
"enhancedMarkdown.plantuml.jarPath": "/opt/plantuml/plantuml.jar",
"enhancedMarkdown.plantuml.javaPath": "java",
"enhancedMarkdown.plantuml.args": ["-tsvg", "-pipe", "-charset", "UTF-8"]
}
command takes precedence over jarPath; if either ends in .jar it is run as
java -Djava.awt.headless=true -jar <path>. If neither is set, plantuml is
resolved from PATH.
Mermaid
{
"enhancedMarkdown.mermaid.engine": "client", // or "cli"
"enhancedMarkdown.mermaid.cliPath": "mmdc", // only for "cli"
"enhancedMarkdown.mermaid.theme": "auto" // auto | default | neutral | dark | forest
}
D2
{
"enhancedMarkdown.d2.cliPath": "d2",
"enhancedMarkdown.d2.layout": "elk", // default | dagre | elk
"enhancedMarkdown.d2.theme": -1, // d2 theme id, -1 = d2 default
"enhancedMarkdown.d2.sketch": false
}
Rendering & cache
| Setting |
Default |
Notes |
enhancedMarkdown.renderOutput |
"data-uri" |
"server" serves from a local HTTP server (faster for large docs) but requires the Markdown preview security level Allow insecure local content. |
enhancedMarkdown.renderConcurrency |
4 |
Parallel render processes. |
enhancedMarkdown.renderTimeoutMs |
60000 |
Per-render timeout. |
enhancedMarkdown.cacheEnabled |
true |
Memory + disk cache. |
enhancedMarkdown.cacheMaxAgeDays |
30 |
Disk-cache pruning age. |
Commands
- Enhanced Markdown: Clear diagram cache — drop all cached renders and
refresh the preview. Useful after upgrading a renderer binary.
Notes & limitations
!include in PlantUML resolves relative to the process working directory
(first workspace folder), not the Markdown file — a limitation of -pipe
mode. Point PlantUML at includes with an extra -I <dir> in
enhancedMarkdown.plantuml.args.
renderOutput: "server" binds a server to 127.0.0.1 on an ephemeral port
and serves only image/svg+xml at hash-addressed paths. The preview blocks
http://127.0.0.1 by default (Strict security level), so switch the preview
security level to "Allow insecure local content" to use it.
- Diagrams are not shown in editor line-number sync highlights (the rendered
block has no source line), but scroll sync still works.
Building
npm install
npm run build # esbuild: dist/extension.js + dist/preview.js
npm run typecheck
npm run watch # during development