You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Thread timeline never unmounts history: 22k DOM nodes and 250-600ms long tasks after scrolling a large thread #1301
Found while profiling the mobile web frontend against a seeded 400k-event database (pnpm seed:perf, #1300).
Numbers (390x844 viewport, Chromium, dev build)
Opening a 9,001-event thread starts healthy: ~2.3k DOM nodes, initial timeline page ~109 KB / ~50 ms.
Scrolling to the top of history grows the DOM monotonically to ~22k nodes and 75,710 px of scroll height. Nothing is ever unmounted.
Every older-page render is a 250-600 ms synchronous long task at desktop speed (multiply ~4x for phone CPUs), so paging through history is a series of visible freezes.
Server-side pagination is already good: anchor-based timeline?beforeAnchorSeq=... pages, ~10 KB / ~40 ms each. The problem is purely client accumulation.
Repro
pnpm seed:perf on a dev instance, plus a jumbo append: pnpm seed:perf -- --projects 1 --threads 8 --events 60000 --seed 42.
Open the largest seeded thread at mobile width.
Hold scroll-up until the first message; watch node count and long tasks in the performance panel.
Suggested approach
Window the timeline: virtual list keyed on the existing stable anchor ids, or collapse turns that scroll far offscreen into cheap summary placeholders that rehydrate on demand. The paging protocol needs no changes.
Supporting real-world production evidence beyond the seeded fixture:
One long-lived conversation reached 44,246 events and 131.7 MiB of event JSON.
Its latest-page server projection alone repeatedly read about 1.7–2.1 MiB / 1.2k–1.5k events.
Warm timeline builds commonly blocked the server event loop for 270–330 ms; immediately after upgrading to 0.37.0, one cold request took 3,578 ms.
Client virtualization will not fix the synchronous server query, but it is still the correct client invariant: scrolling history must not make mounted DOM and composer cost grow without bound. The server-side read isolation evidence is tracked under #1131/#1207.
Root cause (short): useThreadTimelineController prepends every fetched older page into one unbounded loadedTimeline.rows array and ThreadTimelineRows.TimelineRowsList renders all of them as real DOM (no windowing/virtualisation), so mounted DOM grows monotonically (2.4k -> 21k nodes on the 9,001-event seed thread) and each prepend is a 280-590 ms sync task (React mount of ~150 rows + forced layout via readOverflowMeasurement's useLayoutEffect on the growing document).
Linked PR review:Window large mobile timelines #1384 → REQUEST CHANGES — Not linked to the issue but is the fix candidate. With main merged it bounds the mobile DOM (21k -> ~1.7k nodes, long tasks 280-470ms -> 60-170ms on the same repro), but: (1) windowing toggles by swapping the per-row wrapper element type under the same key, so when a thread crosses 40 items or the v
Proposed fix: Window the top-level TimelineRowsList at all viewport widths: keep every row in flow with a stable-id-keyed wrapper that renders real content only near the viewport (IntersectionObserver with ~1 viewport margin) and otherwise a fixed-height placeholder using the last measured height; pin interacted rows and the active turn; never change wrapper component type when windowing is on/off (make it a passthrough instead) so no remount happens. Optionally cap loadedTimeline.rows to N pages around the viewport for memory, and cut per-page constant cost by batching conversation-message-overflow's synch…
Caveats: Measurements are from a Vite dev build (React dev runtime) in headless Chromium on desktop hardware, so absolute ms are inflated vs production and iOS/WebKit momentum behaviour (the main focus of PR Window large mobile timelines #1384's later commits) could not be verified. Fixture was seeded with 8 threads / 34,536 events (largest thread 9,001 events, same as the issue) rather than the 400k base plus jumbo append, so node/px …
The report has copy-paste repro steps, screenshots where the bug is visual, code permalinks at 16ceb3a54, and repro artifacts. It was independently re-run by a second agent (see its Verification section).
Activity
amadad commentedon Aug 12, 2026
Supporting real-world production evidence beyond the seeded fixture:
Client virtualization will not fix the synchronous server query, but it is still the correct client invariant: scrolling history must not make mounted DOM and composer cost grow without bound. The server-side read isolation evidence is tracked under #1131/#1207.
SawyerHood commentedon Aug 18, 2026
Reproduction & root-cause report: https://get-bb.github.io/reports/issues/1301.html
The report has copy-paste repro steps, screenshots where the bug is visual, code permalinks at
16ceb3a54, and repro artifacts. It was independently re-run by a second agent (see its Verification section).ymichael commentedon Aug 21, 2026
Fixed by #2011