Repository navigation
cmux NEXT: expanding a tool call or thought animates and never moves your view - #23093
Conversation
…the clicked line (red) Followed to the latest row, opening the last turn's "Worked for" puts the view at the new latest row, so the line the reader clicked jumps up by everything it opened (453.5px -> 235.5px here). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray
…your view Every transcript collapsible (tool groups, tool, command and MCP rows, a settled run, a folded thought) opens and closes in one fold: height and opacity over --cv-fold-duration (the host's move duration, ~190ms, 0 under Reduce Motion or with animation off), easing out, the chevron turning on the same token (fold.ts). The clicked line stays on its pixel while its fold plays: VirtualTranscript holds it (foldAnchor.ts), following neither the latest row nor the top row, and stops following when what opened pushed the latest row out of view. Covers "Worked for" too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Unclear: CI failed on
Failures:
Matched log linesNot re-run automatically: Written by red: web / Web status; NEW: green on the base 0f6199c; do not merge until green; see lane-rules
Agents: Tour of |
|
Owned by projects-62 on cosmic-gray · session |
|
Owned by projects-62 on cosmic-gray · session |
|
Bead |
|
Found 18 test failures on Blacksmith runners: Failures
...and 8 more test failures. View all on Blacksmith
|
|
Review (claude-review-subagent): BLOCK at 66f967e Blocking
Notes (non-blocking)
Ran (scratch worktree at the head)
|
…se-anchor-anim # Conflicts: # webviews/src/agent-session/acpmux/App.tsx
The close animation has no fill, so when it ended the .cv-fold box went back to full height and opacity for the frame before React committed setClosing(false), and the rows below jumped. onfinish now pins the box shut (height and opacity 0, clipped) before it asks React to remove the body, and any later fold clears the pin first, so a box reopened in the same frame opens from its own size. An open still lets go of the height it measured, so content that loaded meanwhile shows at its own height. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray
|
Review (claude-review-subagent): SHIP at 5f7be0d Independent review of the whole PR diff against its merge base with Blocking: none. The close pop (earlier BLOCK) is fixed
Non-blocking
Also checked
CI (glaeda status, head 5f7be0d): the fold suites pass in CI: transcript-fold-motion, transcript-fold-anchor, transcript-fold-anchor-browser and ThoughtRow.test. Not run: no local tsc or bun test (blocked on this host by policy), and no live app or gallery play of |
|
Red checks on 5f7be0d are base reds, not this PR's: every failing test in web / react-apps-check (1)-(4) also fails on unrelated #23131 (run 39561886342), e.g. composer history, @-picker, Ctrl-B hint, suggested prompt, Full access chip colour, location strip, queue stack. The typecheck step exits 1 on 0 errors / 1 base warning. The fold tests this PR adds pass. Merging after review SHIP at this head. |
…ld timer check:motion-scale was in webviews' `bun run check` but not in ci-web's warn-mode loop (test_cmux_next_checks_workflow). foldAnchor's FOLD_SETTLE_MS (from #23093) is a hold timer after a fold, not a transition, so the motion-scale scan allows it by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray
|
Shipped in:
|
…are one short scale (#23092) * test(newtab): a live resize reads no list row on its frames A live window resize calls the New Tab lists' ResizeObserver every frame, and listMotion read every row's place there (a forced layout per frame). This pins that the rows are read once, after the size holds still. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * fix(newtab): the page lays itself out in CSS while the pane resizes - The field sizes itself (field-sizing: content); the JS fit and its ResizeObserver run only in an engine without field-sizing. - The top padding is one continuous curve in the pane's height, so the composer no longer jumps 55px when a resize crosses 480px tall. - listMotion's ResizeObserver reads no row on a live resize's frames; the places are read once the size holds still for 120ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * feat(motion): one motion duration scale both the app and the webviews read schemas/motion-scale/motion-scale.json holds the scale (quick 80ms, micro 120ms, standard 180ms, large 240ms, ease-out on enter). scripts/cmux-next/gen-motion-scale.py writes its Swift (MotionScale), CSS (--cmux-motion-*) and TypeScript (MOTION_MS) copies; CI and the webviews check run it with --check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion: put Swift and webview durations on the 80/120/180/240 scale Every fade, spring and CSS transition now reads the shared motion scale (schemas/motion-scale/motion-scale.json) instead of its own literal: MotionTunables fades and springs on the Swift side, the host CSS tokens, page literals, Tailwind classes and JS motion constants on the webview side. Springs are critically damped and at rest within the standard step. A static scan (webviews/test/motion-scale.test.ts) fails on any new off-scale duration; MotionScaleTests pins the Swift side. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * test(webviews): New Tab page follows a live window resize Sweeps the New Tab page's iframe from 1100x760 to 480x400 and back, one size per frame, and fails on a composer jump, an inline field height, a missed frame budget or ResizeObserver work per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion: pane close test follows the faster split spring At response 0.23 s the close is about 0.41 of its travel at 50 ms (it was 0.32 at 0.28 s), so the early-progress window moves to 0.3..0.55. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * New Tab: scroll a capped self-sizing field to the caret in WebKit WebKit sizes the field with field-sizing but leaves a draft past the cap scrolled where it was, so typing at the end hid the caret (composer-draft-wrap "webkit: the New Tab field ... grows to its cap"). On a draft change with the caret at the end, scroll it into view; a resize still runs no script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Thread minimap test: per-frame bound fits the 180ms shift glide SHIFT_MS moved from 200 to the scale's standard 180ms, so the fastest frame of the glide is ~40.8px, just over the old 40px bound. 48px still fails a one-frame jump (~150px). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion scale: run its check in ci-web's warn branch; mark the fold hold timer check:motion-scale was in webviews' `bun run check` but not in ci-web's warn-mode loop (test_cmux_next_checks_workflow). foldAnchor's FOLD_SETTLE_MS (from #23093) is a hold timer after a fold, not a transition, so the motion-scale scan allows it by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * New Tab lists and motion scale: review fixes - listMotion: a row arriving resizes the list itself; the observer then reports the size the effect just read. That no longer marks the places stale, so a second change right after (two tabs closed in a row) still animates. Test: "the list's own size change from a row arriving leaves the next change animated". - motion-scale test: compare MOTION_MS as entries (TS2769 on the readonly literal type in `bun run typecheck`). - RevealedMarkdown: the fresh span outlives the 180ms default-speed fade (standard step), so a reveal's tail no longer pops. - Reduce Motion crossfade in the webviews follows Swift's (quick, 80ms). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion scale test: type the generated-copy comparison as [string, number] pairs Object.entries(MOTION_MS) is typed with the literal step values, so toEqual refused the JSON's plain numbers (TS2769 in the webviews type check). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * test(newtab): mark the resize tests' fixed waits and frame read for base's new ratchets check:fixed-waits (#23365) and check-frame-reads joined the base's web lint after this branch's tests were written; each wait and the sweep's frame read says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Opening or closing anything in the agent chat's transcript no longer moves your view. The line you clicked (a tool group, a tool, command or MCP call, a settled run, a folded thought, a turn's "Worked for") stays on the pixel you clicked it at while what it opens lays out. Before, a chat following its reply jumped to the new latest row as soon as something opened, so the line you clicked shot up by everything it revealed.
Every one of those collapsibles now opens and closes in one motion: the body's height and opacity, easing out, with the chevron turning on the same duration. Under Reduce Motion, or with the app's animations off, it opens and closes at once.
How it holds still. A capture-phase click on a disclosure line (
foldAnchor.ts) records where the line is drawn. Until its fold has played (plus 150 ms for the last measure and the rows "Worked for" inserts), each placement pass inVirtualTranscriptputs that line back by moving the offset. It does that instead of following the latest row or keeping the top row. When you're following the reply and what opened pushes the latest row out of view, the view stops following (Jump to latest shows), so the reply streaming on doesn't pull you away. There's one case where the line can still move: closing something at the very end of the transcript. There's nothing below to fill the view, so the browser clamps the offset and the rows above come down by what closed, as in any scroller.The motion.
useFold(fold.ts) keeps a closing body drawn until it has slid away. If you reverse a fold midway, it starts from where it is drawn. It reads--cv-fold-duration, which conversation.css sets from the host's move duration (--agent-motion-move, AgentPaneTheme.swift: about 190 ms, and 0 under Reduce Motion or with animation off), with 180 ms when there's no host. The chevron (.cv-rotor) turns on the same token. The newtab-resize-anim lane owns shared duration tokens but hasn't published any yet. I messaged it, and this is the single alias for it to repoint.ThoughtRowdrops its own 200 ms height-only animation for the shared fold. Only the reader's toggle plays it; the automatic fold still snaps.The rows "Worked for" opens are whole virtual rows. They get the transcript's existing row entrance, not a height fold.
Changelog
Observed / Not run
Ran locally (bun 1.4.2,
webviews/):bun test test/transcript-fold-anchor.test.tsx: red on base 7a4b464 (the "Worked for" line moved from 453.5 px to 235.5 px down the view), green on this head (2 pass). The test is committed first (e9418c6).bun test test/transcript-fold-motion.test.tsx: 13 pass. Each collapsible plays height and opacity over the token, keeps a closing body until it finishes, and doesn't animate under Reduce Motion, withdata-agent-motion="off", or with a 0 ms token. A reversed fold starts from its drawn height, and the CSS ties the token to--agent-motion-moveand the chevron.ThoughtRow.test.tsx(20 pass), 21 other transcript, find and tool-row jsdom suites (109 pass, 0 fail) andgallery-coverage.test.tspass.bun run check: formatting, layers and type check pass. It exits 1 on one lint warning,jsx-a11y(prefer-tag-over-role)insrc/pages/history/HistoryPage.tsx:177. This PR doesn't touch that file, and the warning is the same on base 7f3a43a.Not run locally (browser tests run only in CI here):
test/transcript-fold-anchor-browser.test.ts(Chromium). It checks that the clicked line moves at most 1 px on every frame of the fold, while following a streaming reply and for "Worked for", and that no fold plays under Reduce Motion.agent-pane.transcript/fold-holds-clicked-line. At the latest row it opens Worked for, then a command, then closes it, and fails if any line moved.Agent session: cosmic-gray
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Opening or closing a tool call, thought, "Worked for", or any other collapsible in agent chat now animates and keeps the clicked line exactly where it was, instead of jumping the view.
--cv-fold-duration(the host's move duration, ~190 ms), with the chevron turning on the same token.Written for commit 5f7be0d. Summary will update on new commits.