diff --git a/frontend/src/components/simulator/CameraToggle.tsx b/frontend/src/components/simulator/CameraToggle.tsx index 5861c785..0b043f3d 100644 --- a/frontend/src/components/simulator/CameraToggle.tsx +++ b/frontend/src/components/simulator/CameraToggle.tsx @@ -111,11 +111,14 @@ export const CameraToggle: React.FC = ({ boardId, maxFrameByt // starving without frames. Never while the sim is stopped — the // button is not asking for anything then. Solid border once // streaming or asking. - className={ + // Static layout is in .canvas-cam-mic-btn so the toolbar @container + // query can collapse the label to icon-only (an inline font-size would + // beat it). Only state-driven styles stay inline below. + className={`canvas-cam-mic-btn${ running && boardId && status !== 'streaming' && status !== 'requesting' - ? 'velxio-btn-ants' - : undefined - } + ? ' velxio-btn-ants' + : '' + }`} style={{ // backgroundColor, NOT the background shorthand: inline shorthand // would wipe the class's background-image and kill the ants. @@ -123,13 +126,7 @@ export const CameraToggle: React.FC = ({ boardId, maxFrameByt border: `1px solid ${ isOn ? '#3fb950' : status === 'requesting' ? '#555' : 'transparent' }`, - borderRadius: 4, - padding: '4px 10px', - display: 'flex', - alignItems: 'center', - gap: 6, color, - fontSize: 13, cursor: boardId ? 'pointer' : 'not-allowed', // Inline only while requesting. 'none' here would OVERRIDE the // .velxio-btn-ants class animation (inline beats class), which is diff --git a/frontend/src/components/simulator/MicrophoneToggle.tsx b/frontend/src/components/simulator/MicrophoneToggle.tsx index cc4f8ff0..9376180c 100644 --- a/frontend/src/components/simulator/MicrophoneToggle.tsx +++ b/frontend/src/components/simulator/MicrophoneToggle.tsx @@ -62,18 +62,16 @@ export const MicrophoneToggle: React.FC = ({ boardId }) = onClick={handleClick} disabled={!boardId || status === 'requesting'} title={tooltip} + // Static layout is in .canvas-cam-mic-btn so the toolbar @container + // query can collapse the label to icon-only (an inline font-size would + // beat it). Only state-driven styles stay inline. + className="canvas-cam-mic-btn" style={{ backgroundColor: isOn ? 'rgba(63,185,80,0.15)' : 'transparent', border: `1px solid ${ isOn ? '#3fb950' : status === 'requesting' ? '#555' : 'transparent' }`, - borderRadius: 4, - padding: '4px 10px', - display: 'flex', - alignItems: 'center', - gap: 6, color, - fontSize: 13, cursor: boardId ? 'pointer' : 'not-allowed', animation: status === 'requesting' ? 'velxio-pulse 1s ease-in-out infinite' : undefined, diff --git a/frontend/src/components/simulator/SimulatorCanvas.css b/frontend/src/components/simulator/SimulatorCanvas.css index c7a7a3a8..9263217a 100644 --- a/frontend/src/components/simulator/SimulatorCanvas.css +++ b/frontend/src/components/simulator/SimulatorCanvas.css @@ -105,6 +105,24 @@ cursor: not-allowed; } +/* ── Camera / Microphone toggles ───────────────────── + Static layout for the CameraToggle / MicrophoneToggle buttons. It lives + in a class (not inline) so the same @container query that collapses the + Serial/Scope buttons to icon-only can reach these too — an inline + font-size would beat the class and the label could never collapse. + State-driven styles (background, border, colour, pulse animation) stay + inline on the button. */ +.canvas-cam-mic-btn { + display: flex; + align-items: center; + gap: 6px; + padding: 0 10px; + height: 28px; + border-radius: 4px; + font-size: 13px; + white-space: nowrap; +} + /* ── Serial Monitor button ───────────────────────── */ .canvas-serial-btn { display: flex; @@ -383,6 +401,12 @@ height: 18px; } + .canvas-cam-mic-btn { + padding: 0 6px; + gap: 0; + font-size: 0; /* icon-only on narrow viewports too */ + } + .add-component-btn { padding: 0 8px; font-size: 0; /* hide text, show icon only */ @@ -426,6 +450,12 @@ editor toolbar and view-mode toggle collapse at, so the whole bar degrades together and nothing overlaps. */ @container unified-toolbar (max-width: 1010px) { + /* Pack the toolbar buttons tighter once the AI chat steals horizontal + room, so Serial/Camera/Mic/Scope stop overlapping. */ + .canvas-header-left, + .canvas-header-right { + gap: 4px; + } .canvas-serial-btn { padding: 0 8px; font-size: 0; /* hide "Serial" / "Scope" text, keep the icon */ @@ -434,6 +464,13 @@ width: 18px; height: 18px; } + /* Same collapse as Serial: hide the "Camera" / "Mic" label, keep the icon. + gap: 0 drops the now-empty icon-to-label space. */ + .canvas-cam-mic-btn { + padding: 0 8px; + gap: 0; + font-size: 0; + } .add-component-btn { padding: 0 9px; font-size: 0; /* hide "Add" text, keep the icon */