* feat(agent): resizable, undockable assistant panel
User report: the assistant chat sheet sometimes covers the page content
the user is asking about, with no way to resize or move it.
- Docked mode is now drag-resizable from its left edge (380-800px,
clamped so the page keeps a 480px readable column) and the page
reflows beside it via the existing --agent-dock-w reservation.
- Expanded (focus) mode reserves page margin like the compact dock
instead of overlaying up to 1100px of the page.
- New undock toggle turns the sheet into a floating window that can be
dragged by its header and resized from edges/corners, clamped so the
header always stays reachable. Desktop only; mobile keeps the
full-screen sheet.
- Geometry (mode, dock width, float rect) persists per user in
user_preferences.ui_state.agent_panel, server-seeded to avoid a
first-paint jump; the ui-state API schema gains a strict agent_panel
key with nested merge.
- Pure clamp/resize math lives in lib/agent-panel/geometry with unit
tests; drag frames write styles imperatively and commit one
preference update on release.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): address review findings on panel drag, a11y, and persistence
CodeRabbit round 1, all six findings fixed:
- Bind drag listeners to window (plus lostpointercapture) so a failed
pointer capture or mid-drag unmount can never leave the transition
suppression and data-agent-resizing stuck for the session.
- Keyboard resize now steps from the visible width (expandedW in focus
mode) instead of jumping to the persisted dock width.
- The width handle exposes window-splitter semantics: aria-valuenow,
aria-valuemin, aria-valuemax.
- --nav-w is read reactively via a MutationObserver on #dash-shell
instead of computed-style reads in the render body and per drag frame.
- The ui-state POST in updatePanelPrefs gets a 300ms trailing debounce
(state stays immediate) so key auto-repeat cannot produce one
read-merge-write per repeat; pending write flushes on unmount.
- globals.css keeps one :root token block; the agent-resizing rule moved
below it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(agent): filter drag events by pointer id, clear fired debounce timer
CodeRabbit round 2, both findings fixed:
- Window-level drag listeners now ignore events from pointers other than
the initiating one, so a second touch or pen cannot move the panel or
end the first pointer's drag.
- The persist debounce timer ref is nulled when the timer fires, so the
unmount flush only writes genuinely pending values instead of
replaying an already-persisted (possibly stale) geometry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>