git.lucas.co / cce-files
file manager
git clone https://git.lucas.co/cce-files.git

commit5d3b3acb88a355661bfededaeee6ea1aed8ea8a6
parentd4171e1786
authorLucas Galante <[email protected]>
date2026-09-24 14:49
Spacing: the ladder, not literals

The window-edge inset is root_plate_inset() (the roll plus the padding),
not root_plate_padding(): the padding alone left most of its run on the
roll, so the flat face at the edge read narrower than the gap between
the panes. Pane-level literals move onto the pane rung; per-control
offsets are marked.

main.rs
- layout(): pad = root_plate_padding() -> root_plate_inset() (the four
  window edges; 2*pad follows)
- select bar: bar_y inset and the carved band's height ->
  root_plate_inset(); the Cancel/Select gap 10 -> root_plate_gap()
- open_with_rects(): the dialog's 20px rim inset -> plate_padding()
  (well x/w, buttons' right edge and bottom, title/prompt x, title y);
  the 10px button gap -> plate_gap(); the 42/60 text drops stay,
  TODO(style)
- context menu: label side-padding 24 and lead-in 8 stay, TODO(style)
- select-bar button: left-justified label lead-in 8 stays, TODO(style)
- collapsed preview stub: "Preview" label inset 10 -> plate_padding()

preview_pane.rs
- "Select a file" placeholder 12/12 -> plate_padding()
- content box: rect_y 12 -> plate_padding(), rect_h -24 -> 2x; text x
  and first-line drop 12 -> plate_padding(); line budget cw-24 -> 2x;
  "No preview" 12/12 -> plate_padding()
- details: content start +12, icon/label x 12 -> plate_padding(); the
  8px drop before Target -> plate_gap(); header drop 6, columns 42/112,
  row pitch 20 stay, TODO(style)
- SectionContext 7/12 frame mirrors stay: deliberate

pages/browse.rs
- search strip: margin 6 and x inset 8 / width 16 -> plate_padding();
  count label 24/18 stays, TODO(style)

pages/space.rs, pages/network.rs
- breadcrumb|dropdown gap 12 -> root_plate_gap(); the 4/6/16 leftover
  inset stays, TODO(style)
- space.rs: the map's message text inset 12/12 -> plate_padding()

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

 src/main.rs          | 45 ++++++++++++++++++++++++++++++++-------------
 src/pages/browse.rs  | 13 +++++++++----
 src/pages/network.rs |  7 +++++--
 src/pages/space.rs   | 13 ++++++++-----
 src/preview_pane.rs  | 34 ++++++++++++++++++++++------------
 5 files changed, 76 insertions(+), 36 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 9957c70..0443736 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -30,6 +30,8 @@ fn context_menu_size(options: &[(String, Option<Message>)]) -> (f32, f32) {
         .iter()
         .map(|(s, _)| cce_ui::widget::display::measure_text_width(s, &family, size))
         .fold(0.0f32, f32::max);
+    // TODO(style): 24 is the menu's own label side-padding (12 a side), a
+    // control-level number the toolkit's context_menu should supply.
     let w = (widest + 24.0).max(MENU_MIN_W);
     let h = options.len() as f32 * ROW_H;
     (w, h)
@@ -51,14 +53,24 @@ fn open_with_rects(win_w: f32, win_h: f32) -> OpenWithRects {
     let y = (win_h - DIALOG_H) / 2.0;
     let tb_h = cce_ui::layout::textbox_height();
     let btn_h = cce_ui::layout::button_height();
+    // The dialog is a pane plate standing on the root: its content insets
+    // from the rim by the pane rung, and its two buttons sit one pane gap
+    // apart, right-aligned to that inset.
+    let pad = cce_ui::layout::plate_padding();
+    let gap = cce_ui::layout::plate_gap();
+    let (cancel_w, open_w) = (70.0, 80.0);
+    let btn_y = y + DIALOG_H - btn_h - pad;
+    let open_x = x + DIALOG_W - pad - open_w;
     OpenWithRects {
         x,
         y,
         w: DIALOG_W,
         h: DIALOG_H,
-        tb: (x + 20.0, y + 60.0, DIALOG_W - 40.0, tb_h),
-        cancel: (x + DIALOG_W - 180.0, y + DIALOG_H - btn_h - 16.0, 70.0, btn_h),
-        open: (x + DIALOG_W - 100.0, y + DIALOG_H - btn_h - 16.0, 80.0, btn_h),
+        // TODO(style): the 60px drop to the well (title, then the prompt at
+        // 42) is the dialog's own text rhythm, not a rung.
+        tb: (x + pad, y + 60.0, DIALOG_W - 2.0 * pad, tb_h),
+        cancel: (open_x - gap - cancel_w, btn_y, cancel_w, btn_h),
+        open: (open_x, btn_y, open_w, btn_h),
     }
 }
 
@@ -603,8 +615,10 @@ impl FilesystemApp {
 
         let has_sidebar = false;
         let sidebar_w = if has_sidebar { self.paginator.sidebar_w() } else { 0.0 };
-        // DE-wide plate rim padding (style.surface.root plate.padding).
-        let pad = cce_ui::layout::root_plate_padding();
+        // Inset from the WINDOW edge: the root plate's roll plus its padding
+        // (the padding alone left most of the run on the roll, so the edge
+        // read narrower than the gap between the panes).
+        let pad = cce_ui::layout::root_plate_inset();
         let browse_x = if has_sidebar { sidebar_w + pad + 1.0 } else { pad };
         let usable_w = self.width as f32 - sidebar_w - (if has_sidebar { 1.0 } else { 0.0 }) - 2.0 * pad;
         let content_y = pad;
@@ -758,7 +772,7 @@ impl FilesystemApp {
         // content region, so it goes into window_pc: page content (pc) is clipped
         // to the viewport and would swallow the bar entirely.
         if self.select_mode {
-            let bar_y = self.height as f32 - select_bar_h - cce_ui::layout::root_plate_padding();
+            let bar_y = self.height as f32 - select_bar_h - cce_ui::layout::root_plate_inset();
             // Divider line — under control_relief the bar is a band carved into the
             // plate (see display_list), so the flat line is the fallback only.
             if !cce_ui::layout::control_relief() {
@@ -772,7 +786,7 @@ impl FilesystemApp {
             // the content region with the bar's own padding — and the
             // toolkit's plain button face, not hand-picked red/green tints.
             let btn_w = 84.0;
-            let gap = 10.0;
+            let gap = cce_ui::layout::root_plate_gap();
             // Flush with the panels' right edge — an extra inset here left the
             // buttons hanging short of the column above them.
             let confirm_x = browse_x + usable_w - btn_w;
@@ -850,6 +864,7 @@ impl FilesystemApp {
                 } else {
                     cce_ui::color::TEXT_FG
                 };
+                // TODO(style): the row label's 8px lead-in is a control-level inset.
                 context_menu_pc.text_with_font(opt, cx + 8.0, iy, menu_size, text_color, &menu_family);
             }
         }
@@ -870,10 +885,12 @@ impl FilesystemApp {
             // Dialog panel background
             dialog_pc.rect([0.08, 0.08, 0.12, 1.0], dialog_x + 1.0, dialog_y + 1.0, dialog_w - 2.0, dialog_h - 2.0);
 
-            // Title text
-            dialog_pc.text("Open with...", dialog_x + 20.0, dialog_y + 20.0, 14.0, [1.0, 1.0, 1.0, 1.0]);
-            // Description
-            dialog_pc.text("Enter command:", dialog_x + 20.0, dialog_y + 42.0, 11.0, [0.54, 0.54, 0.58, 1.0]);
+            // Title and prompt, inset from the plate rim by the pane rung
+            // (the same inset the well and buttons take in open_with_rects).
+            let dialog_pad = cce_ui::layout::plate_padding();
+            dialog_pc.text("Open with...", dialog_x + dialog_pad, dialog_y + dialog_pad, 14.0, [1.0, 1.0, 1.0, 1.0]);
+            // TODO(style): the prompt's 42px drop is the dialog's text rhythm.
+            dialog_pc.text("Enter command:", dialog_x + dialog_pad, dialog_y + 42.0, 11.0, [0.54, 0.54, 0.58, 1.0]);
 
             // Set textbox position dynamically using configured textbox height
             textbox.set_rect(tb_x, tb_y, tb_w, tb_h);
@@ -1068,6 +1085,8 @@ impl FilesystemApp {
                 // toolkit's convention for every other text-bearing control.
                 let text_w = cce_ui::widget::display::measure_text_width(label, &label_family, label_size);
                 let text_x = if btn.justify == cce_ui::widget::Justification::Left {
+                    // TODO(style): a left-justified button label's lead-in is a
+                    // control-level inset (Button's own, not a rung).
                     base.x + 8.0
                 } else {
                     base.x + (base.w - text_w) / 2.0
@@ -1650,7 +1669,7 @@ impl Application for FilesystemApp {
         if cce_ui::layout::control_relief() {
             let wall = cce_ui::layout::bar_wall_width();
             if self.select_mode {
-                let band_h = SELECT_BAR_H + cce_ui::layout::root_plate_padding();
+                let band_h = SELECT_BAR_H + cce_ui::layout::root_plate_inset();
                 pc.recess_edges(
                     Rect { x: 0.0, y: fh - band_h, width: fw, height: band_h },
                     (0.0, 0.0, 0.0, 0.0),
@@ -1725,7 +1744,7 @@ impl Application for FilesystemApp {
                 );
                 pc.text_with(
                     "Preview".to_string(),
-                    band.0 + 10.0,
+                    band.0 + cce_ui::layout::plate_padding(),
                     band.1 + (band.3 - 13.0) / 2.0,
                     13.0,
                     [0xc8, 0xc8, 0xd4],
diff --git a/src/pages/browse.rs b/src/pages/browse.rs
index c5c1bce..8741ec5 100644
--- a/src/pages/browse.rs
+++ b/src/pages/browse.rs
@@ -185,7 +185,7 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
     let text_dim = cce_ui::color::TEXT_DIM;
 
 
-    // The pane rect already sits root_plate_padding off the plate rim — no
+    // The pane rect already sits root_plate_inset off the window edge — no
     // second inset here, or the list lands 16+12 from the edge while apps
     // that place content at the pane rect (cce-data-editor's tree) sit at 16.
     let gap = cce_ui::layout::root_plate_gap();
@@ -316,7 +316,10 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
     // When the search strip is open it reserves the bottom of the frame, exactly as
     // the legacy List::set_rect carved its scroll frame.
     let search_h = 26.0;
-    let search_margin_y = 6.0;
+    // The strip stands off the well's rim (and the rows above it) by the
+    // pane rung, on both axes.
+    let search_pad = cce_ui::layout::plate_padding();
+    let search_margin_y = search_pad;
     let search_offset = if state.search_visible { search_h + 2.0 * search_margin_y } else { 0.0 };
     state.list.set_rect(list_x, list_y, list_w, list_h, search_offset);
     state.list.update_bounds_from_rows();
@@ -334,9 +337,9 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
     state.list.push_prims(&mut pc);
     if state.search_visible {
         let (sx, sy, sw, sh) = (
-            list_x + 8.0,
+            list_x + search_pad,
             list_y + list_h - search_offset + search_margin_y,
-            list_w - 16.0,
+            list_w - 2.0 * search_pad,
             search_h,
         );
         cce_ui::layout::render_widget(&mut pc, &mut state.search_box, sx, sy, sw, sh, ctx);
@@ -351,6 +354,8 @@ pub fn view(state: &mut BrowseState, view_dropdown: &mut cce_ui::widget::Adapted
         state.entries.len(),
         if state.show_hidden { " (.)" } else { "" }
     );
+    // TODO(style): the 24 keeps the estimated-width label clear of the
+    // list's scrollbar as well as the rim; the 18 is a text baseline drop.
     let count_text_w = count_str.len() as f32 * 6.0;
     let count_x = list_x + list_w - count_text_w - 24.0;
     let count_y = list_y + list_h - 18.0;
diff --git a/src/pages/network.rs b/src/pages/network.rs
index c389802..b288f83 100644
--- a/src/pages/network.rs
+++ b/src/pages/network.rs
@@ -113,13 +113,16 @@ pub fn view(state: &mut NetworkState, browse: &BrowseState, view_dropdown: &mut
 
     // Render the Breadcrumb and Dropdown next to it
     let dropdown_w = 120.0;
-    let breadcrumb_w = cw - 16.0 - dropdown_w - 12.0;
+    // TODO(style): the 4/6/16 offsets are a leftover inset from the pane rect
+    // that browse.rs has already dropped; the gap to the dropdown is the rung.
+    let gap = cce_ui::layout::root_plate_gap();
+    let breadcrumb_w = cw - 16.0 - dropdown_w - gap;
     cce_ui::layout::render_widget(&mut pc, &mut state.breadcrumb, cx + 4.0, cy + 6.0, breadcrumb_w, 24.0, ctx);
     {
         let rect = cce_ui::scene::layout::Rect { x: cx + 4.0, y: cy + 6.0, width: breadcrumb_w, height: 24.0 };
         crate::pages::breadcrumb_relief(&mut pc, &state.breadcrumb, rect);
     }
-    cce_ui::layout::render_widget(&mut pc, view_dropdown, cx + 4.0 + breadcrumb_w + 12.0, cy + 6.0, dropdown_w, 24.0, ctx);
+    cce_ui::layout::render_widget(&mut pc, view_dropdown, cx + 4.0 + breadcrumb_w + gap, cy + 6.0, dropdown_w, 24.0, ctx);
 
     // Check if directory changed, or if last_dir is empty, and repopulate
     if state.last_dir != browse.current_dir || state.graph.get_nodes().is_empty() {
diff --git a/src/pages/space.rs b/src/pages/space.rs
index fe7e738..8978237 100644
--- a/src/pages/space.rs
+++ b/src/pages/space.rs
@@ -471,13 +471,16 @@ pub fn view(
     // Breadcrumb + view dropdown, mirroring the Network page's header so the
     // two views line up when you switch between them.
     let dropdown_w = 120.0;
-    let breadcrumb_w = cw - 16.0 - dropdown_w - 12.0;
+    // TODO(style): the 4/6/16 offsets are a leftover inset from the pane rect
+    // that browse.rs has already dropped; the gap to the dropdown is the rung.
+    let gap = cce_ui::layout::root_plate_gap();
+    let breadcrumb_w = cw - 16.0 - dropdown_w - gap;
     cce_ui::layout::render_widget(&mut pc, &mut state.breadcrumb, cx + 4.0, cy + 6.0, breadcrumb_w, 24.0, ctx);
     {
         let rect = cce_ui::scene::layout::Rect { x: cx + 4.0, y: cy + 6.0, width: breadcrumb_w, height: 24.0 };
         crate::pages::breadcrumb_relief(&mut pc, &state.breadcrumb, rect);
     }
-    cce_ui::layout::render_widget(&mut pc, view_dropdown, cx + 4.0 + breadcrumb_w + 12.0, cy + 6.0, dropdown_w, 24.0, ctx);
+    cce_ui::layout::render_widget(&mut pc, view_dropdown, cx + 4.0 + breadcrumb_w + gap, cy + 6.0, dropdown_w, 24.0, ctx);
 
     let mut segments = Vec::new();
     for component in browse.current_dir.components() {
@@ -519,7 +522,7 @@ pub fn view(
     let text_fg = cce_ui::color::TEXT_FG;
 
     if let Some(err) = &state.error {
-        pc.text(err, map.0 + 12.0, map.1 + 12.0, 11.0, text_dim);
+        pc.text(err, map.0 + cce_ui::layout::plate_padding(), map.1 + cce_ui::layout::plate_padding(), 11.0, text_dim);
         return pc;
     }
 
@@ -530,7 +533,7 @@ pub fn view(
             state.scan_files,
             format_size(state.scan_bytes)
         );
-        pc.text(&msg, map.0 + 12.0, map.1 + 12.0, 11.0, text_dim);
+        pc.text(&msg, map.0 + cce_ui::layout::plate_padding(), map.1 + cce_ui::layout::plate_padding(), 11.0, text_dim);
         return pc;
     }
 
@@ -538,7 +541,7 @@ pub fn view(
     state.relayout((map.0 + 1.0, map.1 + 1.0, (map.2 - 2.0).max(0.0), (map.3 - 2.0).max(0.0)));
 
     if state.tiles.is_empty() {
-        pc.text("Nothing to show — the directory is empty.", map.0 + 12.0, map.1 + 12.0, 11.0, text_dim);
+        pc.text("Nothing to show — the directory is empty.", map.0 + cce_ui::layout::plate_padding(), map.1 + cce_ui::layout::plate_padding(), 11.0, text_dim);
         return pc;
     }
 
diff --git a/src/preview_pane.rs b/src/preview_pane.rs
index 678eb24..ff52b4f 100644
--- a/src/preview_pane.rs
+++ b/src/preview_pane.rs
@@ -285,12 +285,15 @@ impl PreviewPane {
         let label_fg = cce_ui::color::TEXT_ACCENT;
 
         if self.path.is_none() {
-            pc.text("Select a file to view details", cx + 12.0, cy + 12.0, 13.0, text_dim);
+            let pad = cce_ui::layout::plate_padding();
+            pc.text("Select a file to view details", cx + pad, cy + pad, 13.0, text_dim);
             return;
         }
 
         let half_h = ch * 0.5;
         let pad = cce_ui::layout::section_padding();
+        // Inside each well the content stands off the rim by the pane rung.
+        let inset = cce_ui::layout::plate_padding();
         // Under control_relief the section frames are recessed wells carved
         // into the plate (the list's treatment); the SectionContext 1px line
         // frame is the flat fallback. Titles come from SectionContext::new
@@ -307,6 +310,9 @@ impl PreviewPane {
         // breadcrumb across the split (finish() draws from top+7, so the
         // fallback frame gets top-7 to land on the same edge).
         {
+            // style: deliberate — the 7 and 12 mirror SectionContext::finish's
+            // own frame geometry (top+7 down to content_y+pad+12), so the flat
+            // fallback frame lands on the well's edges.
             let mut preview_sec = SectionContext::new(pc, cx - pad, cy - 7.0, cw + 2.0 * pad, "", false, false);
             preview_sec.content_y = cy + half_h - pad - 12.0;
             if !relief {
@@ -320,8 +326,8 @@ impl PreviewPane {
                 pc.relief_recessed(cx, cy, cw, half_h, radius);
             }
         }
-        let rect_y = cy + 12.0;
-        let rect_h = half_h - pad - 24.0;
+        let rect_y = cy + inset;
+        let rect_h = half_h - pad - 2.0 * inset;
 
         // Content fill spans the FULL well RECT, rounded to the well's radius —
         // `RowList::push_prims` verbatim, so the two read as the same material
@@ -352,7 +358,7 @@ impl PreviewPane {
             let clip_top = rect_y;
             let clip_bottom = rect_y + rect_h - 8.0;
             let clip = [cx, clip_top, cx + cw, clip_bottom];
-            let mut text_y = rect_y + 12.0 - frac;
+            let mut text_y = rect_y + inset - frac;
             for line in content.lines().skip(first_line as usize) {
                 if text_y >= clip_bottom {
                     break;
@@ -360,13 +366,13 @@ impl PreviewPane {
                 // Chars-per-width from one measured glyph (cached) instead of
                 // the old magic 6.8 px/char guess.
                 let char_w = measure_text_width("M", "monospace", 11.0).max(1.0);
-                let limit = (((cw - 24.0) / char_w).floor() as usize).max(20);
+                let limit = (((cw - 2.0 * inset) / char_w).floor() as usize).max(20);
                 let line_truncated = truncate_tail(line, limit);
-                pc.text_with_font_bounded(&line_truncated, cx + 12.0, text_y, 11.0, text_fg, "monospace", clip);
+                pc.text_with_font_bounded(&line_truncated, cx + inset, text_y, 11.0, text_fg, "monospace", clip);
                 text_y += PREVIEW_LINE_H;
             }
         } else {
-            pc.text("No preview available", cx + 12.0, rect_y + 12.0, 11.0, text_dim);
+            pc.text("No preview available", cx + inset, rect_y + inset, 11.0, text_dim);
         }
 
         // 2. Bottom pane: Details Section. The visible gap between the wells
@@ -383,13 +389,14 @@ impl PreviewPane {
             ("Modified", &self.modified),
         ];
 
-        let details_content_start_y = details_top + pad + 12.0;
+        let details_content_start_y = details_top + pad + inset;
 
         // The well's bottom edge sits AT the pane bottom, aligned with the
         // list across the split (content-sized before; short panes just show
         // empty well below the rows). finish() draws from top+7, so the
         // fallback frame gets top-7 to land on the same edge.
         {
+            // style: deliberate — SectionContext::finish's frame geometry, as above.
             let mut details_sec = SectionContext::new(pc, cx - pad, details_top - 7.0, cw + 2.0 * pad, "", false, false);
             details_sec.content_y = cy + ch - pad - 12.0;
             if !relief {
@@ -404,8 +411,10 @@ impl PreviewPane {
             }
         }
 
+        // TODO(style): the header's 6px drop, the 42/112 label columns and
+        // the 20px row pitch are the details form's own rhythm.
         let header_y = details_content_start_y + 6.0;
-        pc.text(icon, cx + 12.0, header_y, 20.0, text_fg);
+        pc.text(icon, cx + inset, header_y, 20.0, text_fg);
 
         // Pixel-measured budgets against the section frame's inner right edge
         // (None-font text renders sans-serif — measure with the same family).
@@ -418,15 +427,16 @@ impl PreviewPane {
 
         let mut y = details_content_start_y + 36.0;
         for (label, val) in &details {
-            pc.text(label, cx + 12.0, y, 12.0, label_fg);
+            pc.text(label, cx + inset, y, 12.0, label_fg);
             let val_str = truncate_px(val, "sans-serif", 12.0, val_avail, true);
             pc.text(&val_str, cx + 112.0, y, 12.0, text_dim);
             y += 20.0;
         }
 
         if !self.target.is_empty() {
-            y += 8.0;
-            pc.text("Target", cx + 12.0, y, 12.0, label_fg);
+            // One pane gap sets the link target off from the rows above.
+            y += cce_ui::layout::plate_gap();
+            pc.text("Target", cx + inset, y, 12.0, label_fg);
             let target_str = truncate_px(&self.target, "sans-serif", 12.0, val_avail, true);
             pc.text(&target_str, cx + 112.0, y, 12.0, text_dim);
         }