From f7fb72a4723d497efad276eb33def44d7648461c Mon Sep 17 00:00:00 2001
From: "brynary-fabro[bot]"
<265161896+brynary-fabro[bot]@users.noreply.github.com>
Date: Mon, 16 Mar 2026 21:38:47 -0400
Subject: [PATCH] Style SVG output from `fabro graph` and API (#18)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This PR styles the SVG output from `fabro graph` and the API graph
endpoints to match the polished look of the documentation SVGs. It
introduces two internal functions in `graph.rs`:
`inject_dot_style_defaults`, which inserts DOT-level defaults
(transparent background, teal `#357f9e` node strokes, gray `#666666`
edges, Helvetica font) after the first `{` in any DOT source, and
`postprocess_svg`, which removes the Graphviz-generated white background
polygon and injects a `"##;
+
+/// DOT graph-level defaults injected after the first `{`.
+const DOT_STYLE_DEFAULTS: &str = r##"
+ bgcolor="transparent"
+ node [color="#357f9e", fontname="Helvetica", fontsize=12, fontcolor="#1a1a1a"]
+ edge [color="#666666", fontname="Helvetica", fontsize=10, fontcolor="#666666"]
+"##;
+
static RANKDIR_RE: LazyLock =
LazyLock::new(|| regex::Regex::new(r"rankdir\s*=\s*\w+").unwrap());
@@ -115,8 +134,52 @@ fn apply_direction<'a>(source: &'a str, direction: Option) -> Co
}
}
-/// Spawn the `dot` command to render DOT source into the given format.
-fn render_dot(source: &str, format: GraphFormat) -> anyhow::Result> {
+/// Inject DOT graph-level style defaults (transparent background, teal nodes,
+/// gray edges, Helvetica font) right after the first `{` in the DOT source.
+/// Per-node/edge attributes override these defaults.
+fn inject_dot_style_defaults(source: &str) -> String {
+ let Some(pos) = source.find('{') else {
+ return source.to_string();
+ };
+ let (before, after) = source.split_at(pos + 1);
+ format!("{before}{DOT_STYLE_DEFAULTS}{after}")
+}
+
+/// Post-process raw SVG output from Graphviz:
+/// 1. Remove the white background `` element
+/// 2. Insert a dark-mode `