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

commit2909633374306dcf2474b04ce77f6a63c65d1729
parentba95d72f31
authorLucas Galante <[email protected]>
date2026-09-01 11:10
feat: the viewport follows the active editor's level

The scene walk (and the OpenCL status probe and meta overlays with it)
starts at the param_editor's directory — whichever network editor took
the last node click — so the viewport, parameters pane and spreadsheet
all agree on what is being looked at. Re-scoping triggers: switching
editors by click rebuilds the scene, and the second editor's dive and
breadcrumb navigation rebuild when it holds the viewport.

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

 src/app.rs    | 17 ++++++++++++++---
 src/render.rs | 15 ++++++++-------
 src/window.rs |  4 ++++
 3 files changed, 26 insertions(+), 10 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 8d03e9b..1ff113d 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -5520,12 +5520,19 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                             }
                             if i == crate::slots::CONTENT2_IDX {
                                 // A node click in the SECOND editor hands the
-                                // parameters pane to its selection.
-                                self.param_editor = crate::slots::CONTENT2_IDX;
+                                // parameters pane (and the viewport's level)
+                                // to it.
+                                if self.param_editor != crate::slots::CONTENT2_IDX {
+                                    self.param_editor = crate::slots::CONTENT2_IDX;
+                                    self.rebuild_scene_geometry();
+                                }
                                 self.sync_parameters_pane();
                             }
                             if i == CONTENT_IDX {
-                                self.param_editor = CONTENT_IDX;
+                                if self.param_editor != CONTENT_IDX {
+                                    self.param_editor = CONTENT_IDX;
+                                    self.rebuild_scene_geometry();
+                                }
                                 self.sync_parameters_pane();
                                 if let Some(slot_idx) = self.graph().selected_node() {
                                     let dir = self.current_dir();
@@ -5810,6 +5817,10 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                         if dir_idx < dir.children.len() && dir.children[dir_idx].is_enterable() {
                             self.current_path2.push(dir_idx);
                             self.sync_nodes();
+                            // The viewport tracks the active editor's level.
+                            if self.param_editor == crate::slots::CONTENT2_IDX {
+                                self.rebuild_scene_geometry();
+                            }
                             changed = true;
                         }
                     }
diff --git a/src/render.rs b/src/render.rs
index 8a8ddd7..0544334 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -861,11 +861,12 @@ impl State {
         let mut sim_cache = std::mem::take(&mut self.sim_cache);
         let geom = {
             let mut sim = crate::geometry::EvalSim::new(frame, start, &mut sim_cache);
-            // The viewport shows the network editor's current level, not the
-            // whole tree: the walk starts at the current directory while name
-            // resolution stays rooted at fs_root. Navigation re-scopes this
-            // through on_path_changed, which lands here.
-            network_sphere_vertices_with_errors(&self.fs_root, self.current_dir(), &mut ocl_error, &mut sim)
+            // The viewport shows the ACTIVE editor's level — whichever
+            // network editor took the last node click (the param_editor, so
+            // the viewport, params pane and spreadsheet agree on what is
+            // being looked at) — while name resolution stays rooted at
+            // fs_root. Navigation in either editor re-scopes this.
+            network_sphere_vertices_with_errors(&self.fs_root, self.param_editor_dir(), &mut ocl_error, &mut sim)
         };
         self.sim_cache = sim_cache;
 
@@ -881,7 +882,7 @@ impl State {
             false
         }
 
-        let displayed_opencl = has_visible_opencl(self.current_dir());
+        let displayed_opencl = has_visible_opencl(self.param_editor_dir());
         if let Some(e) = ocl_error {
             self.update_status_text(&format!("OpenCL Error: {}", e));
         } else if displayed_opencl {
@@ -908,7 +909,7 @@ impl State {
             let mut sim = crate::geometry::EvalSim::new(frame, start, &mut sim_cache);
             // Same scoping as the scene walk above: overlays annotate what is
             // on screen, so they walk the same current level.
-            collect_meta_overlays(&self.fs_root, self.current_dir(), self.meta_marker_size, self.meta_marker_color, &mut sim)
+            collect_meta_overlays(&self.fs_root, self.param_editor_dir(), self.meta_marker_size, self.meta_marker_color, &mut sim)
         };
         self.sim_cache = sim_cache;
         self.meta_marker_verts = markers;
diff --git a/src/window.rs b/src/window.rs
index 982ce59..e6ffa5a 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -67,6 +67,10 @@ impl State {
                     if seg < state.current_path2.len() {
                         state.current_path2.truncate(seg);
                         state.sync_nodes();
+                        // The viewport tracks the active editor's level.
+                        if state.param_editor == crate::slots::CONTENT2_IDX {
+                            state.rebuild_scene_geometry();
+                        }
                         changed = true;
                     }
                 }