Skip to content

feat(web): replace the Resources popover with a trigger, dock and inspector - #631

Merged
Rinat S (rsolmano) merged 7 commits into
mainfrom
redesign-resources-ui
Oct 5, 2026
Merged

Rinat S (rsolmano) merged 7 commits into
mainfrom
redesign-resources-ui

Conversation

@danyaberezun

@danyaberezun danyaberezun commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Background commands and subagents that an agent starts are the work most likely to run while you type, and today they are the least visible: a header popover with three-line rows, a lowercase status word (running and queued look identical at a glance), no elapsed time, no sense of what a row is doing, and separate modal dialogs for logs and transcripts that cover the chat. Nothing is visible while the popover is closed except a bare count, so "is the dev server still running?" means opening a popover and reading a word.

A survey of how Claude Code, Codex, Cursor, Zed, T3 Code, Conductor, Warp, Amp, Antigravity, OpenCode, Kiro and others surface the same thing (COMPETITIVE-ANALYSIS.md, on the review-only assets/resources-redesign ref) converged on the same answer: an ambient live roster where you type, one detail view, one row grammar, and no roster rendered twice.

Approach

The popover is replaced by three surfaces that share one row grammar — the direction the design review settled on ("G"):

  • Trigger (chat header): count of live rows while the snapshot is authoritative, — otherwise; the icon breathes while anything is live; aria-expanded toggles the inspector.
  • Dock (above the composer): live rows only, Stop on hover/focus, the name opens the inspector; collapses past four rows to a summary line; hidden while the inspector is open so the live roster never renders twice.
  • Inspector (over the transcript region): a non-modal in-place Radix dialog — the header and composer stay usable — with the roster as a listbox (Active / Finished, ↑/↓, Esc) and the selected row's detail: bounded command output or the read-only subagent transcript. It replaces the popover, the command-log dialog and the Resources-origin transcript dialog.

Decisions worth a reviewer's eye:

  • Status is glyph + colour + word, collapsing the wire statuses into Working / Queued / Stopping… / Done / Failed / Stopped. A completed command with exit ≠ 0 reads as Failed; a user stop is neutral, not a failure.
  • Authority rules are unchanged: unknown count stays —, a stale snapshot disables Stop everywhere (dock summary says "reconnecting"), Stop means "requested", closing anything never stops work, an old host hides all three surfaces.
  • The inspector lives inside the chat column — no new shell pane, no workbench resource kind — and its detail body is a slot the parent fills, so the props-only chat/resources module still imports nothing from store/transport. Time arrives as a now prop (the shared 30 s clock) rather than widening that boundary.
  • Radix keeps the a11y guarantees: a new DialogPanel primitive renders Dialog.Content in place (no portal/overlay), so Esc, focus return to the trigger (composer fallback when an older host removes it) and the dialog role come from Radix, not hand-rolled handlers. Dock rows are real <li>s whose name is the Inspect button; inspector rows are listbox options — no nested interactive elements.
  • Needs-you state is deliberately out of scope: subagents run headless today, so there is nothing to render yet.

Changes

  • apps/web/src/chat/resources/: new ResourcesDock, ResourcesInspector and a private resourceRow.tsx (state collapse, sorting, elapsed formatting, the shared row); ResourcesButton breathes while live and carries aria-expanded; CommandLogView drops its redundant status line; ResourcesContent deleted. Public surface: ResourcesButton, ResourcesDock, ResourcesInspector, CommandLogView (spec-surface checked).
  • components/ui/dialog.tsx: DialogPanel — in-place, non-modal content; caller positions it.
  • chat/SubagentTranscriptDialog.tsx: SubagentTranscriptPane extracted (polling + rows); the dialog wraps it, the inspector embeds it.
  • chat/ChatView.tsx: one inspector state (open + selected row, chat-scoped); trigger toggles it; dock mounts below QueueStrip only while the inspector is closed; inspector mounts inside the chat-scroll region; useCommandLog keys off the selected command only; the command-log Dialog and the Resources-origin transcript bookkeeping are removed; knownUnsupported closes the inspector and the Stop-all confirmation.
  • chat/turns.tsx: TurnDivider accepts an optional stillRunning deep link; ChatView supplies it for the chronologically latest divider while live resources exist ("N still running" chip → inspector).
  • Specs: chat/resources/SPEC.md respecified (responsibility, boundary, row grammar, presentation of all three surfaces, verification obligations); chat/SPEC.md Chat Resources + transcript-pane paragraphs; components/ui/SPEC.md (DialogPanel); UI-TERMINOLOGY.md (Resources section, test ids); e2e/SPEC.md wording.
  • Tests: chat/resources/resources.test.tsx rewritten (boundary, vocabulary, trigger, dock, inspector, command log); e2e/chat-resources.spec.ts rewritten around the inspector (empty state + dock absence, composer click does not dismiss, focus return, phone width, reconnect/reload, old host, retirement with an embedded transcript); e2e/chat-resources.live.spec.ts translated to the new ids (provider-backed — not run here).
  • Not in the tree: the research and the interactive mockups (directions A–G plus the workbench-wide application) that led here live on the review-only assets/resources-redesign ref — the spec is the durable record of the decisions; the mockups are already drifting from the shipped rows.

