cmux NEXT: smooth chat follow and Jump to latest; no caret once idle
Closed
TE
18d ago+662+662+012345678901234567890123456789-41-41-01234567890123456789
SHOW LEO: this PR acts on Leo's design feedback, so it holds for his OK on the before/after recording before merging.
Leo's agent-chat scroll feedback in one batch, on top of the pinned streaming from #20374. It matches Codex and Claude desktop: Jump to latest, follow while tool calls and commands stream, and the streaming caret. It folds in #20432 (the idle caret).
Causes
- Jump to latest was jarring. The native smooth scroll's first frame read as the reader scrolling up, so the view stopped following. The button came back mid-scroll, and the pin logic fought the animation.
- Jump to latest landed short. The end was reached only through a hard snap 600 ms later. Any wheel event cancelled that snap, including a trackpad's momentum after the click, so the view stopped short while the reply grew.
- Following snapped and moved strangely. A step taller than the view snapped, and content that shrank snapped down. A new row rose 4px on its own while the thread glided, which made two motions at once.
- Opening a command jumped. Opening or closing a command while following pulled it away by its output, because the view re-pinned to the end.
- The caret kept blinking. Text after the turn's result (a late reply, a background report) opened a streaming row that nothing closed. A reply followed by a newer message also stayed streaming. #18123 was closed unmerged, and 7115d4c15002 covers only the turn end itself.
Changes
- Jump to latest:
- The view follows again from the click, so the button hides and stays hidden.
- It runs its own frames: a 320 ms eased glide to the end, retargeted every frame, so it lands on the end while a reply grows.
- From far up, it starts one viewport above the end instead of racing through every row.
- A wheel toward the end does not stop it; the reader scrolling up does. Reduce Motion lands at once.
- Following:
- Every change glides, including new rows and content that shrank. A step taller than the view glides its last viewport instead of snapping.
- A new row only fades in, because the glide already moves it.
- Scrolled up, nothing moves (as before).
- Opening a command: a row the reader opens or closes while following holds still under the pointer. If its output pushes the end out of view, the view stops following and the button shows.
- Caret: a newer message ends the reply before it, and a chat that is not working closes any reply still marked streaming. Replay is not changed.
Testing
- New browser test
webviews/test/agent-pane-jump-latest.test.tscovers:- Jump to latest is a steady glide, and the button stays gone.
- It lands at the very end through momentum and growth, and keeps following.
- A tool call and a reply taller than the view glide in without moving down. This one passes on base too and is kept as a guard.
- Opening a command at the end holds it still.
- Scrolled up, nothing moves while tool calls arrive.
- New browser test
webviews/test/agent-pane-idle-caret.test.tscovers both caret paths. Its red run on base, on #20432, failed both cases: /manaflow-ai/cmux/actions/runs/38512017391/job/115682707315 test/transcript-jump-latest.test.tsxnow waits for the glide's frames rather than onescrollTo.- The tests were committed first (red) and then fixed (green). Results are in the comments below.
- In-app capture: a recording from a DEV build of this head on a fleet mini will follow in a comment.
- The related jsdom transcript tests pass locally, and
bun run checkpasses. Browser tests run only in CI. - Not covered: a live "Ran N commands" group settling smaller at the end. It glides through the same code path as content that shrank, but no test drives it.
Changelog
- Fixed: Jump to latest glides smoothly to the very end, the chat follows tool calls and commands without snapping, and the streaming caret no longer stays once the chat is idle.
🤖 Generated with Claude Code