From 9e05dab74868ed7992991f9908d192b0a035f04c Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 20 Mar 2026 06:16:54 +0100 Subject: [PATCH] =?UTF-8?q?fix(etch):=20edges=20hidden=20behind=20containe?= =?UTF-8?q?rs=20=E2=80=94=20fix=20SVG=20render=20order?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render containers → edges → leaf nodes (was: edges → all nodes). Add port label margin to prevent clipping. Trace: skip Co-Authored-By: Claude Opus 4.6 (1M context) --- etch/src/svg.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/etch/src/svg.rs b/etch/src/svg.rs index a6e52d9..2b9f94d 100644 --- a/etch/src/svg.rs +++ b/etch/src/svg.rs @@ -66,7 +66,9 @@ impl Default for SvgOptions { /// The returned string is a complete, self-contained `` document /// suitable for embedding in HTML or writing to a `.svg` file. pub fn render_svg(layout: &GraphLayout, options: &SvgOptions) -> String { - let pad = options.padding; + // Extra padding for port labels that extend beyond node boundaries. + let port_label_margin = 60.0; + let pad = options.padding + port_label_margin; let vb_w = layout.width + pad * 2.0; let vb_h = layout.height + pad * 2.0; @@ -99,11 +101,12 @@ pub fn render_svg(layout: &GraphLayout, options: &SvgOptions) -> String { // Translate everything by padding. writeln!(svg, " ").unwrap(); - // Edges layer. + // Render order: containers → edges → leaf nodes. + // Containers are backgrounds; edges paint on top of them; + // leaf nodes paint on top of edges. + write_nodes(&mut svg, layout, options, true); // containers only write_edges(&mut svg, layout); - - // Nodes layer. - write_nodes(&mut svg, layout, options); + write_nodes(&mut svg, layout, options, false); // leaves only svg.push_str(" \n"); svg.push_str("\n"); @@ -216,18 +219,13 @@ fn write_edges(svg: &mut String, layout: &GraphLayout) { svg.push_str(" \n"); } -fn write_nodes(svg: &mut String, layout: &GraphLayout, options: &SvgOptions) { - svg.push_str(" \n"); +fn write_nodes(svg: &mut String, layout: &GraphLayout, options: &SvgOptions, containers: bool) { + let class = if containers { "containers" } else { "nodes" }; + writeln!(svg, " ").unwrap(); let default_fill = "#e8e8e8".to_string(); - // Draw containers first (background), then leaf nodes on top. - let containers: Vec<&crate::layout::LayoutNode> = - layout.nodes.iter().filter(|n| n.is_container).collect(); - let leaves: Vec<&crate::layout::LayoutNode> = - layout.nodes.iter().filter(|n| !n.is_container).collect(); - - for node in containers.iter().chain(leaves.iter()) { + for node in layout.nodes.iter().filter(|n| n.is_container == containers) { let fill = options .type_colors .get(&node.node_type)