Not in this PR (follow-ups from the design review): merging QueueStrip into the dock as one composer tray, Inspect on the Agent/background-command cards, a workspace-scoped inspector (needs a wire read), and the chat-tab / rail status vocabulary.

  • packages/contracts + packages/server: an additive optional SubagentResourceSummary.durationMs — pi-delegation's measured run time, exposed once a child is terminal — so settled subagent rows report a duration like completed commands do (from review).

Screenshots

Same scenario (an injected SessionResources snapshot: 2 live commands incl. one stopping, running + queued subagents, 3 finished) at 1440×900, dark theme. Before captured at the base commit, after on this branch's final tree.

Before — header popover After — dock above the composer
before: popover after: dock
Before — logs in a modal dialog After — inspector with live command output
before: log dialog after: inspector, command

After — a subagent selected in the inspector (its transcript pane embedded):

after: inspector, subagent

Related issues

None.

Checklist

  • Fast gates pass: bun run lint, bun run typecheck, bun run test
  • E2E suite passes for app-affecting changes (bun run e2e, or bun run e2e:full when touching agent behavior)
  • Before/after screenshots are included for frontend changes, or marked not applicable
  • Relevant SPEC.md / top-level specs updated to reflect any boundary, contract, or behavior change
  • I have read the Contributing guide and agree to the Code of Conduct

Testing

On the final rebased tree (origin/main = #628):

  • bun run e2e -- --serial e2e/chat-resources.spec.ts e2e/chat-resources-wire.spec.ts → 5 passed
  • bun run e2e → all 8 shards passed, 465 tests (run on the pre-rebase tree; the rebase pulled in feat(web): borderless labelled composer controls #628's composer styling only, so the resource specs above were re-run and the rest of the suite's inputs are unchanged)
  • bun test (apps/web) → 1318 pass
  • pre-commit gate chain (check:deps, check:boundaries, check:seams, typography:check, colors:check, spacing:check, provider-glyphs:check, bun run lint, bun run typecheck) → all green, with one caveat: @thinkrail/desktop#typecheck could not run locally — its electrobun prepare step blocks on the build engine held by the running desktop dev app (0 CPU for 13 min). apps/desktop is untouched by this branch; the other 17 packages typecheck clean. CI will run it.
  • bun run check:spec-surface → OK (20 enrolled, 20 compared)
  • Not run: e2e/chat-resources.live.spec.ts (provider-backed; translated to the new ids and cross-checked against the components' test ids)

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — ready to merge.

Comment thread apps/web/src/chat/resources/resourceRow.tsx Outdated
Comment thread apps/web/src/chat/resources/resourceRow.tsx Outdated

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes — please address the blocking inline finding.

Comment thread apps/web/src/chat/resources/ResourcesInspector.tsx Outdated
Comment thread apps/web/src/chat/resources/resourceRow.tsx Outdated

@jetbrains-air jetbrains-air Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — ready to merge.

Comment thread apps/web/src/chat/resources/resourceRow.tsx
@danyaberezun
danyaberezun force-pushed the redesign-resources-ui branch from 27d6056 to 0e0ebee Compare October 5, 2026 11:17
@rsolmano
Rinat S (rsolmano) added this pull request to the merge queue Oct 5, 2026
Merged via the queue into main with commit bdf39eb Oct 5, 2026
7 checks passed
@rsolmano
Rinat S (rsolmano) deleted the redesign-resources-ui branch October 5, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants