bug(ui): transcript scroll geometry lost its warm-up, and the scrollbar drifts again on long sessions
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 502
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 715
Description
## What happened
On a long session the scrollbar thumb compresses and repositions while the reader scrolls up, and `scrollHeight` keeps changing without any content arriving. This is the symptom #828 fixed in July: never-rendered content occupies an estimated height until Chromium gets around to laying it out, so each upward step inflates the document and scroll anchoring compensates — the top keeps receding.
Since #5170 the Renderer also decides *from this geometry* when to fill an edge and what to trim (`packages/ui/src/use-chat-scroll.ts`, the two-screen fill and six-screen trim bands). Estimated heights that later grow make those bands fire early or late. That is a performance problem now, not a correctness one — the anchored window refuses anything it cannot splice — but it is the same root cause.
## The chain
| PR | What it did to the guard |
| --- | --- |
| #828 `8a47301a5` | Added `turn-size-warmup.ts`: a chunked, bottom-up idle walk that forces each Turn through one rendering opportunity so the browser records its size. Measured: a 24-Turn session mounted at `scrollHeight` 14640 vs 31872 real, 51 anchor-corrected steps to reach the top; after warm-up, exact geometry and monotonic `scrollTop`. |
| #2462 `2f5c75be0` | Deleted `turn-size-warmup.test.ts` (8 cases) and `e2e/scroll-geometry.spec.ts` (565 lines) as "low-value" — the only tests that would have failed on the steps below. |
| #2975 `6b8e3db26` | Deleted `turn-size-warmup.ts` and `turn-size-index.ts`, replaced by a row virtualizer with spacers. Geometry was now the virtualizer's estimate. |
| #4206 `c33617fd8` | Deleted the virtualizer, height index, spacers and compensation state. Did not restore the warm-up. |
| #4259 `d2d9f9060` | Bounded rendering *within* oversized Turns with `content-visibility` line-block chunks (`packages/ui/src/styles.css:744`). The estimate is now per chunk inside a Turn, with nothing walking chunks to measure them. |
Result: the browser's remembered-size mechanism is once again the only thing turning estimates into measurements, and it only runs for what scrolls into view — exactly the situation #828 measured.
## How to reproduce
1. A session with 20+ Turns of mixed length, at least a few long enough to be chunked by #4259.
2. Open it at the tail, then scroll to the top with the wheel or the scrollbar.
3. Watch `scrollHeight` (or the thumb): it grows step by step during the ascent; the top recedes.
The deleted `e2e/scroll-geometry.spec.ts` (`git show 2f5c75be0^:apps/desktop/e2e/scroll-geometry.spec.ts`) asserted constant height and monotonic `scrollTop` for this gesture. Restoring it is the regression; it should fail on `main` today.
## Expected
Geometry that does not change under the reader when no content changed. Either bring the idle warm-up back for whatever now carries the estimate (line-block chunks after #4259), or seed chunk heights from measurement the way #2224 proposed for Turns. The regression that guards it must come back with the fix; #2462 deleting it is how the next three PRs got through.
## Not in scope
The Renderer-owned window and its fill/trim bands (#5163 / #5170). They consume this geometry; they do not produce it.
Contributor guide
Assessment
This issue has not been assessed yet.