feat(agents): redesign — drawers, design-system foundation, a11y, mobile #559
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!559
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/agents-history-drawer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Agents page redesign + design-system foundation + accessibility pass. Agents page is now the reference pattern for the rest of the app (see #558 for the sweep).
Highlights
Agents page redesign
sm:.role="status"save indicator.Design system foundation
<Button>primitive:iconOnly,loading,tone="error",leadingIcon/trailingIcon(typedLucideIcon).<Drawer>primitive:role="dialog"+aria-modal+aria-labelledby+ focus trap + focus return + Esc/backdrop close + slide animations. Three call-sites migrated.useMediaQueryhook (SSR-safe viauseSyncExternalStore).lucide-reactadopted; emoji glyphs replaced with icons (Pencil,Trash2,Clock,Settings,ChevronRight/Down,Pause/Play,RotateCcw,Plus,X,Fuel,AlertTriangle,ArrowUp/Down).Accessibility (WCAG 2.1 AA)
<button>witharia-expanded(no more absolute-overlay swallowing content from screen readers).role="status" aria-live="polite".<label>;aria-invalid+aria-describedbylink errors to inputs.prefers-reduced-motionreduces every animation/transition to ~0ms.--ch-color-text-dimbumped (4.0:1 → ~4.9:1 dark, 2.5:1 → ~4.7:1 light).Server-side cleanup
model_overrideremoved end-to-end (UI input gone, server silently dropsmodelon POST/PATCH, response always returnstype.default_model). Per-instance overrides — Provider chain on the type is the only model surface.cfg.reposnow sources from thewatched_reposSQLite table, notagents.json#/repos. Two-systems-fighting bug fixed — DB is the single source of truth (webhook ingress, board fetcher, settings UI all read the same rows).Test plan
just qa— typecheck + lint clean/agentspage on desktop + mobile viewports, drawers (history / advanced / type editor) open + close + Esc + focus trap + focus return/planner/board) populates triage with user-story issues fromwatched_reposFollow-ups (separate tickets)
🤖 Generated with Claude Code
- New `InstanceAdvancedDrawer` mirrors `InstanceHistoryDrawer` / `TypeEditorDrawer` (slide-in animation, leave guard, Esc/backdrop/× closes). Wraps the existing `InstanceAdvancedEdit` body so the prompt-appendix + notes textareas now live in a focused panel instead of an inline expansion row. - Drop the inline-row machinery: `expanded` Set state, `toggleAdv`, the colSpan adv `<tr>`, and the `Fragment` wrapping that needed. Net: ~30 lines lighter. - New `RowActionButton` — square 28×28 icon-only ghost button. Wraps `title` + `aria-label` + `data-testid` for consistency. Variants: default ghost or `tone="error"` (red on hover for destructive). - History → 🕘 (clock). - Adv edit → ✎ (pencil) — distinct from `⚙` used by type-level edit. - Delete → 🗑. - Operator routes the action via tooltip; row stays compact. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Foundation primitive — every clickable in the app should now route through `<Button>`. Agents page is the migration template; remaining features sweep in follow-up PRs. Button changes: - Add `iconOnly` modifier (square aspect, sm/md/lg → 28/36/44 px) - Add `loading` prop (Loader2 spinner, disables click, aria-busy) - Add `tone="error"` (red focus ring + hover for destructive actions) - Add `leadingIcon` / `trailingIcon` slots typed as `LucideIcon` - `cursor-pointer` baseline; per-variant focus-visible rings Icon library: lucide-react (~1-2 kB per icon, tree-shakable, MIT). No other icon deps needed. Agents page migration: - RowActionButton primitive removed → `<Button variant="ghost" iconOnly>` - 🕘 ✎ 🗑 → Clock / Pencil / Trash2 (row actions) - ⚙ → Settings (Edit type) - ▶ ▼ → ChevronRight / ChevronDown (collapse) - ⏸ ▶ → Pause / Play (tier badge pause toggle) - ↺ → RotateCcw (reset tier) - × → X (drawer + dialog close, chip remove, env row remove, tier remove) - ⚠ → AlertTriangle (provider chain validation) Tier number glyphs (① ② ③) kept as semantic numbered markers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>The pause button on the row read as "pause running task" — confusing on idle agents (nothing to pause), and pausing flipped the row to a red ✕ + recovery icons that looked like a crash state. Tier column now shows status only: - healthy: tier number + active model (no controls) - degraded (tier > 1): adds the reset button - paused: red "Paused" pill + Resume + Reset (recovery actions only) Pause/disable moves into the Advanced drawer as a labelled toggle ("Dispatch · Enabled/Disabled"). It's a rare ops action — drawer has the room to explain what it actually does (stop accepting new tasks). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Three fixes from PR review. 1. Autosave race (data loss): - Mutation snapshots payload at fire-time, not at resolve-time, so edits made between PUT fire and resolve can't be rolled back into `lastSavedRef`. - Bootstrap effect now gates on `inFlightSaveRef` so a cache invalidation triggered by another query (or a refetch) mid-save can't stomp the user's in-progress edits. - Drop the redundant `invalidateQueries(["agent-config"])` on onSuccess — the stale-while-revalidate refetch already brings the server snapshot back; the explicit invalidate just doubled writes. 2. Duplicate testids in DOM: - `InstancesTable` previously rendered both desktop table and mobile stack with `hidden sm:*` classes. Both branches lived in the DOM, so every `agent-row-*`, `agent-inspect-*`, etc. testid duplicated and Playwright strict locators would throw. - Add `useMediaQuery` hook (SSR-safe via useSyncExternalStore) and mount only the active layout. 3. Drawer mutual exclusion: - Three independent state vars (editingTypeName, inspectingInstance, advEditingInstance) let drawers stack on top of each other. - Collapse to a single tagged-union state — opening any drawer closes the other two. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>UX polish from PR review. - Tier column displays status only when not paused. Reset moves into the row's action cluster (next to History / Edit / Delete) so the eye doesn't confuse a Reset button for a status glyph — same trap the user hit with the previous red ✕. - Replace ⛽ emoji with the lucide `Fuel` icon (last emoji left after the icon migration). - Provider summary chip on the type-card header now truncates with ellipsis at `max-w-[55vw]` on mobile / `max-w-[280px]` on sm+, so long provider · model strings don't push the header to a third line on narrow viewports. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Three minor fixes: 1. DeleteAgentDialog now uses the new `tone="error"` Button modifier (was raw `border-error text-error` className override). Also wires `loading={mutation.isPending}` so the spinner replaces the text. 2. ProviderSection up/down buttons migrate from raw Unicode arrows to lucide `ArrowUp` / `ArrowDown` via the foundation Button. Closes the last spot where the lucide migration was inconsistent. 3. `model_override` field removed from the GET /agents response shape (was always null), from `AgentsListEntry` on the web side, and from the e2e fixture. PATCH response now forces `model: null` so legacy rows with stale non-null `model` columns don't leak through. The column itself is left in the DB — it's dead but harmless, and a migration to drop it is out of scope. Tests updated to assert `model_override` is undefined on the response. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>