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

commita08bda044648a10a62a0b99b6c3ef76cd141af35
parentefb0ce6cf2
authorLucas Galante <[email protected]>
date2026-09-01 10:22
feat: the plate corner menu groups its rows with separators

Engraved "-" rows (cce-ui@HEAD) divide the menu into window actions |
layout spans | tab switching | tab management, pushed lazily so a group
that contributes nothing leaves no orphaned line; a Separator action
keeps the row-index dispatch aligned and inert.

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

 src/plate_corner.rs | 35 +++++++++++++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

diff --git a/src/plate_corner.rs b/src/plate_corner.rs
index da96b76..88c8355 100644
--- a/src/plate_corner.rs
+++ b/src/plate_corner.rs
@@ -59,6 +59,9 @@ pub enum PlateMenuAction {
     SplitTab,
     /// Remove a closable pane (the second network editor) from the docks.
     CloseTab,
+    /// A "-" row: engraved, inert — keeps `plate_menu_actions` aligned with
+    /// the option rows so a click on the line dispatches nothing.
+    Separator,
 }
 
 impl State {
@@ -145,7 +148,19 @@ impl State {
             return;
         }
 
+        // Group boundaries are engraved separators ("-" rows — the toolkit
+        // convention): window actions | layout spans | tab switching | tab
+        // management. Pushed lazily so a group that contributes nothing
+        // leaves no orphaned line.
+        let mut separate = |options: &mut Vec<String>, actions: &mut Vec<PlateMenuAction>| {
+            if !options.is_empty() && options.last().map(String::as_str) != Some("-") {
+                options.push("-".to_string());
+                actions.push(PlateMenuAction::Separator);
+            }
+        };
+
         if idx == SPREADSHEET_IDX && !self.collapsed_panes[idx] {
+            separate(&mut options, &mut actions);
             // Layout spans: full-width is the playbar treatment; the neighbors'
             // bottoms rise to make room via the tuck interlock.
             if !(self.spreadsheet_tucks_left() && self.spreadsheet_tucks_right()) {
@@ -163,26 +178,41 @@ impl State {
         // can be pulled in as tabs; a pane sharing its dock can move back
         // out to the empty dock its arrival left behind.
         if let Some(d) = self.dock_of_pane(idx) {
-            for &t in &self.dock_tabs[d as usize] {
-                if t != idx {
+            let switch_rows: Vec<usize> =
+                self.dock_tabs[d as usize].iter().copied().filter(|&t| t != idx).collect();
+            if !switch_rows.is_empty() {
+                separate(&mut options, &mut actions);
+                for t in switch_rows {
                     options.push(format!("Tab: {}", plate_title(t)));
                     actions.push(PlateMenuAction::ShowTab(t));
                 }
             }
+            let mut managed = false;
             for other in TAB_CANDIDATES {
                 if other != idx
                     && !self.dock_tabs[d as usize].contains(&other)
                     && !self.pane_is_detached(other)
                 {
+                    if !managed {
+                        separate(&mut options, &mut actions);
+                        managed = true;
+                    }
                     options.push(format!("Add Tab: {}", plate_title(other)));
                     actions.push(PlateMenuAction::AddTab(other));
                 }
             }
             if self.dock_tabs[d as usize].len() > 1 {
+                if !managed {
+                    separate(&mut options, &mut actions);
+                    managed = true;
+                }
                 options.push("Move To Own Plate".to_string());
                 actions.push(PlateMenuAction::SplitTab);
             }
             if idx == NETWORK_PANEL2_IDX {
+                if !managed {
+                    separate(&mut options, &mut actions);
+                }
                 options.push("Close Tab".to_string());
                 actions.push(PlateMenuAction::CloseTab);
             }
@@ -244,6 +274,7 @@ impl State {
             }
             PlateMenuAction::SplitTab => self.split_dock_tab(idx),
             PlateMenuAction::CloseTab => self.close_dock_tab(idx),
+            PlateMenuAction::Separator => {}
         }
     }