Client half of the PiP observation window, wired to the host
ObservationService over the /bsk-observation HTTP+SSE seam:
- ObservationClientStore: initial state fetch + SSE increments +
on-demand thumbnail blob loading (session-authorized readAttachment,
same path as the toolview) + interrupt POST. All I/O injectable.
- ObservationOverlay registered into the shell.overlay list seat (the
sanctioned frame-wide surface; root is off-limits). Hidden with no
owned sessions; appears on first start; vanishes when all stop.
- Focus view: status row (green/grey/red dot + session + action + mm:ss
ticking), breathing thumbnail (new frames fade in, failures keep the
last frame with a warning badge, pre-navigate placeholder), and the
action area.
- Interrupt: single click, no confirm; greys into 'Interrupting…' and
back; disabled when nothing is in flight; one-time tooltip explains
the semantics (stops the current action only; the run continues).
- Card: drag-move via the header, corner-handle resize (min 240x180,
max 80% viewport), both clamped and kept for the page lifetime;
collapsible status capsule.
- PiP: Pop out (user gesture) opens Document PiP with the card's
current size, portals the same content in, and clones the document's
style nodes; pagehide falls back to the card with state intact;
unsupported browsers hide the button; nothing auto-pops.
Tests (+17, 89 total): store (fetch/SSE apply/malformed frames/
thumbnail lifecycle/interrupt wire/stop cleanup) and overlay (hidden->
visible lifecycle, status row, thumbnail via loader, interrupt
disabled/active/hint-once, capsule, resize clamps, move clamps, PiP
unsupported, PiP pop/fallback). Styling follows web-styling (dsw-alias
tokens, CSS Modules, focus-visible, reduced-motion).