feat(mermaid): add mermaid-rs-renderer diagram module

Add src/mermaid.rs with render_diagram() wrapper around
mermaid-rs-renderer for build-time Mermaid-to-SVG conversion.

- Use mermaid-rs-renderer git dependency (SVG-only, no PNG)
- Configure with RenderOptions::modern() theme
- Include unit tests for flowchart and sequence diagrams
This commit is contained in:
Timothy DeHerrera
2026-01-28 20:31:40 -07:00
parent eeec4a999b
commit abe465723c
5 changed files with 221 additions and 1 deletions

173
Cargo.lock generated
View File

@@ -41,6 +41,12 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "arraydeque" name = "arraydeque"
version = "0.5.1" version = "0.5.1"
@@ -74,6 +80,15 @@ dependencies = [
"wyz", "wyz",
] ]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]] [[package]]
name = "bon" name = "bon"
version = "3.8.2" version = "3.8.2"
@@ -178,6 +193,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.6" version = "0.8.6"
@@ -203,6 +227,16 @@ version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.33.0" version = "0.33.0"
@@ -235,6 +269,16 @@ dependencies = [
"syn 2.0.114", "syn 2.0.114",
] ]
[[package]]
name = "dagre_rust"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0e44f22d2a32979a1265aa1a70f6e2f4f43ec0d562d483a1c818f9a4121926"
dependencies = [
"graphlib_rust",
"ordered_hashmap",
]
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.23.0" version = "0.23.0"
@@ -297,6 +341,16 @@ dependencies = [
"matches", "matches",
] ]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]] [[package]]
name = "dtoa" name = "dtoa"
version = "1.0.11" version = "1.0.11"
@@ -351,6 +405,16 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]] [[package]]
name = "getopts" name = "getopts"
version = "0.2.24" version = "0.2.24"
@@ -383,6 +447,15 @@ dependencies = [
"wasip2", "wasip2",
] ]
[[package]]
name = "graphlib_rust"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b47de2c5685b5bb3689d6b0977f38959f3d7d82d3556627cfebbfaa42eda05ea"
dependencies = [
"ordered_hashmap",
]
[[package]] [[package]]
name = "gray_matter" name = "gray_matter"
version = "0.2.9" version = "0.2.9"
@@ -478,6 +551,17 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]] [[package]]
name = "katex-rs" name = "katex-rs"
version = "0.2.3" version = "0.2.3"
@@ -597,6 +681,22 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mermaid-rs-renderer"
version = "0.1.2"
source = "git+https://github.com/1jehuang/mermaid-rs-renderer#66b7d8c3f3152153666d589e789a143e87afcec5"
dependencies = [
"anyhow",
"dagre_rust",
"graphlib_rust",
"json5",
"once_cell",
"regex",
"serde",
"serde_json",
"thiserror",
]
[[package]] [[package]]
name = "nrd-sh" name = "nrd-sh"
version = "0.1.0" version = "0.1.0"
@@ -605,6 +705,7 @@ dependencies = [
"katex-rs", "katex-rs",
"lightningcss", "lightningcss",
"maud", "maud",
"mermaid-rs-renderer",
"pulldown-cmark", "pulldown-cmark",
"serde", "serde",
"thiserror", "thiserror",
@@ -632,6 +733,12 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "ordered_hashmap"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb1ea499d242299d564879c8b375c11285f5a26d62d8010768909d6d099c4c5a"
[[package]] [[package]]
name = "outref" name = "outref"
version = "0.1.0" version = "0.1.0"
@@ -693,6 +800,49 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "pest"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7"
dependencies = [
"memchr",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "pest_meta"
version = "2.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365"
dependencies = [
"pest",
"sha2",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.11.3" version = "0.11.3"
@@ -1121,6 +1271,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"
@@ -1452,6 +1613,18 @@ dependencies = [
"tree-sitter-language", "tree-sitter-language",
] ]
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "ucd-trie"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.9.0" version = "2.9.0"

View File

@@ -35,3 +35,6 @@ lightningcss = "1.0.0-alpha.70"
katex-rs = "0.2.3" katex-rs = "0.2.3"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
toml = "0.8" toml = "0.8"
# Diagram rendering
mermaid-rs-renderer = { git = "https://github.com/1jehuang/mermaid-rs-renderer", default-features = false }

View File

@@ -9,6 +9,7 @@ mod error;
mod feed; mod feed;
mod highlight; mod highlight;
mod math; mod math;
mod mermaid;
mod render; mod render;
mod templates; mod templates;

43
src/mermaid.rs Normal file
View File

@@ -0,0 +1,43 @@
//! Mermaid diagram rendering via mermaid-rs-renderer.
//!
//! Converts Mermaid diagram definitions to SVG at build-time.
use mermaid_rs_renderer::RenderOptions;
/// Render a Mermaid diagram to SVG.
///
/// # Arguments
/// * `code` - The Mermaid diagram definition
///
/// # Returns
/// The rendered SVG string, or an error message on failure.
pub fn render_diagram(code: &str) -> Result<String, String> {
let opts = RenderOptions::modern();
mermaid_rs_renderer::render_with_options(code, opts).map_err(|e| e.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_flowchart() {
let result = render_diagram("flowchart LR; A-->B-->C").unwrap();
assert!(result.contains("<svg"));
assert!(result.contains("</svg>"));
}
#[test]
fn test_sequence_diagram() {
let result = render_diagram("sequenceDiagram\n Alice->>Bob: Hello").unwrap();
assert!(result.contains("<svg"));
}
#[test]
fn test_invalid_syntax_no_panic() {
// Should not panic, returns error
let result = render_diagram("invalid diagram syntax ???");
// May succeed with error node or fail gracefully
assert!(result.is_ok() || result.is_err());
}
}

View File

@@ -1,6 +1,6 @@
//! Markdown to HTML rendering via pulldown-cmark with syntax highlighting. //! Markdown to HTML rendering via pulldown-cmark with syntax highlighting.
use crate::highlight::{Language, highlight_code}; use crate::highlight::{highlight_code, Language};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
/// Render markdown content to HTML with syntax highlighting. /// Render markdown content to HTML with syntax highlighting.