diff --git a/src/mermaid.rs b/src/mermaid.rs index caa437b..498400d 100644 --- a/src/mermaid.rs +++ b/src/mermaid.rs @@ -3,6 +3,7 @@ //! Converts Mermaid diagram definitions to SVG at build-time. use mermaid_rs_renderer::RenderOptions; +use std::panic; /// Render a Mermaid diagram to SVG. /// @@ -11,9 +12,21 @@ use mermaid_rs_renderer::RenderOptions; /// /// # Returns /// The rendered SVG string, or an error message on failure. +/// +/// # Note +/// Uses catch_unwind to handle panics in upstream dependencies gracefully. pub fn render_diagram(code: &str) -> Result { - let opts = RenderOptions::modern(); - mermaid_rs_renderer::render_with_options(code, opts).map_err(|e| e.to_string()) + let code = code.to_owned(); + let result = panic::catch_unwind(move || { + let opts = RenderOptions::modern(); + mermaid_rs_renderer::render_with_options(&code, opts) + }); + + match result { + Ok(Ok(svg)) => Ok(svg), + Ok(Err(e)) => Err(e.to_string()), + Err(_) => Err("mermaid rendering panicked (upstream bug)".to_string()), + } } #[cfg(test)] @@ -40,4 +53,11 @@ mod tests { // May succeed with error node or fail gracefully assert!(result.is_ok() || result.is_err()); } + + #[test] + fn test_state_diagram() { + let result = + render_diagram("stateDiagram-v2\n [*] --> Idle\n Idle --> Processing: Start"); + assert!(result.is_ok() || result.is_err()); + } } diff --git a/src/render.rs b/src/render.rs index f83f61d..66a70f0 100644 --- a/src/render.rs +++ b/src/render.rs @@ -46,23 +46,43 @@ pub fn markdown_to_html(markdown: &str) -> String { Event::End(TagEnd::CodeBlock) => { // Render the code block with highlighting let lang_str = code_block_lang.as_deref().unwrap_or(""); - html_output.push_str("
", lang_str));
-                    html_output.push_str(&highlight_code(lang, &code_block_content));
-                } else {
-                    // Unsupported language: render as plain escaped text
-                    if !lang_str.is_empty() {
-                        html_output.push_str(&format!(" class=\"language-{}\">", lang_str));
-                    } else {
-                        html_output.push('>');
+                // Mermaid diagrams: render to SVG
+                if lang_str == "mermaid" {
+                    match crate::mermaid::render_diagram(&code_block_content) {
+                        Ok(svg) => {
+                            html_output.push_str("
\n"); + html_output.push_str(&svg); + html_output.push_str("\n
\n"); + } + Err(e) => { + eprintln!("mermaid render error: {e}"); + html_output.push_str("
");
+                            html_output.push_str(&html_escape(&code_block_content));
+                            html_output.push_str("
\n"); + } } - html_output.push_str(&html_escape(&code_block_content)); + } else { + // Code blocks: syntax highlighting + html_output.push_str("
", lang_str));
+                        html_output.push_str(&highlight_code(lang, &code_block_content));
+                    } else {
+                        // Unsupported language: render as plain escaped text
+                        if !lang_str.is_empty() {
+                            html_output.push_str(&format!(" class=\"language-{}\">", lang_str));
+                        } else {
+                            html_output.push('>');
+                        }
+                        html_output.push_str(&html_escape(&code_block_content));
+                    }
+
+                    html_output.push_str("
\n"); } - html_output.push_str("
\n"); code_block_lang = None; code_block_content.clear(); }