Problem
The current Mermaid integration uses mermaid-rs-renderer (a Rust crate) to render diagrams server-side to SVG. This does not work well — the rendering quality is poor and doesn't match what Mermaid.js produces.
Proposed Solution
Replace server-side rendering with client-side Mermaid.js. The standard embedding pattern is:
<!-- In the body, where the diagram appears -->
<pre class="mermaid">
graph TD
A[Client] --> B[Load Balancer]
B --> C[Server1]
</pre>
<!-- Once per page, as a module script -->
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
Architectural Changes Required
1. Extend CodeBlockHandler return type
Currently the trait returns Result<String> (just HTML). It needs to also carry out-of-band metadata — specifically, resources that must be injected into the document's <head> (or end of <body>).
Proposed: change the return type to a struct:
pub struct CodeBlockOutput {
/// HTML to insert where the code block appeared
pub html: String,
/// Additional elements the page must include (scripts, stylesheets, etc.)
/// These are outside the markdown fragment — the caller is responsible for placing them.
pub head_injections: Vec<String>,
}
2. Add head_injections to Document
pub struct Document {
// ... existing fields ...
/// Resources that handlers request be added to the page's <head> or similar.
/// Deduplicated (if multiple mermaid blocks exist, only one script tag).
pub head_injections: Vec<String>,
}
During rendering, collect head_injections from all handler outputs and deduplicate them before storing in Document.
3. Update MermaidHandler
- Body output:
<pre class="mermaid">{escaped_code}</pre>
- Head injection: the
<script type="module"> that imports Mermaid.js from CDN and calls mermaid.initialize({ startOnLoad: true })
- Drop the
mermaid-rs-renderer dependency and the mermaid feature flag (or repurpose it)
4. Update all other handlers
Mechanical change — all existing handlers (ArboriumHandler, CompareHandler, AasvgHandler, PikruHandler, TermHandler, RawCodeHandler) return empty head_injections. This is a breaking API change for any external handler implementations.
5. Downstream (dodeca)
The markdown cell protocol needs to pass head_injections through so the page assembler can include them in the final HTML document. This is a separate change in dodeca.
What This Enables
Beyond Mermaid, this pattern generalizes to any code block handler that needs external resources — KaTeX for math, custom stylesheets for specialized renderers, etc.
Problem
The current Mermaid integration uses
mermaid-rs-renderer(a Rust crate) to render diagrams server-side to SVG. This does not work well — the rendering quality is poor and doesn't match what Mermaid.js produces.Proposed Solution
Replace server-side rendering with client-side Mermaid.js. The standard embedding pattern is:
Architectural Changes Required
1. Extend
CodeBlockHandlerreturn typeCurrently the trait returns
Result<String>(just HTML). It needs to also carry out-of-band metadata — specifically, resources that must be injected into the document's<head>(or end of<body>).Proposed: change the return type to a struct:
2. Add
head_injectionstoDocumentDuring rendering, collect
head_injectionsfrom all handler outputs and deduplicate them before storing inDocument.3. Update
MermaidHandler<pre class="mermaid">{escaped_code}</pre><script type="module">that imports Mermaid.js from CDN and callsmermaid.initialize({ startOnLoad: true })mermaid-rs-rendererdependency and themermaidfeature flag (or repurpose it)4. Update all other handlers
Mechanical change — all existing handlers (
ArboriumHandler,CompareHandler,AasvgHandler,PikruHandler,TermHandler,RawCodeHandler) return emptyhead_injections. This is a breaking API change for any external handler implementations.5. Downstream (dodeca)
The markdown cell protocol needs to pass
head_injectionsthrough so the page assembler can include them in the final HTML document. This is a separate change in dodeca.What This Enables
Beyond Mermaid, this pattern generalizes to any code block handler that needs external resources — KaTeX for math, custom stylesheets for specialized renderers, etc.