Skip to content

Agent chat: opening or closing Worked for or a subagent group slides the rows below and fades its steps - #24514

Merged
teamleaderleo merged 4 commits into
feat-cmux-nextfrom
tp/fold-turn-motion-c
Oct 23, 2026
Merged

teamleaderleo merged 4 commits into
feat-cmux-nextfrom
tp/fold-turn-motion-c

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Leo-ask: ask-a5c1976d1c
Leo-ask: ask-d974edac49
Leo-ask: ask-c07aebfca2
Leo-ask: ask-0bfebc52fd

Leo: "and we shuold also have some animation for collapsing and expanding". Also: "these situations when collapsibles get expanded i said earlier last night lawrence was talking about how it shouldnt move your scroll or whatever just expand the area at the bottom and you're in the same place... also this current one seems to shift the expandable area away also these all need animations for expand and collpase!" And: "and thne there were a few animations that didnt quite work for collapsing and expanding wrt tool calls and stufff".

This is the second half of #24050, split out to stay under 800 lines. #24050 (merged) routed every in-row collapsible through useAnchoredCollapse. Two toggles were left: "Worked for" and subagent groups. Those two add or remove whole virtual rows, so the in-row fold can't size them. On base they open and close in one frame, with only the chevron turning.

Scope: motion only. This PR doesn't change what a "Worked for" fold holds (that's turns.ts, #24300) and doesn't touch the one-row summary for runs of tool calls that another lane is building.

What this PR does

conversation/turnFold.ts plays these two toggles on the shared fold token (--cv-fold-duration):

  • Opening: the rows below the clicked line slide down from where they were drawn (a FLIP on translate, which never changes layout, so nothing the virtualizer measures moves), and the steps the fold reveals fade in.
  • Closing: inert copies of the hidden steps fade out where they were, while the rows below slide up over them.
  • Stays put: the clicked line and every row above it. VirtualTranscript's hold from Agent chat: opening or closing Worked for slides the rows below and fades its steps in or out #24050 keeps the line on its pixel, and the motion plays on top of that hold.
  • One clock: every row plays on the same clock (a row that mounts mid-fold joins with a negative delay), so rows that mount or unmount while the fold plays keep their motion. A second toggle settles the playing fold first and keeps the clicked line where it is.
  • When a pass runs: only when the rows below the line are placed again. A MutationObserver (watchTurnFold, started at the toggle) watches the thread's row elements mounting or unmounting (childList, no subtree) and each mounted row's style and aria-posinset. Rows are placed by translateY from their absolute layout top, so a token streamed into a row changes no row's placement and runs no pass. A render only checks, in O(1), whether the fold is over or another chat shows.
  • Stop condition and cleanup:
    • Observer: it disconnects when the fold ends. That happens on its timer (the fold token plus FOLD_SETTLE_MS), on the next toggle, on another chat, when a pass finds the fold over, or when the transcript unmounts (endTurnFold through fold.stop, from the timer or settleTurnFold).
    • Timer: the fold's one setTimeout (the token plus FOLD_SETTLE_MS) ends it, and each ghost copy's drop timer is bounded by the same end.
    • Animations: each slide and fade is a finite Web Animation on the fold token. settleTurnFold finishes them on the next toggle, another chat or unmount.
    • Memory: an ended fold lets go of its tracked rows, animations and placement snapshot, and the ghost copies are removed.
    • Motion off: nothing runs under Reduce Motion, with data-agent-motion="off" or a 0 ms token, or without Web Animations. In those cases the fold opens and closes at once.

Changelog

  • Changed: Opening or closing "Worked for" or a subagent group in agent chat now slides the rows below and fades the steps in or out, and the clicked line stays in place.

Frame strips

These are gallery fixtures, captured with headless Playwright at 760x640 in the dark theme. The fold token is slowed tenfold to 1800 ms. Each frame label gives ms since the click, the clicked line's y offset (dy) and the number of running animations; DUP would mark an element running two at once.

Kind base head, Chromium dy (open / close) head, WebKit dy double animations
Worked for no motion: only the chevron turns, rows snap 0 / 0 0 / 0 none
Subagent group no motion on close; a 2-animation blip on open 0 / 0 0 / 0 none

Worked for, base (rows snap):
before worked for chromium

Worked for, head (Chromium, then WebKit):
after worked for chromium
after worked for webkit

Subagent group, base:
before subagents chromium

Subagent group, head (Chromium, then WebKit):
after subagents chromium
after subagents webkit

Live side by side (tailnet): https://cmux-lawrences-mac-mini.tail137216.ts.net:18796/compare/tp-fold-turn-motion-c/

