* fix(transcript): auto-follow live task output in transcript dialog (#5921)
Since the transcript event list moved to Virtuoso (#5733), a live task's
new events required manual scrolling to see — the dialog opened at the
top and never followed appended output.
Wire live-follow through Virtuoso's own primitives, per sort direction:
- Chronological (default): `followOutput` returns "smooth" while the
task is live and the reader is at the bottom (Virtuoso's own atBottom
tracking, with a forgiving 120px threshold). Scrolling up suspends the
follow until the reader returns.
- Newest-first: growth is PREPENDS, which the firstItemIndex anchoring
deliberately holds in place — so a reader parked at the top would
silently stop seeing new rows. Track the top edge via
`atTopStateChange` into a ref and snap back to index 0 when new events
arrive while at the top. Readers who scrolled away are left in place.
- Opening a live chronological transcript now lands on the newest event
(`initialTopMostItemIndex: LAST/end`, same pattern as the chat list);
the per-listEpoch remount re-applies it after task/sort/filter
changes. Completed tasks keep opening at the top.
Fixes#5921.
* fix(transcript): rework newest-first live follow as a user-intent latch (#5921)
The previous approach gated the newest-first follow on "is the viewport at
the top right now" — but firstItemIndex prepend anchoring moves the viewport
away from the top on every flush, so the signal broke itself: after the
first prepend the follow silently disengaged and never recovered.
Replace it with a pure latch controller (transcript-follow.ts):
- Disengage only on accumulated USER displacement (wheel/touch/key deltas,
scrollbar drag) beyond the 120px edge zone; system displacement never
counts, no matter how far it pushes the viewport.
- While following, non-user displacement is pinned back to the live end on
the scroll event itself (Virtuoso's prepend compensation lands after React
effects, so an effect-timed snap alone stays one flush behind).
- Enforcement is suppressed while the mouse is held (text-selection
autoscroll) or mid-gesture; returning within the edge zone re-engages.
- Timeline segment clicks explicitly unlatch so navigation isn't pinned back.
Chronological followOutput switches "smooth" -> "auto": a still-animating
smooth scroll reads as "not at bottom" on the next flush and drops the
follow under rapid appends.
The latch decision table is unit-tested (transcript-follow.test.ts), and the
mechanism was validated end-to-end against react-virtuoso 4.18.7 in a
browser harness: follow-at-top, in-zone nudge + flush (no false disengage),
scroll-away with stable anchor, return-to-top re-engage, rapid flushes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>