Status: Accepted
ADR 0022 introduced a terminal-native markdown renderer (src/markdown/mod.rs) that converts
CommonMark to styled ratatui Lines. It handles headings, lists, blockquotes, inline
formatting and fenced code blocks well.
A natural follow-on question is: can Mermaid diagram definitions embedded in markdown be rendered inside the terminal?
Mermaid (```mermaid fenced blocks) is increasingly common in technical documentation —
architecture diagrams, sequence flows, ER diagrams, Gantt charts. The existing renderer treats
them as ordinary code blocks: bordered box, green-coloured source text, no diagram output.
Terminals are character-cell grids. Every "pixel" is one monospace glyph. Rendering an arbitrary directed graph, sequence diagram or Gantt chart in that medium requires solving several hard sub-problems simultaneously.
The Rust ecosystem has no stable, maintained crate that parses the full Mermaid DSL and emits ASCII or Unicode-art output. Mermaid itself is a JavaScript library that delegates layout and rendering to browser-based SVG. Porting that pipeline to Rust would be a multi-month project with a high maintenance burden as the Mermaid spec evolves.
Terminals that support inline images (Sixel, iTerm2 image protocol, Kitty graphics protocol) can display arbitrary bitmaps, which could in theory show a rendered diagram. However:
- Coverage is low — the most widely used terminals (macOS Terminal.app, most SSH sessions, tmux without patches) do not support any graphics protocol.
- ratatui / crossterm have no native support — adding it would require a significant new
dependency (
ratatui-imageor equivalent) and bespoke fallback handling for unsupported terminals. - Async complexity — generating a bitmap requires either an external process or a headless browser, introducing latency, IPC, and new failure modes into the render loop.
The reference Mermaid CLI (mmdc, Node.js) can produce SVG or PNG from a diagram definition.
Using it as a subprocess would require:
- Detecting whether
mmdcis installed (and surfacing a useful error if not). - Writing diagram source to a temp file, invoking
mmdc, reading the output — all synchronously or via a background task that feeds back into the render loop. - Converting SVG/PNG to a terminal graphics protocol payload and writing it at the correct cursor position within the ratatui layout — a position that can change on every resize.
- Caching invalidation: re-invoking
mmdcwhen the source changes.
This is a substantial feature in its own right, tightly coupled to terminal capabilities the editor does not currently model.
Tools like graph-easy (Perl) can render simple flowcharts as ASCII art. The output is
typically harder to read than the source Mermaid definition for anything beyond a handful of
nodes. Coverage of Mermaid diagram types (sequence, ER, Gantt, git graph, pie, mindmap…) is
partial or absent. The cognitive value rarely exceeds just reading the source.
Rather than attempting to render diagrams, the TUI preview makes Mermaid blocks visually distinct from ordinary code blocks to communicate their nature clearly:
| Property | Ordinary code block | Mermaid block |
|---|---|---|
| Header colour | DarkGray | Yellow |
| Header label | ╭─ <lang> |
╭─ mermaid diagram ─ |
| Body text colour | Green | DarkGray (dimmed) |
| Footer hint | (none) | diagram · open in a browser to render (italic, DarkGray) |
The dimmed body signals that the text is a diagram spec, not runnable code. The italic hint below the closing border gives users a clear next step without adding any complexity to the render pipeline.
Implementation: three targeted changes to the Event::Start(Tag::CodeBlock),
Event::Text (when in_code_block), and Event::End(TagEnd::CodeBlock) arms of
src/markdown/mod.rs. No new dependencies; no new state beyond checking
self.code_lang == "mermaid".
A new action Action::MarkdownOpenBrowser (keybinding SPC m b) renders the current buffer
to a self-contained HTML file and opens it in the system default browser.
Pipeline:
- Current buffer content is fed to
pulldown_cmark::ParserwithOptions::all(). pulldown_cmark::html::push_htmlemits a complete HTML body — the same parser already in use, just a different output target.- The body is wrapped in a minimal HTML page with a legible stylesheet (system font stack, 800 px max-width, code blocks, blockquotes).
- Mermaid.js 11 is loaded from CDN (
cdn.jsdelivr.net). A small inline script convertspulldown-cmark's<pre><code class="language-mermaid">output — which is valid CommonMark but not what Mermaid.js expects — into<div class="mermaid">elements, then callsmermaid.run(). - The HTML is written to
$TMPDIR/forgiven_<stem>.html. - The platform opener is spawned detached (
openon macOS,xdg-openon Linux,exploreron Windows). The TUI continues running; no suspend/restore cycle is needed.
Why this approach over alternatives:
- Zero new Rust dependencies —
pulldown-cmark::htmlis already in the dependency tree. - No install requirement for the user — Mermaid.js loads from CDN on demand.
- Full Mermaid support — the browser renders every diagram type correctly.
- The TUI is uninterrupted —
SPC m bis instant from the user's perspective; the browser opens asynchronously. - A temp file with a stable name (
forgiven_<stem>.html) means re-runningSPC m bafter edits refreshes the same browser tab on most platforms.
Positive
- Mermaid diagrams in documentation are clearly labelled in the TUI preview; users are never confused by a wall of green source text.
- Full, correctly rendered Mermaid output is one keypress away via
SPC m b. - The browser export is useful for all markdown, not just files containing Mermaid — it doubles as a general "share / print" path.
- No complexity added to the TUI render loop; no new async tasks; no new dependencies.
Negative / trade-offs
SPC m brequires an internet connection to render Mermaid (CDN fetch). Users working offline will see the raw diagram source in the browser instead of a rendered diagram. A future option could bundle a local Mermaid.js asset.- The temp file is never cleaned up automatically. On most operating systems the OS temp directory is purged on reboot; this is considered acceptable for a preview artefact.
- The HTML page does not live-update as the buffer changes. Users must press
SPC m bagain to refresh — consistent with the mental model of an explicit export action.