fix(app): stabilize virtual session timeline interactions#28422
Conversation
There was a problem hiding this comment.
Pull request overview
This PR stabilizes the session message timeline’s virtualized row behavior by persisting tool expand/collapse state across remounts and adding a synchronous “measure mounted items” hook to Virtua so recent context group expansions can reflow before the next paint.
Changes:
- Adds a small
virtua@0.49.1patch exposingVirtualizerHandle.measure()and uses it to synchronously remeasure mounted items. - Makes tool open/close state controllable (
open/onOpenChange) inBasicToolandToolErrorCard, and wires controlled state throughMessagePart/MessageTimeline. - Adds Playwright regression tests covering (1) tool collapse-state persistence under streaming updates and (2) context-group expansion reflow/overlap.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| patches/virtua@0.49.1.patch | Adds measure() to the Solid Virtualizer handle by synchronously remeasuring mounted items. |
| packages/ui/src/components/tool-error-card.tsx | Adds controlled open state (open, onOpenChange) for error tool cards. |
| packages/ui/src/components/message-part.tsx | Plumbs controlled tool open state; triggers timeline remeasure on context group expand/collapse. |
| packages/ui/src/components/basic-tool.tsx | Adds controlled open state support and routes open changes via a unified setter. |
| packages/app/src/pages/session/message-timeline.tsx | Persists tool open state in the timeline; calls virtualizer.measure() on context group size changes; refactors row rendering around accessors. |
| packages/app/e2e/regression/session-timeline-context-resize.spec.ts | New regression test asserting no visible overlap/flicker on context group expansion. |
| packages/app/e2e/regression/session-timeline-collapse-state.spec.ts | New regression test ensuring manual tool collapse persists when later assistant parts stream in. |
| package.json | Registers the Virtua patch in patchedDependencies. |
| bun.lock | Adds the Virtua patch entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const row = () => timelineRowByKey().get(props.rowKey)! | ||
|
|
||
| return renderTimelineRow(row) |
| ms: pace(640), | ||
| }) | ||
| const [toolOpen, setToolOpen] = createStore<Record<string, boolean | undefined>>({}) | ||
|
|
| const samples = await sampleExpansion(page) | ||
| const visibleOverlap = samples.filter((sample) => sample.frame >= 1 && sample.overlap > 0.5) | ||
|
|
||
| console.log("context resize samples", JSON.stringify(samples, null, 2)) |
|
Regressione from PR #28334 — renderer crash with TypeError: Cannot read properties of undefined (reading 'group') I'm hitting a renderer crash after updating that maps directly to the refactor in PR #28334 (merged yesterday, May 19). Stack trace: TypeError: Cannot read properties of undefined (reading 'group') The crash occurs in renderAssistantPartGroup inside message-timeline.tsx (~line 984), where row.group.type is accessed but row is undefined. The root cause is that PR #28334 removed the Accessor wrapping, switching to direct values. The internal createMemo calls capture row in a stale closure when session state changes (new messages, streaming, session switch), the memo re-executes but row references a stale value that becomes undefined during SolidJS reactive transitions. Before the refactor, row() was a reactive function that always returned the current value, avoiding this issue. Question: Does this PR (#28422) cover this specific crash? From the modified files (message-timeline.tsx and message-timeline.data.ts) it looks like it should, but I wanted to confirm since the crash is a hard blocker — the app becomes completely unusable and requires a forced restart. Thanks for the work! |
Summary
virtua@0.49.1Solid patch exposingVirtualizerHandle.measure()for synchronous mounted-item remeasurement.before/afterinputs as complete Pierre diffs.Data-driven repros fixed
undefinedtext.Scope
before/afterpayloads remain compatibility input and preserve Pierre'sisPartial: falseinvariant; this PR does not optimize pathological patchless full rewrites.@pierre/diffsand evaluating its newCodeViewAPI for the dedicated review surface are separate follow-ups.Tests
bun test srcfrompackages/uibun typecheckfrompackages/uibun typecheckfrompackages/appbun test --preload ./happydom.ts src/utils/diffs.test.tsfrompackages/appbun run test:e2e e2e/regression/session-timeline-collapse-state.spec.ts e2e/regression/session-timeline-context-resize.spec.ts e2e/smoke/session-timeline.spec.tsfrompackages/appbun turbo typecheckFollow-up
@pierre/diffs@1.1.0-beta.18; stable1.2.xaddsCodeViewand improved virtualized lifecycle/layout handling. Upgrade separately once scoped and eligible under the dependency release-age policy.