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

commit724c32cab561e3e3daece3f5867bd6ff061fd953
parent5192a3d142
authorLucas Galante <[email protected]>
date2026-07-21 12:54
refactor: remove the dormant in-app NodePalette widget

Dead since the add-node palette became a cce-cloud popup: the widget
slot (NODE_PALETTE_IDX — SPREADSHEET/SPREADSHEET_MENUBAR/NETWORK_PANEL/
PLAYBAR indexes renumbered, WIDGET_COUNT 17 -> 16; no slot index is
persisted), the NodePalette struct and its Layout/Paint/Input impls,
the palette query/selection state and its keyboard handler, and the
unconsumed WidgetSlots::dyn_refs. The modal dialog_open guards in the
wheel/button paths collapse to their pane conditions.
grid_cursor_col/row stay — the popup's AddNode placement reads them.

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

 src/app.rs | 282 +++----------------------------------------------------------
 1 file changed, 12 insertions(+), 270 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 14bf5cb..ac50c49 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -83,13 +83,12 @@ pub const RIGHT_MENUBAR_IDX: usize = 8;
 pub const PARAM_MENUBAR_IDX: usize = 9;
 pub const STATUS_IDX: usize = 10;
 pub const BREADCRUMB_IDX: usize = 11;
-pub const NODE_PALETTE_IDX: usize = 12;
-pub const SPREADSHEET_IDX: usize = 13;
-pub const SPREADSHEET_MENUBAR_IDX: usize = 14;
-pub const NETWORK_PANEL_IDX: usize = 15;
-pub const PLAYBAR_IDX: usize = 16;
+pub const SPREADSHEET_IDX: usize = 12;
+pub const SPREADSHEET_MENUBAR_IDX: usize = 13;
+pub const NETWORK_PANEL_IDX: usize = 14;
+pub const PLAYBAR_IDX: usize = 15;
 
-pub const WIDGET_COUNT: usize = 17;
+pub const WIDGET_COUNT: usize = 16;
 
 /// The roster, concretely typed (Phase 6bb): every slot's type is statically known — the
 /// old `Vec<Box<dyn WidgetHost>>` erased that and pinned `WidgetHost`'s full surface through the
