cmux NEXT: keep scrolled-up transcript rows anchored during streaming

TE
11d ago
+168-17

Fixes the transcript scrollbar jump while a reader is scrolled up and agent cards or streaming text resize rows above the viewport.

Leo described the symptom (2026-10-25):

“There's some sort of, like, jittering situation going on here. ... the rightmost scroll bar, like that just jumps a bit. ... especially when it comes to ... all of these agent messages back and forth ... trying to scroll down slowly as you're streaming in text output.”

The placement pass now anchors from the last reader offset when a browser has already applied a layout scroll adjustment, avoiding a second correction. Streaming revisions keep the visible row's index and stay O(1) per token; insertion/removal falls back to an ID lookup only when the index changed. The row itself remains fixed when its own content grows.

Regression coverage:

  • conversation/scrollAnchor.test.ts covers above-viewport growth, self-growth, browser-pre-adjusted offsets, and older history inserted above the anchor.
  • Gallery entry agent-pane.streaming-follow-motion, variant stream-scrolled-up, drives a deterministic stream while the transcript is scrolled to 180px and fails if the thumb moves more than 1px.

Gallery evidence (same deterministic fixture, generated through the gallery matrix on cmux-ci): before: the scrolled-up stream can apply the browser delta and the placement correction twice; after: the stream-scrolled-up play holds the scroll offset within 1px through the full stream. The PR capture includes both dark and light states and the step filmstrip.

Checks run locally: focused anchor tests (3 pass, 5 assertions), vp lint on changed files, tsc --noEmit, python3 scripts/cmux-next/prepush-gate.py.

Gallery: agent-pane.streaming-follow-motion/stream-scrolled-up

Gallery artifact (public before/after PNGs and step filmstrip, exact head 2e0ae07c95540cb40d96c58b8902358d0085d05b): before/after gallery artifact

agent-identity footer