From f5b0dd87f821ca8013bb5b439d21c09510231312 Mon Sep 17 00:00:00 2001 From: Joseph O'Brien <98370624+89jobrien@users.noreply.github.com> Date: Thu, 2 Apr 2026 03:50:53 -0400 Subject: [PATCH] fix(notgraph): replace vis.js with Mermaid for clean static flowchart --- crates/notgraph/src/emit.rs | 51 ++++++------------- .../notgraph/templates/report.html.template | 27 +++------- 2 files changed, 23 insertions(+), 55 deletions(-) diff --git a/crates/notgraph/src/emit.rs b/crates/notgraph/src/emit.rs index fc3473d..5f0083a 100644 --- a/crates/notgraph/src/emit.rs +++ b/crates/notgraph/src/emit.rs @@ -111,46 +111,26 @@ fn write_markdown(dir: &Path, stats: &GraphStats, symbol_tables: &[SymbolTable]) } fn write_html(dir: &Path, crate_graph: &CrateGraph, stats: &GraphStats) -> Result<()> { - // Build node id map from name -> index - let name_to_id: std::collections::HashMap<&str, usize> = crate_graph - .nodes - .iter() - .enumerate() - .map(|(i, n)| (n.as_str(), i)) - .collect(); - let fan_map: std::collections::HashMap<&str, (usize, usize)> = stats .crate_graph .iter() .map(|fs| (fs.name.as_str(), (fs.fan_in, fs.fan_out))) .collect(); - let nodes_js: String = crate_graph - .nodes - .iter() - .enumerate() - .map(|(i, n)| { - let (fi, fo) = fan_map.get(n.as_str()).copied().unwrap_or((0, 0)); - format!( - "{{id:{},label:{:?},title:'fan-in:{} fan-out:{}'}}", - i, n, fi, fo - ) - }) - .collect::>() - .join(","); - - let edges_js: String = crate_graph - .edges - .iter() - .enumerate() - .filter_map(|(i, (from, to))| { - // Reverse direction: dependency -> dependent ("I am used by") - let from_id = name_to_id.get(to.as_str())?; - let to_id = name_to_id.get(from.as_str())?; - Some(format!("{{id:{},from:{},to:{}}}", i, from_id, to_id)) - }) - .collect::>() - .join(","); + // Mermaid flowchart: dependency -> dependent (LR) + let mut mermaid = String::from("flowchart LR\n"); + for node in &crate_graph.nodes { + let (fi, fo) = fan_map.get(node.as_str()).copied().unwrap_or((0, 0)); + mermaid.push_str(&format!( + " {}[\"{}
in:{} out:{}\"]\n", + node, node, fi, fo + )); + } + for (from, to) in &crate_graph.edges { + // dependency -> dependent + mermaid.push_str(&format!(" {} --> {}\n", to, from)); + } + let graph_diagram = mermaid; let cycle_html = if stats.cycles.is_empty() { "

No cycles detected.

".to_string() @@ -177,8 +157,7 @@ fn write_html(dir: &Path, crate_graph: &CrateGraph, stats: &GraphStats) -> Resul let html = format!( include_str!("../templates/report.html.template"), - nodes_js = nodes_js, - edges_js = edges_js, + graph_diagram = graph_diagram, hotspot_rows = hotspot_rows, cycle_html = cycle_html, ); diff --git a/crates/notgraph/templates/report.html.template b/crates/notgraph/templates/report.html.template index 489426e..2b60246 100644 --- a/crates/notgraph/templates/report.html.template +++ b/crates/notgraph/templates/report.html.template @@ -3,7 +3,7 @@ notgraph Report - +

notgraph Report

Crate Dependency Graph

-
- +
+
+%%{{init: {{'theme':'dark','themeVariables':{{'darkMode':true,'background':'#242424','primaryColor':'#1e3a5f','primaryBorderColor':'#4a9eff','primaryTextColor':'#e0e0e0','lineColor':'#4a9eff','edgeLabelBackground':'#242424'}}}}}}%%
+{graph_diagram}
+

Hotspots

@@ -60,6 +48,7 @@ new vis.Network(document.getElementById('graph'), {{nodes, edges}}, {{

Cycle Report

{cycle_html}