@@ -110,7 +109,6 @@ pub struct WidgetSlots {
     pub param_menubar: Adapted<MenuBar>,
     pub status: Adapted<StatusBar>,
     pub breadcrumb: Adapted<Breadcrumb>,
-    pub node_palette: Adapted<NodePalette>,
     pub spreadsheet: Adapted<Spreadsheet>,
     pub spreadsheet_menubar: Adapted<MenuBar>,
     pub network_panel: Adapted<PassivePlate>,
@@ -136,7 +134,6 @@ impl WidgetSlots {
             PARAM_MENUBAR_IDX => self.param_menubar.draggable(),
             STATUS_IDX => self.status.draggable(),
             BREADCRUMB_IDX => self.breadcrumb.draggable(),
-            NODE_PALETTE_IDX => self.node_palette.draggable(),
             SPREADSHEET_IDX => self.spreadsheet.draggable(),
             SPREADSHEET_MENUBAR_IDX => self.spreadsheet_menubar.draggable(),
             NETWORK_PANEL_IDX => self.network_panel.draggable(),
@@ -159,7 +156,6 @@ impl WidgetSlots {
             PARAM_MENUBAR_IDX => self.param_menubar.is_dragging(),
             STATUS_IDX => self.status.is_dragging(),
             BREADCRUMB_IDX => self.breadcrumb.is_dragging(),
-            NODE_PALETTE_IDX => self.node_palette.is_dragging(),
             SPREADSHEET_IDX => self.spreadsheet.is_dragging(),
             SPREADSHEET_MENUBAR_IDX => self.spreadsheet_menubar.is_dragging(),
             NETWORK_PANEL_IDX => self.network_panel.is_dragging(),
@@ -182,7 +178,6 @@ impl WidgetSlots {
             PARAM_MENUBAR_IDX => &self.param_menubar,
             STATUS_IDX => &self.status,
             BREADCRUMB_IDX => &self.breadcrumb,
-            NODE_PALETTE_IDX => &self.node_palette,
             SPREADSHEET_IDX => &self.spreadsheet,
             SPREADSHEET_MENUBAR_IDX => &self.spreadsheet_menubar,
             NETWORK_PANEL_IDX => &self.network_panel,
@@ -205,7 +200,6 @@ impl WidgetSlots {
             PARAM_MENUBAR_IDX => &mut self.param_menubar,
             STATUS_IDX => &mut self.status,
             BREADCRUMB_IDX => &mut self.breadcrumb,
-            NODE_PALETTE_IDX => &mut self.node_palette,
             SPREADSHEET_IDX => &mut self.spreadsheet,
             SPREADSHEET_MENUBAR_IDX => &mut self.spreadsheet_menubar,
             NETWORK_PANEL_IDX => &mut self.network_panel,
@@ -213,11 +207,6 @@ impl WidgetSlots {
             _ => panic!("widget slot index out of range: {idx}"),
         }
     }
-
-    /// Per-slot dyn view in index order (the serialize path's input).
-    pub fn dyn_refs(&self) -> [&dyn WidgetHost; WIDGET_COUNT] {
-        [&self.header, &self.content, &self.splitter1, &self.viewport, &self.splitter2, &self.param, &self.canvas, &self.left_menubar, &self.right_menubar, &self.param_menubar, &self.status, &self.breadcrumb, &self.node_palette, &self.spreadsheet, &self.spreadsheet_menubar, &self.network_panel, &self.playbar]
-    }
 }
 
 
@@ -632,34 +621,6 @@ impl DesignSettings {
     }
 }
 
-pub struct NodePalette {
-    pub x: f32, y: f32, w: f32, h: f32,
-    pub visible: bool,
-    pub query: String,
-    pub items: Vec<String>,
-    pub selected: usize,
-}
-
-impl NodePalette {
-    fn new() -> cce_ui::widget::Adapted<NodePalette> {
-        cce_ui::widget::Adapted::new(Self { x: 0.0, y: 0.0, w: 0.0, h: 0.0, visible: false, query: String::new(), items: Vec::new(), selected: 0 })
-    }
-
-    fn panel_rect(&self) -> (f32, f32, f32, f32) {
-        let pw = 460.0_f32.min(self.w - 40.0).max(280.0);
-        let ph = 360.0_f32.min(self.h - 80.0).max(240.0);
-        (self.x + (self.w - pw) / 2.0, self.y + 76.0, pw, ph)
-    }
-
-    pub fn set_palette_state(&mut self, visible: bool, query: &str, items: &[String], selected: usize) {
-        self.visible = visible;
-        self.query = query.to_string();
-        self.items = items.to_vec();
-        self.selected = selected;
-    }
-}
-
-
 /// Dissolved cce-ui `Plate` (Phase 6as): a passive translucent panel — configured color
 /// at `plate_opacity` times the network fade, alpha negated when blur is on (the
 /// scenefx blur marker) — with no children and no events.
@@ -757,60 +718,6 @@ impl cce_ui::widget::Input for Canvas {
     fn hit(&self, _rect: cce_ui::scene::layout::Rect, _x: f32, _y: f32) -> bool { false }
 }
 
-impl cce_ui::widget::Layout for NodePalette {
-    // The model mirrors the landed rect (panel_rect/labels read it between events).
-    fn rect_assigned(&mut self, rect: cce_ui::scene::layout::Rect) {
-        self.x = rect.x;
-        self.y = rect.y;
-        self.w = rect.width;
-        self.h = rect.height;
-    }
-
-    fn z_order(&self) -> i32 {
-        200
-    }
-}
-
-impl cce_ui::widget::Paint for NodePalette {
-    fn color(&self) -> [f32; 4] { [0.0, 0.0, 0.0, 0.0] }
-
-    fn paint(&self, _rect: cce_ui::scene::layout::Rect, pc: &mut cce_ui::scene::paint::PaintCtx) {
-        use cce_ui::scene::layout::Rect;
-        // Visibility is model-owned (set through set_palette_state on the downcast model,
-        // not WidgetHost::set_visible on the wrapper), so the paint gate lives here.
-        if !self.visible {
-            return;
-        }
-        pc.quad(Rect { x: self.x, y: self.y, width: self.w, height: self.h }, [0.0, 0.0, 0.0, 0.45]);
-        let (px, py, pw, ph) = self.panel_rect();
-        pc.quad(Rect { x: px, y: py, width: pw, height: ph }, colors::popover_bg_color());
-        pc.quad(Rect { x: px + 16.0, y: py + 48.0, width: pw - 32.0, height: 32.0 }, [0.10, 0.10, 0.14, 1.0]);
-        let list_y = py + 92.0;
-        let row_h = 24.0;
-        let visible_rows = ((ph - 120.0) / row_h).floor().max(0.0) as usize;
-        let start = self.selected.saturating_sub(visible_rows.saturating_sub(1));
-        for (row, item_idx) in (start..self.items.len().min(start + visible_rows)).enumerate() {
-            let y = list_y + row as f32 * row_h;
-            let bg = if item_idx == self.selected { [0.24, 0.33, 0.55, 0.85] } else { [0.14, 0.14, 0.19, 0.55] };
-            pc.quad(Rect { x: px + 16.0, y, width: pw - 32.0, height: row_h - 2.0 }, bg);
-        }
-        for l in self.own_labels() {
-            pc.text(l.text, l.x, l.y, l.font_size, l.color);
-        }
-    }
-}
-
-impl cce_ui::widget::Input for NodePalette {
-    fn hit(&self, rect: cce_ui::scene::layout::Rect, x: f32, y: f32) -> bool {
-        self.visible && x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height
-    }
-
-    fn take_click(&mut self) -> bool {
-        self.visible
-    }
-}
-
-
 pub fn get_next_visible_pane(
     current_pane: usize,
     show_network: bool,
@@ -941,10 +848,6 @@ pub struct State {
     pub slots: Box<WidgetSlots>,
     pub positions: Vec<(f32, f32, f32, f32)>,
     pub splitter_layout: cce_ui::layout::SplitterLayout,
-    pub node_palette_visible: bool,
-    pub node_palette_query: String,
-    pub node_palette_filtered: Vec<usize>,
-    pub node_palette_selected: usize,
     /// The add-node palette's cce-cloud popup (single active popup, toggle
     /// semantics — the status bar's tracker pattern).
     pub cloud_popups: cce_ui::process::CloudPopupTracker,
@@ -1119,13 +1022,6 @@ impl State {
         self.slots.get_dyn(idx).as_any().downcast_ref::<MenuBar>()
     }
 
-    pub fn palette(&self) -> &NodePalette {
-        self.slots.node_palette.as_any().downcast_ref::<NodePalette>().expect("not a NodePalette")
-    }
-
-    pub fn palette_mut(&mut self) -> &mut NodePalette {
-        self.slots.node_palette.as_any_mut().downcast_mut::<NodePalette>().expect("not a NodePalette")
-    }
 
     // Roster accessors on CONCRETE types (Phase 6aw, controller decision option 2): each
     // index's type is known statically, so the capability traits are reached by downcast +
@@ -1876,30 +1772,6 @@ impl State {
         names
     }
 
-    pub fn refresh_node_palette(&mut self) {
-        let q = self.node_palette_query.to_lowercase();
-        self.node_palette_filtered = self.node_templates.iter().enumerate()
-            .filter_map(|(i, t)| {
-                if q.is_empty() || t.label.to_lowercase().contains(&q) { Some(i) } else { None }
-            })
-            .collect();
-        if self.node_palette_selected >= self.node_palette_filtered.len() {
-            self.node_palette_selected = self.node_palette_filtered.len().saturating_sub(1);
-        }
-        let items: Vec<String> = self.node_palette_filtered.iter()
-            .map(|&i| self.node_templates[i].label.clone())
-            .collect();
-        let visible = self.node_palette_visible;
-        let query = self.node_palette_query.clone();
-        let selected = self.node_palette_selected;
-        self.palette_mut().set_palette_state(
-            visible,
-            &query,
-            &items,
-            selected,
-        );
-    }
-
     pub fn open_file_chooser(&self) {
         let Some(sender) = self.event_sender.clone() else { return };
         std::thread::spawn(move || {
@@ -2032,8 +1904,7 @@ impl State {
         });
     }
 
-    /// The add-node palette, as a `cce-cloud --dmenu` popup (the in-app
-    /// NodePalette widget is retired from this path): toggle-tracked like the
+    /// The add-node palette, as a `cce-cloud --dmenu` popup: toggle-tracked like the
     /// status bar's popups, positioned at the pointer and parented to the
     /// designer surface. The picked template comes back through the event
     /// loop as a fire-and-forget `AddNode` at the grid cursor.
@@ -2077,81 +1948,6 @@ impl State {
         });
     }
 
-    pub fn close_node_palette(&mut self) {
-        self.node_palette_visible = false;
-        self.refresh_node_palette();
-    }
-
-
-    pub fn place_selected_node(&mut self) -> bool {
-        let Some(&template_idx) = self.node_palette_filtered.get(self.node_palette_selected) else { return false; };
-        let mut node = self.node_templates[template_idx].node.clone();
-        let is_in_utility = !self.current_path.is_empty() && self.fs_root.children[self.current_path[0]].node_type == "utility";
-        if is_in_utility {
-            if crate::geometry::is_geometry_node_type(&node.node_type) {
-                self.update_status_text("Utility nodes cannot contain geometry.");
-                self.close_node_palette();
-                return false;
-            }
-        }
-        let (nx, ny) = self.find_empty_cell(self.grid_cursor_col as f32, self.grid_cursor_row as f32, None);
-        node.position = (nx, ny);
-        node.name = self.get_lowest_unused_name(&node.name);
-        self.current_dir_mut().children.push(node);
-        self.close_node_palette();
-        self.sync_nodes();
-        self.rebuild_positions();
-        self.apply_layout();
-        self.update_panel_bounds();
-        self.rebuild_scene_geometry();
-        true
-    }
-
-
-    pub fn handle_node_palette_key(&mut self, event: &KeyEvent) -> bool {
-        if event.state != ElementState::Pressed { return false; }
-        match &event.logical_key {
-            Key::Named(NamedKey::Escape) => {
-                self.close_node_palette();
-                true
-            }
-            Key::Named(NamedKey::Enter) => self.place_selected_node(),
-            Key::Named(NamedKey::ArrowDown) => {
-                if !self.node_palette_filtered.is_empty() {
-                    self.node_palette_selected = (self.node_palette_selected + 1).min(self.node_palette_filtered.len() - 1);
-                    self.refresh_node_palette();
-                }
-                true
-            }
-            Key::Named(NamedKey::ArrowUp) => {
-                if self.node_palette_selected > 0 {
-                    self.node_palette_selected -= 1;
-                    self.refresh_node_palette();
-                }
-                true
-            }
-            Key::Named(NamedKey::Backspace) => {
-                self.node_palette_query.pop();
-                self.refresh_node_palette();
-                true
-            }
-            Key::Named(NamedKey::Tab) => {
-                self.close_node_palette();
-                true
-            }
-            _ => {
-                if let Some(text) = &event.text {
-                    for ch in text.chars().filter(|c| !c.is_control()) {
-                        self.node_palette_query.push(ch);
-                    }
-                    self.refresh_node_palette();
-                    true
-                } else {
-                    false
-                }
-            }
-        }
-    }
 
 pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String>>) {
     let mut headers = vec![
@@ -2594,7 +2390,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             param_menubar: MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("2: Parameters").with_label("Parameters Menu Bar").with_item("Preset", &["Default", "Custom"]).with_item("Reset", &["All"]).with_item("View", &["Close Pane"]).with_context_options(context_opts.clone(), 2),
             status: StatusBar::new().with_text("Ready"),
             breadcrumb: Breadcrumb::new(),
-            node_palette: NodePalette::new(),
             spreadsheet: Spreadsheet::new(),
             spreadsheet_menubar: {
                 let mut mb = MenuBar::new(0.0, 0.0, 0.0, MENUBAR_H).with_title("3: Spreadsheet").with_label("Spreadsheet Menu Bar").with_item("View", &["Close Pane"]).with_context_options(context_opts.clone(), 3);
@@ -2682,10 +2477,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             slots,
             positions,
             splitter_layout,
-            node_palette_visible: false,
-            node_palette_query: String::new(),
-            node_palette_filtered: Vec::new(),
-            node_palette_selected: 0,
             cloud_popups: cce_ui::process::CloudPopupTracker::new(),
             drag_widget: None,
             focused_widget: None,
@@ -3084,7 +2875,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             self.positions[CANVAS_IDX] = (0.0, 0.0, 0.0, 0.0);
             self.positions[PARAM_MENUBAR_IDX] = (0.0, 0.0, 0.0, 0.0);
             self.positions[STATUS_IDX] = (0.0, 0.0, 0.0, 0.0);
-            self.positions[NODE_PALETTE_IDX] = (0.0, 0.0, self.width, self.height);
             self.positions[PLAYBAR_IDX] = (0.0, 0.0, 0.0, 0.0);
 
             self.slots.header.set_visible(false);
@@ -3179,7 +2969,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
             self.positions[CANVAS_IDX] = (0.0, HEADER_H, self.width, body_h);
             self.positions[PARAM_MENUBAR_IDX] = (col_r_x, HEADER_H, col_r_w, if right_visible { MENUBAR_H } else { 0.0 });
             self.positions[STATUS_IDX] = (0.0, self.height - STATUS_H, self.width, STATUS_H);
-            self.positions[NODE_PALETTE_IDX] = (0.0, 0.0, self.width, self.height);
             self.positions[PLAYBAR_IDX] = (0.0, HEADER_H + body_h, self.width, pb_h);
 
             self.slots.header.set_visible(true);
@@ -3293,7 +3082,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                 self.positions[CANVAS_IDX] = (0.0, 0.0, self.width, body_h);
                 self.positions[PARAM_MENUBAR_IDX] = (0.0, 0.0, 0.0, 0.0);
                 self.positions[STATUS_IDX] = (0.0, self.height - STATUS_H, self.width, STATUS_H);
-                self.positions[NODE_PALETTE_IDX] = (0.0, 0.0, self.width, self.height);
                 self.positions[PLAYBAR_IDX] = (0.0, body_h, self.width, pb_h);
 
                 self.slots.header.set_visible(false);
@@ -3387,7 +3175,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                 self.positions[CANVAS_IDX] = (0.0, 0.0, self.width, body_h);
                 self.positions[PARAM_MENUBAR_IDX] = (0.0, 0.0, 0.0, 0.0);
                 self.positions[STATUS_IDX] = (0.0, self.height - STATUS_H, self.width, STATUS_H);
-                self.positions[NODE_PALETTE_IDX] = (0.0, 0.0, self.width, self.height);
                 self.positions[PLAYBAR_IDX] = if self.show_playbar {
                     (gap, self.height - STATUS_H - gap - PLAYBAR_H, self.width - 2.0 * gap, PLAYBAR_H)
                 } else {
@@ -3742,7 +3529,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
     pub fn handle_event(&mut self, event: &WindowEvent) -> bool {
         match event {
             WindowEvent::MouseWheel { delta, phase, .. } => {
-                let dialog_open = self.node_palette_visible;
                 let in_network_pane = self.in_network_pane();
                 // eprintln!("DEBUG MOUSEWHEEL: delta={:?}, phase={:?}, cursor=({}, {}), in_network_pane={}", delta, phase, self.cursor_x, self.cursor_y, in_network_pane);
                 let node_area_y = self.positions[CONTENT_IDX].1;
@@ -3754,7 +3540,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
 
                 let mut focus_changed = false;
                 let mut new_pane = None;
-                if !dialog_open {
+                {
                     if in_network_pane {
                         new_pane = Some(LEFT_MENUBAR_IDX);
                     } else if in_viewport {
@@ -3777,7 +3563,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
 
                 let mut handled = false;
                 let mut needs_sync_grid = false;
-                if !dialog_open && !self.modifiers.control_key() {
+                if !self.modifiers.control_key() {
                     // Routed (6bd shrink): propagate_event owns the scroll-gesture
                     // bookkeeping this loop used to hand-roll. The wheel is the only
                     // designer surface routed so far — the press/move cascade stays
@@ -3855,7 +3641,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                     self.sync_layout();
                     self.read_panel_offsets();
                     true
-                } else if !dialog_open && in_network_pane {
+                } else if in_network_pane {
                     if self.modifiers.control_key() {
                         let factor = match delta {
                             MouseScrollDelta::LineDelta(_x, y) => {
@@ -4049,22 +3835,11 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
 
                     self.viewport_mut().reset_velocity();
                 }
-                if self.node_palette_visible {
-                    if *btn_state == ElementState::Pressed {
-                        let (px, py, pw, ph) = self.palette().panel_rect();
-                        if self.cursor_x < px || self.cursor_x > px + pw || self.cursor_y < py || self.cursor_y > py + ph {
-                            self.close_node_palette();
-                        }
-                    }
-                    return true;
-                }
-                let dialog_open = self.node_palette_visible;
                 let in_network_pane = self.in_network_pane();
                 let node_area_x = self.positions[CONTENT_IDX].0;
                 let node_area_y = self.positions[CONTENT_IDX].1;
 
-                let is_pan_trigger = !dialog_open
-                    && in_network_pane
+                let is_pan_trigger = in_network_pane
                     && (*button == MouseButton::Middle
                         || (*button == MouseButton::Left && self.space_pressed));
 
@@ -4241,7 +4016,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                         }
 
                         if *button == MouseButton::Right {
-                            if !dialog_open && in_circle_network_pane {
+                            if in_circle_network_pane {
                                 let col = ((self.cursor_x - node_area_x - self.pan_x) / (self.grid_size_x + self.gap_col_w)).floor() as i32;
                                 let row = ((self.cursor_y - node_area_y - self.pan_y) / (self.grid_size_y + self.gap_row_h)).floor() as i32;
                                 self.grid_cursor_col = col;
@@ -4344,7 +4119,7 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                             self.slots.param.unfocus();
                             self.sync_parameters_to_project();
                         }
-                        if click_target.is_none() && !dialog_open && in_circle_network_pane {
+                        if click_target.is_none() && in_circle_network_pane {
                             let col = ((self.cursor_x - node_area_x - self.pan_x) / (self.grid_size_x + self.gap_col_w)).floor() as i32;
                             let row = ((self.cursor_y - node_area_y - self.pan_y) / (self.grid_size_y + self.gap_row_h)).floor() as i32;
                             self.grid_cursor_col = col;
@@ -4545,10 +4320,6 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
                     return true;
                 }
 
-                if self.node_palette_visible {
-                    return self.handle_node_palette_key(event);
-                }
-
                 if event.state == ElementState::Pressed && event.logical_key == Key::Named(NamedKey::Escape) {
                     self.graph_mut().cancel_connecting();
                     return true;
@@ -5305,32 +5076,3 @@ pub(crate) fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec
     }
 }
 
-impl NodePalette {
-    fn own_labels(&self) -> Vec<TextLabel> {
-        if !self.visible { return Vec::new(); }
-        let (px, py, _pw, ph) = self.panel_rect();
-        let mut labels = Vec::new();
-        labels.push(TextLabel { text: "Add Node".into(), x: px + 16.0, y: py + 14.0, font_size: 15.0, color: [0xdd, 0xdd, 0xe6] });
-        labels.push(TextLabel { text: "Type to search, Enter to place, Esc to close".into(), x: px + 100.0, y: py + 17.0, font_size: 11.0, color: [0x88, 0x88, 0x99] });
-        let query = if self.query.is_empty() { "Search nodes...".to_string() } else { self.query.clone() };
-        let query_color = if self.query.is_empty() { [0x66, 0x66, 0x77] } else { [0xdd, 0xdd, 0xe6] };
-        labels.push(TextLabel { text: query, x: px + 28.0, y: py + 57.0, font_size: 13.0, color: query_color });
-        let row_h = 24.0;
-        let visible_rows = ((ph - 120.0) / row_h).floor().max(0.0) as usize;
-        if self.items.is_empty() {
-            labels.push(TextLabel { text: "No matching nodes".into(), x: px + 28.0, y: py + 100.0, font_size: 12.0, color: [0xaa, 0xaa, 0xbb] });
-        } else {
-            let start = self.selected.saturating_sub(visible_rows.saturating_sub(1));
-            for (row, item_idx) in (start..self.items.len().min(start + visible_rows)).enumerate() {
-                labels.push(TextLabel {
-                    text: self.items[item_idx].clone(),
-                    x: px + 28.0,
-                    y: py + 98.0 + row as f32 * row_h,
-                    font_size: 12.0,
-                    color: if item_idx == self.selected { [0xff, 0xff, 0xdd] } else { [0xcc, 0xcc, 0xd8] },
-                });
-            }
-        }
-        labels
-    }
-}