Observed / Not run

  • New gallery variants: worked-for-opening and worked-for-closing, each held halfway through its fold.
  • The turn-fold tests sit in transcript-fold-anchor.test.tsx, which shares the jsdom transcript setup. They cover:
    • rows below slide and revealed steps fade on open;
    • ghosts fade out on close;
    • one clock for late-mounted rows;
    • settle-before-measure on a second toggle;
    • no motion with animations off;
    • an ended fold holds no rows;
    • a token streamed while a fold plays runs no pass (a counted read of the mounted rows stays 0 across three tokens; moving a row's placement runs one).
  • transcript-fold-anchor-browser.test.ts adds a mid-transcript Worked for case: the line stays within 1 px on open and close, and no ghost is left behind.
  • Ran on huge-blue at dd2b666:
    • fold-anchor: 10/10
    • fold-anchor-browser: 9/9
    • fold-motion: 15/15
    • worked-fold: 9/9
    • no-blank-space: 1/1
    • long-prompt: 1/1
    • working-line: 12/12
    • ThoughtRow: 30/30
    • The subagent and turn test files pass, except three gallery-frame tests (turn-fork, composer-turn-cycle, inspector-subagents-wave-hold) that time out because the sparse checkout leaves out a Swift source the gallery server reads. CI runs them.
    • Lint and format are clean on the changed files. tsc reports errors only in base files (QuestionCard.gallery.tsx, home-cloud-rows.test.ts).
  • 797 changed lines (under 800): the turn-fold tests reuse the existing transcript test setup instead of copying it.
  • Reviewed by Codex xhigh. The receipt at the head is in a comment.
  • Not run: an app build (webview-only change).

Agent session: cosmic-gray

🤖 Generated with Claude Code

…the rows below sliding and the steps fading

Leo (2026-10-21): "these all need animations for expand and collpase!" Opening Worked for inserts
whole virtual rows, so the in-row fold can't size it. turnFold.ts plays it instead:
- rows below the clicked line slide from where they were drawn (a FLIP on translate);
- the steps it reveals fade in;
- on close, inert copies of the hidden steps fade out under the rows sliding up.
Every row plays on one clock, so rows that mount or unmount mid-fold keep their motion. A second
toggle settles the playing fold first and keeps the clicked line on its pixel. Nothing runs with
animations off or under Reduce Motion.

Reviewed in three Codex xhigh rounds while it was part of #24050.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16030-bf28-7072-b1f6-b83bbd0beacc&host=cosmic-gray
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review dd2b666: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr24514 -f ref=tp/fold-turn-motion-c -f platform=macos -f backend_mode=local

Gallery before/after for dd2b666: 16 states rendered, 0 images differ, 920 more touched states not rendered. Images are in the run's artifact, not on this public PR.

gh run download 39939411971 -R manaflow-ai/cmux -n gallery-advisory-24514-dd2b666b1d3bef05189097eefc0e8135d5cbec26 (then read gallery-review.json)

States
State Theme Status Diff Picked by
agent-pane.chat-find-bar/empty Apple System Colors Light unchanged 0% own conversation.css
agent-pane.chat-find-bar/empty Apple System Colors unchanged 0% own conversation.css
agent-pane.chat-find-bar/matches Apple System Colors Light unchanged 0% own conversation.css
agent-pane.chat-find-bar/matches Apple System Colors unchanged 0% own conversation.css
agent-pane.chat-find-bar/next-match Apple System Colors Light unchanged 0% own conversation.css
agent-pane.chat-find-bar/next-match Apple System Colors unchanged 0% own conversation.css
agent-pane.chat-find-bar/no-results Apple System Colors Light unchanged 0% own conversation.css
agent-pane.chat-find-bar/no-results Apple System Colors unchanged 0% own conversation.css
agent-pane.chat-width/standard Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/standard Apple System Colors unchanged 0% own App.tsx
agent-pane.chat-width/wide Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/wide Apple System Colors unchanged 0% own App.tsx
agent-pane.chat-width/wide-blocks Apple System Colors Light unchanged 0% own App.tsx
agent-pane.chat-width/wide-blocks Apple System Colors unchanged 0% own App.tsx
agent-pane.composer-floats/scrolled-to-end Apple System Colors Light unchanged 0% own App.tsx
agent-pane.composer-floats/scrolled-to-end Apple System Colors unchanged 0% own App.tsx
agent-pane.composer-floats/composer-grown Apple System Colors Light unchanged 0% own App.tsx
agent-pane.composer-floats/composer-grown Apple System Colors unchanged 0% own App.tsx
agent-pane.diff-panel/last-turn Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/last-turn Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/toolbar Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/toolbar Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/tree-selection Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/tree-selection Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/open-file-error Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/open-file-error Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/escape Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/escape Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/review-file Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/review-file Apple System Colors unchanged 0% own conversation.css
agent-pane.diff-panel/stage-hunk Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/stage-hunk Apple System Colors unchanged 0% own conversation.css

Pick other states with a Gallery: entry.id, entry.id/variant line in the PR body.

red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 444f819 to compare; do not merge until green; see lane-rules

Tour of dd2b666b1d (run): no media. The tour did not run: only webview code changed (6 path(s)), which the gallery advisory run checks in Linux Chromium on the pull request (its artifact holds any captures); add the media-tour-mac label, then re-add media-tour, to tour it on a Mac.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by transcript-parity on cosmic-gray · session

@blacksmith-sh

blacksmith-sh Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Found 8 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp6cruaaye/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmpohifofd0/summary-fail.json (live):/summary-fail.json (live): View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
test_base_passes (main.NoticesPairTests.test_base_passes)/
test_base_passes (main.NoticesPairTests.test_base_passes)
View Logs
test_targets_outside_the_app_go_to_the_mac_canary (main.SwiftCanaryFleetCoverage.te
st_targets_outside_the_app_go_to_the_mac_canary)/
test_targets_outside_the_app_go_to_the_mac_canary (main.SwiftCanaryFleetCoverage.te
st_targets_outside_the_app_go_to_the_mac_canary)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-4heqq created for this PR by the cmux coordinator.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

rust workspaces / plan (changed Cargo workspaces) is red on 16 of 16 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

…laying one

When its timer ended a fold, the transcript still pointed at it, with the
rows as placed at the toggle, the tracked row elements and animations, until
the next render. Unmount now settles a playing fold too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16030-bf28-7072-b1f6-b83bbd0beacc&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

rust workspaces / rust workspaces is red on 19 of 19 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

teamleaderleo and others added 2 commits October 23, 2026 03:28
… streamed token

A MutationObserver started at the toggle runs the pass when the thread's
row elements mount or unmount or a row's placement (style, index) changes;
a token streamed into a row changes neither. It disconnects when the fold
ends (its timer, the next toggle, another chat, unmount). A render only
checks in O(1) whether the fold is over. The turn-fold tests move into
transcript-fold-anchor.test.tsx, which has the same transcript setup, and
a new one counts passes while tokens stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16030-bf28-7072-b1f6-b83bbd0beacc&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / nightly-guards / workflow-guard-tests / release-notary is red on 16 of 19 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex xhigh, correctness first): SHIP at dd2b666

  • Codex xhigh (codex exec --sandbox read-only on cmux16, diff-only, no checkout) reviewed the full PR diff and the delta since 9e294ef.
  • That earlier round said HOLD because the fold re-ran its pass over the mounted rows on every render, including each streamed token. That pass now runs only from a MutationObserver started at the toggle. The observer watches row elements mounting or unmounting and each row's style and aria-posinset. It disconnects when the fold ends: its timer, the next toggle, another chat, or unmount. A render only checks, in O(1), whether the fold is over.
  • New test: three streamed tokens during a fold run no pass, and moving a row's placement runs one.
  • The round before that fixed an ended fold keeping its tracked rows.
  • Ran on huge-blue at dd2b666:
    • fold-anchor: 10/10
    • fold-anchor-browser: 9/9
    • fold-motion: 15/15
    • worked-fold: 9/9
    • no-blank-space: 1/1
    • long-prompt: 1/1
    • working-line: 12/12
    • ThoughtRow: 30/30
    • The subagent and turn test files also pass.
    • Lint and format are clean on the changed files. tsc reports errors only in base files.
  • 797 changed lines.
  • None of the reds are this PR's own:
    • fast gates: its only "finding" is vp lint's closing line Finished in … on 2465 files with 162 rules using 4 threads., whose file count changes because this PR adds a file. The gate's noise filter doesn't recognize the with N rules form; Fast gates: vp lint's closing file count is not a finding the change adds #24527 fixes that.
    • cmux-next checks: "No new SwiftUI", "Checks job structure", Script tests and the perf gate are Swift or script checks, and this PR is webview-only.
    • Rust workspaces, guard status and release-notary are also red on base.

@teamleaderleo
teamleaderleo merged commit dddbac2 into feat-cmux-next Oct 23, 2026
137 of 143 checks passed
@teamleaderleo
teamleaderleo deleted the tp/fold-turn-motion-c branch October 23, 2026 11:09
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant