git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit0acc130660231d3e49b76184cca9917e31beaae4
parent94cb374ccb
authorLucas Galante <[email protected]>
date2026-09-02 13:12
feat: added nodes start with their display flag off

Under the one-visible-per-directory rule, showing geometry is an
explicit act ('e' or the click toggle), never a side effect of adding:
both add routes — AddNode (palette/MCP/menu) and Ctrl+V paste — now
clear the new node's top-level geometry_visible. A subnet template's
internal chain keeps its own flags, so the node displays normally once
enabled. The serde default stays true so existing saves load unchanged.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/app.rs    |  4 ++++
 src/main.rs   | 22 ++++++++++++++++++++++
 src/window.rs |  6 ++++++
 3 files changed, 32 insertions(+)

diff --git a/src/app.rs b/src/app.rs
index 89392c5..1d40728 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -6364,6 +6364,10 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                                                 let start_y = self.grid_cursor_row as f32;
                                                 let (nx, ny) = self.find_empty_cell(start_x, start_y, None);
                                                 node.position = (nx, ny);
+                                                // Added = hidden, same as AddNode: a
+                                                // paste of a displayed node must not
+                                                // become a second visible sibling.
+                                                node.geometry_visible = false;
                                                 self.current_dir_mut().children.push(node);
                                                 self.grid_cursor_col = nx as i32;
                                                 self.grid_cursor_row = ny as i32;
diff --git a/src/main.rs b/src/main.rs
index fe7ba7a..f9d6c90 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -182,6 +182,28 @@ mod tests {
         assert!(dir.children[2].geometry_visible);
     }
 
+    /// A newly added node arrives with its display flag OFF: under the
+    /// one-visible-per-directory rule, showing geometry is an explicit act,
+    /// never a side effect of adding. Template children keep their own flags
+    /// (a subnet's internal chain still displays once the parent is enabled).
+    #[test]
+    fn test_added_nodes_start_hidden() {
+        let mut state = State::new(false);
+        let mut redraw = false;
+        state
+            .apply_action(
+                McpAction::AddNode { template_name: "Sphere".to_string(), name: None, x: 5.0, y: 5.0 },
+                &mut redraw,
+            )
+            .expect("add sphere node");
+        let added = state.current_dir().children.last().unwrap();
+        assert!(!added.geometry_visible, "added node must start hidden");
+        assert!(
+            added.children.iter().any(|c| c.geometry_visible),
+            "the template's internal chain must keep its own flags"
+        );
+    }
+
     /// The button must exist on Main, inside the File section, before Exit.
     #[test]
     fn test_main_node_offers_set_as_default() {
diff --git a/src/window.rs b/src/window.rs
index 1ada59a..7e45284 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -815,6 +815,12 @@ impl State {
                             node.name = state.get_lowest_unused_name(&node.name);
                         }
                         crate::app::ensure_meta_on(&mut node);
+                        // New nodes arrive with their display flag OFF: the
+                        // one-visible-per-directory rule means showing is an
+                        // explicit act ('e', the click toggle), never a side
+                        // effect of adding. Top-level flag only — a subnet
+                        // template's internal chain keeps its own flags.
+                        node.geometry_visible = false;
                         state.current_dir_mut().children.push(node);
                         state.sync_nodes();
                         state.rebuild_positions();