Repository navigation
🪢 feat: Collapse Activity Folds From Their Rails - #16637
Conversation
Hovering an open fold's rail lights the line and swaps the header's glyph for a collapse knob; clicking the rail collapses that fold and brings its header back into view.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Collapsed retained panels can preserve stale hover state, nested headers can remain obscured, and multi-icon headers shift on hover.
Review effort: Balanced
Findings: 3
Open (3)
What changed in this PR
Adds pointer-accessible rails for collapsing activity phases and tool groups.
Changes:
- Introduces shared rail hover, glyph, collapse, and header-reveal behavior.
- Integrates rails into nested activity folds and preserves failed-row interactions.
- Adds unit and end-to-end coverage.
| File | Description |
|---|---|
e2e/specs/mock/activity-fold.spec.ts |
Tests nested rail hover and collapse behavior. |
client/src/components/Chat/Messages/Content/ToolCallGroup.tsx |
Adds tool-group rail controls. |
client/src/components/Chat/Messages/Content/rows.ts |
Moves rail rendering out of shared CSS. |
client/src/components/Chat/Messages/Content/rail.tsx |
Implements shared rail behavior. |
client/src/components/Chat/Messages/Content/ProgressText.tsx |
Allows rail interaction through failure stripes. |
client/src/components/Chat/Messages/Content/ActivityPhaseGroup.tsx |
Adds phase rail controls. |
client/src/components/Chat/Messages/Content/__tests__/ToolCallGroup.test.tsx |
Covers tool-group rail collapse. |
client/src/components/Chat/Messages/Content/__tests__/rail.test.tsx |
Tests shared rail lifecycle and scrolling. |
client/src/components/Chat/Messages/Content/__tests__/ActivityPhaseGroup.test.tsx |
Covers phase rail collapse. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Review head: a8c1694. Preserves glyph layout during rail hover, clears hover for retained collapsed bodies and closed parent phases, and reveals nested group headers below the sticky phase header. Includes unit and browser regressions. |
|
Review head: 9deef1d. Adds the host message’s existing toolbar scroll inset to phase and nested-group header reveals. Browser regressions now retain space below the fold and verify both revealed headers are pointer-accessible. Prior glyph-layout and retained-hover fixes remain included. |

Summary
An open activity fold (a phase card or a tool group) draws a hairline down the left of its rows, but the line is decoration only: closing a long fold means scrolling back up to its header. This makes the rail a collapse control, the way Reddit's thread lines work.
Bringing the pointer near an open fold's rail lights the line, and the header's glyph turns into a ⊖ knob, so the reader sees which fold will close before clicking. Clicking anywhere in the 24px inset beside the rows collapses that fold. If the header has scrolled out of view, it is brought back first. Nested rails work independently: a tool group's rail sits inside its phase's rows, so hovering it marks and closes only the group.
The header button stays the one control for keyboard and screen-reader users. The rail is a pointer shortcut, hidden from assistive tech and never focused.
How it works
rail.tsxholds the pieces both folds share. The rail lives in the panel and the knob in the header, so they share a tiny external store rather than state on the fold. A hover repaints only the header glyph, not every row under the fold.FOLD_RAIL_CLASSESdrops itsbefore:pseudo-element hairline;FoldRaildraws the same line (same position,border-medium,text-secondaryon hover) inside a hit area that fills the inset.handleRailCollapsedoes nothing once the fold is closed: the rail stays drawn while the panel animates shut, so a second click must not reopen it.revealFoldHeaderscrolls the card to its start when its header is pinned (sticky phase header), otherwise brings the header just into view.before:pointer-events-none.Type of change
Testing
Mock e2e lane (
activity-fold.spec.ts, Chromium, memory Mongo). On the failed-reply scenario, the test hovers the group rail and the phase rail and checks that exactly one knob shows, in the hovered fold's header, and that the line colour changes. Clicking the group rail closes only the group; clicking the phase rail closes the phase.Tested environments/configuration:
Mock Provider FAutomated tests:
__tests__/rail.test.tsx(header reveal, knob dropped on unmount under the pointer)ToolCallGroup.test.tsxandActivityPhaseGroup.test.tsx; each fails with its guard removede2e/specs/mock/activity-fold.spec.ts