From b8b5220583f6f6fd4c2336b25c794273470ab006 Mon Sep 17 00:00:00 2001 From: Paul Trowbridge Date: Fri, 25 Sep 2026 13:06:22 -0400 Subject: [PATCH] Hold the toolbar's widths still while stepping and editing The stepper's middle is sized to the axis's longest level rather than the current one, and Save and the dirty dot keep their slots while hidden, so nothing shifts under the cursor. Co-Authored-By: Claude Opus 5.5 (1M context) --- ui/src/components/DepthButtons.jsx | 9 ++++++++- ui/src/components/LayoutMenu.jsx | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/ui/src/components/DepthButtons.jsx b/ui/src/components/DepthButtons.jsx index ab3e2f3..d36e1d6 100644 --- a/ui/src/components/DepthButtons.jsx +++ b/ui/src/components/DepthButtons.jsx @@ -35,6 +35,9 @@ export default function DepthButtons({ label, levels, depth, onPick, totalLabel const known = depth != null const current = !known ? '—' : (depth === 0 ? totalLabel : levels[depth - 1]) const step = (d) => onPick(Math.min(max, Math.max(0, d))) + // Capped so one long field name cannot push the rest of the toolbar off; + // past the cap the label truncates, with the full name in the title. + const widest = Math.min(18, Math.max(...[totalLabel, ...levels].map(l => String(l).length))) // One joined control, in the style of the operation panel's // Scale | Recode | Clone. The arrows used to be separate outlined boxes a @@ -53,9 +56,13 @@ export default function DepthButtons({ label, levels, depth, onPick, totalLabel title={`Collapse ${label.toLowerCase()} by one level`} onClick={() => step((depth ?? 0) - 1)}>◂ + {/* Sized to the axis's longest level, not the current one. Sized to + its content it resized on every step, so the ▸ moved out from + under the cursor between clicks. */} diff --git a/ui/src/components/LayoutMenu.jsx b/ui/src/components/LayoutMenu.jsx index 7db8102..f79d85a 100644 --- a/ui/src/components/LayoutMenu.jsx +++ b/ui/src/components/LayoutMenu.jsx @@ -123,15 +123,22 @@ export default function LayoutMenu({ ${open ? 'border-blue-300 text-blue-700 bg-blue-50' : 'border-gray-200 text-gray-600 hover:border-gray-400'}`}> {active ? active.name : 'Unsaved'} - {dirty && •} + {/* Always rendered, hidden when clean, so the button does not widen + by a dot the moment the pivot changes. */} + • ▾ {/* Save is the common action and stays outside the menu, but only when - there is something to save it to and it is yours to overwrite. */} - {active?.can_edit && dirty && ( - )}