From ffb4fc0159db792cb60f0951b3f45c363294062d Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Thu, 30 Jul 2026 18:56:24 +0200 Subject: [PATCH] =?UTF-8?q?feat(editor):=20toolbar=20rides=20inside=20the?= =?UTF-8?q?=20header=20=E2=80=94=20one=20row,=20not=20two?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the marketing nav gone from the editor header, its middle was ~700px of dead space at 1280 while the toolbar occupied a whole second 38px bar. The unified toolbar strip now fills that middle through a new AppHeader editorToolbar slot: one 44px row where there used to be 44+38 — a full row returned to the code and the canvas. The strip keeps its own class names, so everything that keyed on them keeps working untouched: the container queries, the docked-chat padding-right, and the internal flex-wrap. When it truly cannot fit, the strip wraps and the header grows (height: auto on the modifier class) instead of clipping or overlapping; brand and the right-side controls stay pinned to the first line. Inside the header the strip drops its own background and border so it reads as one bar, not a box within a bar. Mobile keeps the previous layout (no strip; the mobile tab bar remains). --- frontend/src/App.css | 53 ++++++++ frontend/src/components/layout/AppHeader.tsx | 12 +- frontend/src/pages/EditorPage.tsx | 121 ++++++++++--------- 3 files changed, 128 insertions(+), 58 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index e4147ade..22eaba9c 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -477,3 +477,56 @@ body { @media (prefers-reduced-motion: reduce) { .velxio-ants { animation: none; } } + +/* ── Editor: toolbar riding inside the header ───────────────────────────── + With the marketing nav gone from the editor header, its middle was dead + space while the toolbar occupied a whole second bar. The strip now fills + that middle: one 44px row instead of 44+38 — a full row returned to the + code/canvas. The strip keeps its own class names (container queries, the + docked-chat padding-right) and wraps internally when it truly cannot + fit; the header then grows instead of clipping or overlapping. */ +.app-header--with-toolbar { + height: auto; + min-height: 44px; +} + +.header-editor-toolbar { + flex: 1 1 auto; + min-width: 0; + display: flex; + align-self: stretch; + margin: 0 10px; +} + +.header-editor-toolbar .unified-toolbar { + flex: 1 1 auto; + min-width: 0; + /* Skin: it is part of the header now, not a bar of its own. */ + background: transparent; + border-bottom: none; + min-height: 44px; + align-items: center; +} + +/* Keep the brand block and the right-side controls pinned to the first + line when the strip wraps the header taller. */ +.app-header--with-toolbar .header-left, +.app-header--with-toolbar .header-right { + align-self: flex-start; + min-height: 44px; + display: flex; + align-items: center; +} + +.app-header--with-toolbar .header-content { + align-items: stretch; +} + +/* The editor zone / toolbar inside the strip: no own chrome, blend into + the header instead of painting a nested dark box with its own border. */ +.header-editor-toolbar .editor-toolbar, +.header-editor-toolbar .unified-toolbar-editor .editor-toolbar { + background: transparent; + border-bottom: none; + height: 44px; +} diff --git a/frontend/src/components/layout/AppHeader.tsx b/frontend/src/components/layout/AppHeader.tsx index ff41583c..7d88e0c2 100644 --- a/frontend/src/components/layout/AppHeader.tsx +++ b/frontend/src/components/layout/AppHeader.tsx @@ -22,6 +22,11 @@ interface AppHeaderProps { * toolbar is starved of on small screens; the logo still links home. * Same mechanism the Tauri desktop build uses (VITE_DESKTOP). */ editorMenu?: React.ReactNode; + /** Editor variant: the unified toolbar strip rendered in the header's + * middle — the space the marketing nav used to occupy. One row instead + * of header + toolbar stacked; the strip wraps internally when narrow + * and the header grows to fit (height: auto on the modifier class). */ + editorToolbar?: React.ReactNode; } const SAVE_STATUS_COPY: Record = { @@ -68,7 +73,7 @@ const AutoSaveIndicator: React.FC<{ state: AutoSaveState }> = ({ state }) => { ); }; -export const AppHeader: React.FC = ({ autoSave, editorMenu }) => { +export const AppHeader: React.FC = ({ autoSave, editorMenu, editorToolbar }) => { const location = useLocation(); const currentProject = useProjectStore((s) => s.currentProject); const [menuOpen, setMenuOpen] = useState(false); @@ -98,7 +103,7 @@ export const AppHeader: React.FC = ({ autoSave, editorMenu }) => location.pathname === localize(path) ? ' header-nav-link-active' : ''; return ( -
+
{/* Brand */} @@ -216,6 +221,9 @@ export const AppHeader: React.FC = ({ autoSave, editorMenu }) => )}
+ {/* Editor toolbar strip — fills the middle the nav vacated. */} + {editorToolbar &&
{editorToolbar}
} + {/* Right: language + share + auth + mobile hamburger */}
diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index 536afbee..29258028 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -376,62 +376,16 @@ export const EditorPage: React.FC = () => { [explorerWidth], ); - return ( -
- : undefined} /> - - {/* ── Mobile tab bar (top, above panels) ── */} - {isMobile && ( - - )} - - {/* ── Unified top toolbar (desktop only) ── - Editor controls + canvas controls share a single full-width row so - the bar doesn't reflow when the editor/canvas splitter is dragged. - The canvas controls (board selector, Serial, Scope, zoom, Add) are - portaled into `canvasHeaderSlot` from inside SimulatorCanvas. */} - {!isMobile && ( + /* ── Unified toolbar (desktop) ── + Editor controls + canvas controls in one strip; the canvas side is + portaled into `canvasHeaderSlot` from SimulatorCanvas. Since the + marketing nav left the editor header, the header's middle is empty + space — so this strip now rides INSIDE the AppHeader row (via the + editorToolbar prop) instead of being a second bar: one 44px row where + there used to be 44+38. On narrow widths the strip wraps internally + and the header grows; the docked AI chat is avoided by the same + padding-right the strip always had. */ + const unifiedToolbar = !isMobile ? (
+ ) : undefined; + + return ( +
+ : undefined} + editorToolbar={unifiedToolbar} + /> + + {/* ── Mobile tab bar (top, above panels) ── */} + {isMobile && ( + )} +
{/* ── Editor side ── */}