Repository navigation
Agent chat: a mouse wheel's notch glides the transcript instead of jumping - #24327
teamleaderleo wants to merge 5 commits into
Conversation
|
Owned by nx-chat on big-red · session |
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. |
1bbf717 to
a0730f8
Compare
|
Bead |
a0730f8 to
8a7b078
Compare
8a7b078 to
1d1ed86
Compare
|
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)
Gallery before/after for
States
Pick other states with a Probably yours: CI failed on
Failures:
Matched log linesNot re-run automatically: Written by web and size checks: green |
|
Found 45 test failures on Blacksmith runners: Failures
...and 35 more test failures. View all on Blacksmith
|
|
|
|
|
|
|
|
|
|
Review (codex gpt-6.1-sol): BLOCK at b6ae0c2 Blocking findings
Non-blocking findings
Known red checks are unrelated to this diff: Guard status, release-notary, the HoverCard objc-selectors crash-class rule, the cmux-next fleet/gallery/job/No-new-SwiftUI/namespace-type reds, the base react-apps-check webview reds, gallery health Checked the merge-base |
#24327 review: a switch to another chat mid-glide, a notch over a part that scrolls on its own, a modified or sideways wheel, a pane hidden mid-glide, and remove() with a frame pending. A notch over Jump to latest reaches the transcript with its modifiers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5eec-7700-94d7-9a3a1f7d889a&host=Air-Blue
…ther part takes the wheel #24327 review (Codex BLOCK): - VirtualTranscript is kept across chats, so a glide in flight ran its old distance on the new chat. The session switch now stops it. - A notch over a part that scrolls on its own (code, a long tool output) returned without stopping a glide in flight, so the transcript kept moving while the part scrolled. Any wheel the glide does not take now ends it where it is: another part's, a trackpad's, a modified or sideways one, or one under Reduce Motion or the Debug toggle off. - A pane hidden mid-glide ends it. - The copy of a wheel forwarded from Jump to latest, the stack gutters or the minimap card keeps the wheel's shift, alt and meta keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5eec-7700-94d7-9a3a1f7d889a&host=Air-Blue
|
Review (codex gpt-6.1-sol): BLOCK at 683d6d8 blocking findings
non-blocking findings
Known-red audit: Guard status, release-notary, crash-class HoverCard objc-selectors, cmux-next fleet-host/gallery fixtures, job-structure, No-new-SwiftUI, namespace-type (AppsPageNativeOps/ChiefReplyWatchdog), react-apps-check base webview reds, gallery health composer#steer-read-hover, Swift failures in AgentCursorNameTag/BrowserProfileLinkMenu/NowPlayingCardDismiss/NativeTooltipGuard, and tooling-tests (Developer ID profile, .tbd stub, submodule-reset) touch no changed path and cannot be caused by this PR. The only check-script edit is the scoped visibility listener allowlist entry. checked: full diff from merge-base |
|
|
…g reply grew to The cases for #24327 review round 3, ahead of the fix. A notch that sends the view to the bottom while a reply streams, with the reply growing more than the follow deadband mid-glide, lands at the new bottom and the chat follows the reply again (unit and Chromium/WebKit transcript harness). A glide short of the bottom keeps its own distance. A modified notch over Jump to latest scrolls nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5eec-7700-94d7-9a3a1f7d889a&host=Air-Blue
|
|
|
|
|
Review (codex gpt-6.1-sol): BLOCK at 3a632eb Blocking findings:
Non-blocking findings:
Checked the exact head against merge-base |
…shrinking cases The cases for #24327 review round 4, ahead of the fix: - A notch and its reverse before the next frame cancel out (unit and Chromium/WebKit): both are taken and nothing moves. - A reverse notch mid-glide heads back; a reply that shrinks mid-glide ends the glide at the new bottom (the stand-in now keeps scrollTop inside its content, as a browser does). - forwardWheel, as Jump to latest, the stack gutters and the minimap's card call it: a notch glides, a trackpad moves at once and ends a glide, a pinch is left alone, a modified notch is heard and scrolls nothing. Over Jump to latest in the browsers: a trackpad moves the transcript by its pixels once, a pinch not at all, a notch with the glide off once. - The glide check now reads the ease at each painted frame's time from the wheel (two frames' grace) instead of a fixed share per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5eec-7700-94d7-9a3a1f7d889a&host=Air-Blue
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. |
fd69d01 to
66bae5f
Compare
66bae5f to
227a814
Compare
|
|
|
Orphaned: no activity for 14 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged |
…#26114) * test(agent-chat): reproduce jumping mouse wheel notches Agent-Session: https://cmux.com/deeplink/agent?id=aid-8d9d6ada62087fc93733 * fix(agent-chat): ease mouse notches on the shared scroll clock Recuts #24327 and retains its notch classification and default-on Debug setting. Incremental wheel steps share the jump runner without a second scroll anchor or animation loop. Agent-Session: https://cmux.com/deeplink/agent?id=aid-8d9d6ada62087fc93733 * fix(agent-chat): honor wheel speed and explicit view placements Agent-Session: https://cmux.com/deeplink/agent?id=aid-8d9d6ada62087fc93733
…so action.run's deadline test no longer times out behind it (#24456) * test(cmux-next control): the watchdog CPU test spins its own thread, not the main actor ActionRunCreatedTests.aCreatedTabThatNeverShowsStillAnswersRanBeforeTheDeadline timed out at 1999 ms with its stage log stuck at daemon@43 (base run 39882563236, PR #24327 run 39798893065). In both runs MainThreadWatchdogPolicyTests.mainThreadCPUIsReadableFromAnotherThreadAndMovesWithWork started within 4 ms of it and spun the main actor until it had burned 80 ms of CPU, which took 3.5-4.1 s of wall time on the loaded host. The action.run test needs the main actor (the work queue's run-loop block, awaitIdle's MainActor.run) inside its 2 s deadline, so it timed out. BusyWatchdog.watchCurrentThread() watches whichever thread calls it, so the CPU test now watches and spins a thread of its own and reads it from the test's task: the same contract, and the main actor stays free. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue * test: the watchdog CPU test joins its spinning thread before it ends Review (Codex): the watched thread was signalled but never awaited, so it could outlive the test. The thread now finishes the stream after its last wait; the test reads both CPU times, releases the thread, drains the stream to its end, and only then checks the values, so no path returns before the thread exits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c6d-a1d2-7fc2-9458-92c6bdcb7b4d&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo-ask: 102e9a090f
Leo-ask: 6e88488b1c (build half)
Leo: "smoother scrolling in webkit: i mean with a mouse instead of just trackpad. i have mos and lawrence doesn't, tho we'd presumably be doing a bit of our handrolled mos-ish thing"
Context, from his message just before it: "...the former [chromium] has smoother scroll btu the latter [webkit] has glass... but we should be able to do smoother scroll on webview too right?"
What changed
In the agent chat transcript, a notched mouse wheel now glides instead of jumping. Each notch eases the view toward an accumulated target over the motion scale's standard step, 180 ms (
MOTION_MS.standard, ease-out cubic). A notch that comes in mid-glide adds its distance to the target. Trackpad and momentum scrolling are untouched, so the engine still handles them.webviews/src/agent-session/acpmux/wheelGlide.ts:isWheelNotchandinstallWheelGlide. No engine says which device sent a wheel event, so the notch test reads what each engine leaves behind (WebKit'sWebEventFactory.mm/WheelEvent.cpp):deltaModelines or pages mean a wheel.wheelDeltaY, whatever its pixels. A precise scroll'swheelDeltaYis 3 times its pixels.wheelDeltaY= 3 times its pixels, so the pixels decide. A notch scrolls its accelerated lines times 40, and those lines are 16.16 fixed point (a slow notch is 4.000244140625 px). A trackpad (hasPreciseScrollingDeltas) scrolls whole points.App.tsx: the transcript's scroller installs the glide. Jump to latest stops a glide in flight.wheelPassThrough.ts(forwardWheel, after Agent chat: the minimap's hover card blurs the chat behind it, not just tints it #24024): the copy sent from over Jump to latest, the stack gutters or the minimap's hover card is now cancelable and carrieswheelDeltaY. A notch there glides as well. A trackpad scroll there still scrolls at once, as before.agentPane.debug.wheelGlide, on by default) inAgentPaneTunables.swift, following thestreamWordspattern. Off setsdata-wheel-glide="off"on the page's<html>, and the wheel scrolls as WebKit does. New agent tabs read it. Reduce Motion and the app's motion off (data-agent-motion="off") also leave the notch to the engine.readerAt) still runs for every notch, and the glide moves the view through ordinary scroll events. So a wheel up leaves follow mode as it does today, and stick-to-bottom never fights it. A row that scrolls on its own (wheelTarget) still takes the wheel first. At the top, the wheel still asks for older history.Timers and cleanup
One
requestAnimationFrameloop per transcript, running only while a glide is in flight.remove(), which cancels the frame, removes the window listeners and removes the scroller's wheel and scroll listeners.Tests
webviews/test/agent-pane-wheel-glide.test.ts, run in Chromium and Playwright WebKit. Playwright's WebKit wheel is always a precise (trackpad) scroll, so the WebKit notch is the event macOS WebKit dispatches for a mouse wheel, built in the page: 100.006103515625 px,wheelDeltaY300. It checks:defaultPreventedfalse) and Chromium applies it in one frame;data-wheel-glide="off"leave the notch to the engine;transcript-wheel-glide.test.ts(Chromium and WebKit, the transcript harness), for the Agent chat: a mouse wheel's notch glides the transcript instead of jumping #24327 review:scrollTop, since rows above that measure move the offset without moving anything on screen.wheelGlide.test.tsalso drivesinstallWheelGlideon a stand-in scroller and frame clock. It checks each stop path frame by frame (a nested part, a trackpad, modified and sideways wheels,stop(), a hidden pane), and thatremove()with a frame pending cancels it and takes every listener off.wheelGlide.test.ts: unit cases forisWheelNotch, using the values each engine dispatches.agent-pane-wheel-pass-through.test.ts(existing) runs alongside it, unchanged.Before and after (same harness, huge-blue, Linux headless; one notch of 100 px)
page.mouse.wheel(0, -100))Linux WPE animates a wheel step over 2-3 frames by itself (see the #23062 note). macOS WebKit applies it in one frame, which is the jump Leo sees. On WPE the base red is therefore that the page doesn't take the notch (
defaultPreventedfalse); the max jump of 95 of 100 px also shows it. WebKit's head frame counts vary because the loaded host skips frames; the glide is time-based, so it still takes its 180 ms (200 ms when these numbers were taken, before 4c55c45).Same file on huge-blue: base (test-only commit) fails the 3 Chromium glide cases and the 2 WebKit glide cases; trackpad and toggle cases pass. Head passes all 9.
Review fixes (head 75ab7f8; test-only commit 9c1cdd7):
wheelGlide.test.ts12/12,transcript-wheel-glide4/4 (3 runs in a row),agent-pane-wheel-glide11/11,agent-pane-wheel-pass-through6/6,thread-minimap-feel4/4.Current head dc000a7 (75ab7f8 plus a merge of feat-cmux-next, which brings in #24377's fix for the PaneMotionBench compile red on base):
wheelGlide.test.ts,agent-pane-wheel-glide,transcript-wheel-glideandthread-minimap-feel: 4 files, 0 failed, exit 0.cmux-ci compile-check --ref dc000a7549b10202dc44442f1ab0cbe8b4fb065e --apppassed (job e0b527f05b68a4d4f87ed8df).AgentPaneThoughtTunablesTestsrun alone on the fleet, step 17934d5ed345480051f901f1 (aws-m4pro-2): 9/9 passed, includingwheelGlideIsOnByDefaultAndOffSetsItsAttribute().Review round 2 (head 683d6d8; test-only commit faca193):
motion-scale.testrefused the 200.visibilitychangelistener now stops the glide whendocument.hidden. It is added only while a glide runs and removed instop(), soremove()takes it off too. The frame step also checksdocument.hidden. It is allowlisted incheck-switch-fetches.pybecause it reads no data.deltaY <= deltaXrule wheelPassThrough uses.wheelGlide.test,motion-scale,agent-pane-wheel-glide,thread-minimap-feelandtranscript-wheel-glide: 5 files, 0 failed.transcript-wheel-glide("skipping webkit"), so there Chromium and the unit test cover the hidden-page case.AgentPaneThoughtTunablesTestspassed 9/9 (step 9163689c79abb133800284c7). Round 2 touches no Swift.agent-pane-older-rows-fadefails the same way at the base merge a687a49 (step 15ced7124fd88e55d6dc53dd).agent-pane-wheel-pass-throughtimes out with no output at both base a687a49 (step 1e1b85dd1c7bc4da2824ac59) and head 4c55c45 (step 5eface195dd067e8a0e74f76).Review round 3 (head 3a632eb; test-only commit d8c9bdc; base merged at 53173b2):
scrollHeightread the step already made, so no O(rows) work per frame. A glide short of the bottom keeps its own distance.agent-pane-wheel-glidenow takes 2 or more frames with no frame covering more than 80% of the distance. A loaded mini painted only 2 frames of the 180 ms glide; the engine's own step is still one frame for all of it.vp lintwarning in the unit stand-in (no-useless-spread) fixed. That warning reddened fast gates at 683d6d8.The Chromium streaming case passes on the old code too, so in Chromium only the unit case shows this bug.
motion-scale,wheelGlide.test,transcript-wheel-glide,agent-pane-wheel-glide,thread-minimap-feel,transcript-follow: 6 files, 0 failed.Earlier head b6ae0c2:
agent-pane-wheel-glide.test.ts9/9,agent-pane-wheel-pass-through.test.ts6/6,thread-minimap-feel.test.ts4/4,wheelGlide.test.ts6/6. These ran with each file's browsers launched once; huge-blue hangs on the files' launch-then-relaunch probe even on base.agent-pane-wheel-glide,wheelGlide.test.tsandthread-minimap-feel. 3 files, 0 failed, exit 0.preventedfalse, one 100 px jump); the trackpad and toggle cases pass.cmux-ci compile-check --ref b6ae0c2b12d5cf847a4c580b3c5535930d995950 --apppassed (fleet job 700e8b30aef37319af11a2db).The last commit fixes a red this PR caused in
thread-minimap-feel("the band of lit rows follows the scroll on the scroll's own frame": 29-34 wrong frames). AscrollTopwrite in a frame callback fires its scroll event only in the next frame, so the minimap's lit band and the mounted rows painted one frame behind each glide step. Each step now dispatches its scroll event at once, and the browser's later event for the same offset is dropped. Checked on huge-blue: without the fix 34 wrong frames, with it 4/4 pass.No capture: the only native change is one Debug Settings toggle row generated from its
Tunabledescriptor. What changes for the user is wheel motion in the webview, which the Chromium and WebKit frame test above measures.Changelog
Agent session: Air-Blue
🤖 Generated with Claude Code