Checkpoint of in-progress work:
- renderer: team messages panel/composer, messagesPanelLogic, teamSlice,
AnimatedHeightReveal plus their tests
- main: runtime process usage-stats caching (ignoreCachedMisses, bounded
eviction), alive-run-id helpers, team watch-scope notify wiring
Note: the getTeamAgentRuntimeSnapshot rssBytes expectation in
TeamAgentLaunchMatrix.safe-e2e is environment-dependent and still red.
Final step of the virtualization plan. Turns the virtualized render
path on in production behind a row-count threshold, and adds regression
tests covering every gate.
- `VIRTUALIZATION_ROW_THRESHOLD = 60`. Short lists stay on the direct
render path (no wrapper, no position: absolute, no measurement
churn). Above the threshold the virtualizer takes over. Threshold is
sized so conversations under ~one session of activity don't pay the
virtualization cost; it activates once scrolling through a longer
history.
- `shouldVirtualize` now requires `renderRows.length >= threshold` in
addition to the existing opt-in and scroll-ref checks.
- `MessagesPanel` opts into virtualization for every layout it wires
(inline / sidebar / bottom-sheet). The internal threshold then
decides when to actually enable it, so callers don't need per-layout
heuristics.
- Tests: adds a new `ActivityTimeline virtualization threshold` block
covering (a) below-threshold list stays on the direct path,
(b) no viewport → direct path regardless of count, (c) above
threshold + viewport with `virtualizationEnabled` flips to the
virtualized render path (simulated by clicking "show all" past
pagination).
With this in, #70 → #74 combine to deliver:
- correct IntersectionObserver roots in scroll containers
- atomic render rows with stable keys
- windowed rendering with DOM-measured scrollMargin and measureElement
- auto-on when the cost of direct rendering actually shows up
Second step of the virtualization plan. No virtualization yet. This PR
makes IntersectionObserver-based visibility tracking correct inside
scroll containers (sidebar, bottom-sheet), which is a prerequisite for
virtualizing the timeline.
- Introduces `TimelineViewport` — a grouped contract passed as a single
`viewport` prop on `ActivityTimeline`. Holds `scrollElementRef`,
`observerRoot`, `scrollMargin`, and `virtualizationEnabled`.
- `MessageRowWithObserver` and `LeadThoughtsGroupRow` now create their
`IntersectionObserver` with `root = observerRoot?.current ?? null`
instead of defaulting to the document viewport. Unread marking now
fires when rows enter their real scroll parent.
- `MessagesPanel` resolves the active scroll owner from `position`
(inline from parent ref, sidebar from `sidebarScrollRef`, bottom-sheet
from `bottomSheetScrollRef`) and passes it into ActivityTimeline.
- Tests: stubs `IntersectionObserver` to capture `options.root` and
asserts null when no viewport is passed, and the provided element when
`viewport.observerRoot` is set.
`scrollMargin` and `virtualizationEnabled` are included in the contract
but not consumed yet — they land in follow-up PRs (#4/#5).
- Introduced caching mechanism with expiration for message feeds to improve performance.
- Added logging for cache expiration events to aid in debugging.
- Updated MessagesPanel to reopen search bar when participant filters are active.
- Added test cases for handling tmux server errors and message panel behavior with filters.
First step toward virtualizing ActivityTimeline. Makes the real scroll
container observable per layout, without changing behavior.
- `TeamDetailView` forwards `contentRef` to `MessagesPanel` as
`inlineScrollContainerRef`. `MessagesPanel` accepts it as an optional
prop (unused in this release) so a follow-up can wire the inline
viewport to virtualization consumers.
- `MessagesPanel` creates `bottomSheetScrollRef` and passes it to
`Sheet.Content scrollRef`. react-modal-sheet merges it with its
internal scroll ref, so the element stays the same DOM node; this
only exposes it to us.
- Sidebar already owns `sidebarScrollRef`; no change there.
Behavior is unchanged — this only exposes refs for the follow-up that
will thread a viewport contract into ActivityTimeline.