Picture-in-picture

Picture-in-picture panel

<lab-pip-panel> · js/components/pip-panel/

Universal PiP shell. Cream + ink + offset-shadow chrome via --widget-bar-* tokens, a default <slot> for the content element, and a single bottom bar carrying the device label (left) and, when the device declares magnification states, a generated toggle group (right). The label attribute is the instrument kind ("Microscope", "Magnifier"), from the step's widget_label; the bar hides only when there is neither a label nor any buttons. Below a ~300 px width the bar reflows — buttons stack above the label, both centred, with a smaller-button variant. The shell sizes adaptively (width clamps between a 240 px floor and a 380 px ceiling, tracking the right-edge bracket) and can render the shared Error state itself (the error attribute) when there is no content element to mount. Clicking a button sets objective and emits objective-change, which PipHandler relays to the content's HUD. Production positioning is owned by #pip-anchor; these previews pin --pip-panel-width and stack inline.

[ content slot ]
With device label — bottom strip
[ content slot ]
No label — strip hidden (legacy demo steps)
Shell Error state — unknown image_class (no content to mount)

Microscope view

<lab-microscope-view> · js/components/microscope-view/

Pure PiP content element. 1:1 sample viewport over the shared --pip-base desk gray, with a light-mono HUD (magnification + sample identity bottom-left, scale bar bottom-right). It carries no device label and no controls — the shell owns the label and the magnification toggle group. The objective attribute (set by PipHandler, updated when the shell's buttons change it) drives the HUD readout only. Viewport states: Placeholder (target declared, real imagery pending — faint hatch + HUD), Empty (no target), Error (unresolved target). Real microscopy imagery is Phase D. Mounts inside <lab-pip-panel> via PIP_CONTENT_REGISTRY.

Placeholder — target="peat_sample", 0 states (no buttons)
Empty — no target
Error — unresolved target
With states — label left, buttons right (wide)
With states, narrow — buttons stacked above label, smaller

Magnifier view

<lab-magnifier-view> · js/components/magnifier-view/

Chrome-free PiP content element — a single square viewport over the shared --pip-base base. The image is not authored on the step; it is pushed at runtime via the pip-image event onto the src property. State follows the image: Empty (bare base) until the first push, Loaded once it arrives, Error if it fails to load (the <img> error event). No HUD, no controls; the device label is the shell's.

Empty — awaiting first image
Loaded — image pushed via src
Error — image failed to load