diff --git a/frontend/src/App.css b/frontend/src/App.css
index 16b1f25f..47df9d9c 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -169,6 +169,23 @@ body {
background: #252526;
border-bottom: 1px solid #333;
min-height: 38px;
+ /* Query container so every zone (view-mode toggle, editor toolbar, canvas
+ controls) can collapse based on the *shared bar's own width* — which is
+ what shrinks when the right-docked AI chat opens — instead of the viewport
+ width. Prevents the canvas controls (board selector, Serial, Scope) from
+ overlapping the editor Run/Compile buttons on a narrow bar. `layout`
+ containment (implied) does not clip: the overflow/run dropdowns still
+ render below the bar. */
+ container-type: inline-size;
+ container-name: unified-toolbar;
+}
+
+/* Collapse the Code/Both/Circuit labels to icon-only first (biggest fixed
+ block in the bar) as the shared bar narrows. */
+@container unified-toolbar (max-width: 1140px) {
+ .view-mode-toggle .vm-label {
+ display: none;
+ }
}
.unified-toolbar-explorer-toggle {
@@ -177,7 +194,11 @@ body {
.unified-toolbar-editor {
flex: 1 1 auto;
- min-width: 0;
+ /* Floor the editor zone at its fully-collapsed content width so it can never
+ shrink *under* its own buttons and paint them over the adjacent canvas
+ controls. Below this floor the (lower-priority) canvas controls get pushed
+ toward the right edge instead of overlapping — never the other way. */
+ min-width: 280px;
display: flex;
}
diff --git a/frontend/src/components/simulator/SimulatorCanvas.css b/frontend/src/components/simulator/SimulatorCanvas.css
index e8ffdef8..53c52e67 100644
--- a/frontend/src/components/simulator/SimulatorCanvas.css
+++ b/frontend/src/components/simulator/SimulatorCanvas.css
@@ -398,6 +398,53 @@
}
}
+/* ── Narrow shared toolbar (desktop, AI chat open) ────────────────────────
+ When the canvas header is portaled into the unified top toolbar, it shares
+ one row with the editor actions. As that *bar* narrows (mainly when the
+ right-docked AI chat opens), collapse the canvas controls the same way the
+ mobile block does — but keyed to the bar's own width via a container query,
+ not the viewport, so it triggers on a wide screen with the chat open too.
+ Priority: labels -> icons, then drop the non-interactive count, then the
+ zoom buttons (mouse-wheel zoom still works). Mirrors the thresholds the
+ editor toolbar and view-mode toggle collapse at, so the whole bar degrades
+ together and nothing overlaps. */
+@container unified-toolbar (max-width: 1010px) {
+ .canvas-serial-btn {
+ padding: 0 8px;
+ font-size: 0; /* hide "Serial" / "Scope" text, keep the icon */
+ }
+ .canvas-serial-btn svg {
+ width: 18px;
+ height: 18px;
+ }
+ .add-component-btn {
+ padding: 0 9px;
+ font-size: 0; /* hide "Add" text, keep the icon */
+ }
+ .add-component-btn svg {
+ width: 18px;
+ height: 18px;
+ }
+ .board-selector {
+ max-width: 120px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+}
+@container unified-toolbar (max-width: 905px) {
+ .component-count {
+ display: none;
+ }
+ .board-selector {
+ max-width: 96px;
+ }
+}
+@container unified-toolbar (max-width: 835px) {
+ .zoom-controls {
+ display: none; /* mouse-wheel / trackpad zoom still available */
+ }
+}
+
/* ── WiFi / BLE status badges ─────────────────────────────────────────── */
.canvas-wifi-badge,
.canvas-ble-badge {
diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx
index 13b3bffe..106f03c9 100644
--- a/frontend/src/pages/EditorPage.tsx
+++ b/frontend/src/pages/EditorPage.tsx
@@ -480,7 +480,7 @@ export const EditorPage: React.FC = () => {
>
- {m.label}
+ {m.label}
))}