fix(ui): tighten grouped activity spacing - #446
Conversation
There was a problem hiding this comment.
Pull request overview
Tightens the visual hierarchy of chat activity runs by reducing padding for tool/thought rows rendered inside an expanded activity run, while leaving the run header and non-grouped activity rows unchanged. It also expands the mock showcase and bundled webview smoke test to exercise tooltip-backed and bodyless activity rows under the new spacing.
Changes:
- Scope denser vertical padding to the first child header of each collapsible activity row inside an expanded
ActivityRun. - Extend the showcase mock activity run with a “bodyless” read tool call entry.
- Add a bundled browser-smoke assertion that expanded activity-run children render with denser padding than the run header, covering tooltip-trigger and non-tooltip rows.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/presentation/ui/src/chat/activity-run.tsx | Applies a scoped Tailwind arbitrary selector so nested activity rows render with tighter vertical padding inside expanded activity runs. |
| packages/client/workbench/src/mock/data/showcase.ts | Adds a bodyless read tool-call fixture used to exercise non-tooltip activity rows in the showcase thread. |
| apps/webview/e2e/browser-smoke.e2e.mts | Adds a smoke check that expands the showcase activity run and validates nested row hierarchy/padding (including tooltip-trigger + bodyless rows). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
ℹ️ One assertion-message nit inline; the change itself verified out cleanly.
Reviewed changes — a one-line spacing fix in ActivityRun plus the mock row and browser-smoke check that pin it. This PR is stacked on ruocheng/code-576, so the diff is against that branch, not master.
- Denser grouped rows —
activity-run.tsx:157extends the expanded-group body class with[&>[data-slot=collapsible]>*:first-child]:py-0.5, dropping each direct child header frompy-1(4px) topy-0.5(2px) while the group trigger and top-level rows keeppy-1. - Bodyless showcase row —
showcase.tsadds areadcall with nocontent,rawInput, orlocations, sohasToolBody()is false and its header renders as a plaindivinstead of a trigger button. - Browser-smoke hierarchy check —
verifyActivityRunHierarchyexpands the mixed showcase run and asserts every child header'spadding-block-startis non-zero and smaller than the group header's.
What I verified rather than assumed:
- The arbitrary variant actually compiles and wins the cascade. Running the candidate through
@tailwindcss/node(v4.3.3) emits… > [data-slot=collapsible] > :first-child { padding-block: calc(var(--spacing) * 0.5) }— specificity(0,3,0)against.py-1's(0,1,0), and emitted after it in the same layer.@source "./"inpackages/presentation/ui/src/styles.csscovers the file, so the class is scanned. - All three header shapes are matched. Every branch of the
run.items.mapbottoms out in a coss-uiCollapsibleroot whose first element child is the header:ToolCallItem/QuestionCallItemviaTool,ThoughtBlockviaReasoning. The tooltip-backed case is base-ui'sTooltipTrigger render={…}, which merges onto the same node rather than wrapping it, so*:first-childstill lands on the real header. - The new check is CI-enforced, not local-only —
ci.yml:215runspnpm -F @linkcode/webview e2e:browserunconditionally on pull requests — and it genuinely fails without the fix, since the children would otherwise measure equal to the group header. - The mock addition doesn't disturb the one suite that asserts against the showcase session:
dev-mock-transport.test.tspasses (13/13) with the new tool call. Resulting row height is 24px (text-sm's 20px line box + 2×2px), exactly the WCAG 2.2 AA target minimum, and no theme overrides--text-sm--line-heightor--spacing.
Claude Opus | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — b3c819f is test-only hardening of the browser-smoke check added in 8f31665; the ActivityRun spacing fix and the showcase mock row are unchanged since the last review.
- Loosened the activity-run locator — the exact accessible name
'Activity details: An action failed · Ran a command · Made a file change · Explored 2 times'is replaced byRE_ACTIVITY_RUN_DETAILS(browser-smoke.e2e.mts:25-26), so the check no longer breaks when a clause count shifts. - Added failure messages to the two bare asserts — the tooltip-trigger and bodyless-row checks now dump
metrics.children, matching their neighbours.
The one thing worth verifying about the looser locator is whether it can now match a second button and trip Playwright's strict mode. It can't — I enumerated every activity run the showcase thread renders and derived each aria-label:
- Bursts are separated by
agent-message-chunknarration (dev-mock-host.ts:1391-1428), and a non-tool item flushes the in-progress run (activity-groups.ts:85-88), so each burst becomes its own run. explore→ Explored 4 times;files→ Made 6 file changes;commands→ An action failed · Ran 3 commands;afterTask→ Made a file change · Explored once. None carries all four clauses the regex requires.- Only
activityRun.beforeNarrationmatches, and only its first five items: the precedingagent-thought-chunk(dev-mock-host.ts:1416) joins asACTIVITY_RUN_GLUE_KEYglue whilemock-activity-integrationclassifies asbrand:linearand flushes the run (activity-groups.ts:44-46,90-91). Reasoning + read + read-bodyless + execute-failed + edit — exactly the 5 childrenchildren.length >= 5expects.
Dropping Explored 2 times from the locator doesn't cost coverage either: the bodyless row that clause stood for is pinned directly by the tagName === 'DIV' assertion, which is now the one that reports what went missing.
Claude Opus | 𝕏

Summary
Why
Grouped activity rows used the same block padding as top-level rows, which weakened the visual hierarchy. The group now scopes denser padding to each direct child header.
Validation
pnpm check:cipnpm test --maxWorkers=2 --maxConcurrency=2pnpm -F @linkcode/webview run e2e:browserLinear: CODE-376