Skip to content

Agent chat: long lines in diff and command cards show a thin scrollbar at rest - #23967

Merged
teamleaderleo merged 16 commits into
feat-cmux-nextfrom
cmux/code-block-hscroll
Oct 23, 2026
Merged

teamleaderleo merged 16 commits into
feat-cmux-nextfrom
cmux/code-block-hscroll

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Leo-ask: ask-d900f5acf9

In agent chat, an unwrapped diff, Shell block or call output whose lines run past the card's right edge now keeps a thin scrollbar thumb under its lines at rest, as long as there is more to scroll. Before this, the only sign at rest was the right-edge fade from #23413: the diff's thumb stayed transparent until you scrolled it (and was a 1.5 to 7.5% tint while you did), and Shell and output bodies used WebKit's overlay scroller, which only shows while scrolling. A card cut to its eight-line preview now ends with that scrollbar in view. Before, a cut diff's scrollbar sat under its last line, below the cut.

I picked "scroll with a visible thumb" over always wrapping. Prose files (.md, .txt) and command output already wrap by default since #23413, so Leo's codex-excursions.md card now wraps. Code stays unwrapped because wrapping breaks its shape, and the Wrap lines toggle covers anyone who wants it. The visible thumb follows Leo's newer call on the composer (#23854): "IF THERE ARE THINGS TO SCROLL, JUST KEEP THE SCROLLBAR". It is a thumb with no track, so SCROLLBARS-THIN's "never a permanent gray track" still holds.

What changes

  • webviews/src/scrollers.ts
    • watchSideways sets SIDEWAYS_ATTRIBUTE on a block's scroller while its lines run past it.
    • On Pierre's code rows, that attribute gives the thumb its at-rest color, --cmux-scroller-thumb. It keeps the same color while scrolling.
    • Shell and output bodies carry SIDEWAYS_SCROLLER_ATTRIBUTE from their JSX. SIDEWAYS_SCROLLER_CSS, installed with the page's other scroller rules, makes their scrollbar the thin 8px one from the start. The attribute has to be there from the start because WebKit keeps a scroller's kind once it has made it.
  • conversation.css and sideways.ts
    • A cut preview grows by the scrollbar lane (--cv-lane) wherever it keeps one (see the review fixes below), and its bottom fade stops above the lane.
    • A cut diff also cuts Pierre's code box (--cv-block-cut), so the box's scrollbar sits at the cut instead of under the hidden lines.
    • Pierre's lane is pinned at its current 6px through --diffs-scrollbar-gutter-override inside diff cards; the new browser test reads it as 6px there.
  • Gallery: new states edit-long-line-code-cut and shell-unwrapped-long-cut (and the review states below).

The resize cursor

I found no resize cursor or resize: property in any transcript card. The only col-resize in the agent pane's webview is the inspector's own edge (inspector/inspector.css:53), and it isn't over the chat column.

The cause #23413 found is native. DividerHandleView.resetCursorRects added its whole frame as a cursor rect, so a strip divider running under a docked chat kept its resize cursor over the chat. AppKit cursor rects ignore views stacked above them. #23413 fixed it with cursorClip (merged 2026-10-21 15:59 UTC, after this screenshot was taken on 10-20 21:57), and that fix is unchanged here. The new browser test adds a guard: across a grid over each card and its tool row, nothing shows a *-resize cursor or has a resize value.

Tests

  • webviews/test/transcript-block-sideways-scrollbar.test.ts, in real Chromium (scrollbars on) and WebKit, on the long-line gallery states, cut and whole. For each block it checks:
    • the lines overflow by more than 100px;
    • the scroller's lane is 1 to 10px tall and sits inside the card's shown area;
    • at rest (pointer away, 1.2s after the last scroll) the lane draws a thumb and no full track. It compares the lane's pixels with the same lane once every thumb is made transparent;
    • no resize cursor shows anywhere over the card or its tool row.
  • transcript-block-overflow.test.tsx (JSDOM) checks that the new attribute is on both kinds of scroller, stays after scrolling to the end, and is off when the lines fit. It also checks that the bottom fade ends above the lane.
  • Why they fail on base: the diff's thumb is transparent at rest; the Shell body's lane is 15px (Chromium's classic bar) or absent (an overlay scroller); a cut diff's lane lies below the cut; and the JSDOM test's attribute doesn't exist yet.

Focused command: cmux-ci run --class light --script scripts/ci/run-webviews-tests.sh --env CMUX_BROWSER_TESTS=1 --env CMUX_WEB_TEST_FILES=<the two files, newline-separated>

  • Green at head b0d426c: CI run 39738984052 (react-apps-check) passed both files in Chromium and WebKit.
  • Red without the fix: a Chromium-only fleet run of the tests on base showed the Shell body with no lane (lane: 0) and the cut diff's lane below the cut (535.75 vs 427.75). The JSDOM test fails on base because the attribute doesn't exist. There is no red run of the final pixel (thumb-at-rest) check: the fleet's WebKit didn't launch (180 s timeout), and the last red/green pair sat queued for about 4 hours before it was cancelled. An earlier red for the diff thumb used a detection method that has since been fixed, so it isn't counted as evidence.

Codex review fixes (rounds 1 and 2)

  • Work per streamed token (sideways.ts):
    • Only mount walks a block.
    • A change marks the code box it happened in, takes in only the nodes it added, and lets go of the ones it removed.
    • Marked boxes are measured at most once per animation frame. Scroll and resize still update straight away.
  • Leak (sideways.ts): a removed Pierre host now takes everything under it with it: its shadow root's change observer, its code boxes, their size observations and their scroll listeners. A host removed before its shadow root arrived is dropped too.
  • Stop conditions: the returned stop function cancels the pending frame, disconnects every change observer and the size observer, and removes every scroll listener.
  • Lane height (conversation.css):
    • A wrapped diff has no lane, so a cut preview is exactly eight rows.
    • A call output's lane is its own 8px bottom padding, which the scrollbar takes when it appears, so the output is the same height with or without it.
    • An unwrapped Shell body keeps the 8px lane while its lines run past. While its command is still running (data-live, from the row's running state), it keeps the lane whether or not they do, so a streamed line that starts or stops running past doesn't move the transcript.
    • End of a stream for a block that fit: when the command ends, the body drops its 8px lane once, at the same moment the row changes from running to done. A settled block whose lines fit has no empty tail, and its height doesn't change after that.
  • New tests:
    • JSDOM: a streaming test (200 blocks, 50 tokens into one; only that block is measured, once per frame, with fewer than 20 elements walked). A leak test (six host removals, half with a late shadow root, then a host removed before its root; afterwards the scroll listeners, size-observed elements and observed roots are back to their mount counts, nothing detached is measured, and stop releases everything).
    • Browser: a diff (whole and cut), a running command (whole and cut) and a call output keep the same body and card height while their lines stop and then start running past again. Settled Shell bodies (whole and cut) and the call output, once their lines fit, are as tall unwrapped as wrapped, with no empty tail. Wrapped diffs (cut and whole) have no lane, and a cut one is eight rows.
    • New gallery states: shell-live-unwrapped-long-line, shell-live-unwrapped-long-cut, output-unwrapped-long-line.
  • Red/green for the review fixes, both test files, Chromium only (the fleet's WebKit doesn't launch). Red is the tests without the fix.
    • Head 0056f5e (the round-2 fix): red on fleet step cc54a7b19da8c140501681fa and on huge-blue (the leak test, the wrapped-diff tests and the settled-Shell tests fail); green on fleet step b0fb5d3a6e488d7e4cb1e202 and on huge-blue (both files pass).
    • Round 1: red on fleet step 626a6ed87c59d17c9aa16244 (old code measured during the stream; the Shell body shrank by 8px when its scrollbar went), green on 6f03d1c010974893bc1e4cef.
    • The live-Shell height tests also pass on the round-2 red, because round 1 already kept the lane on every unwrapped Shell. Their round-1 red is the 8px shrink above.

Codex review fix (round 4)

  • A non-shell call output past eight lines is cut (.cv-tool-output[data-preview="cut"]). Its cap was calc(8 * 18px) under box-sizing: border-box, so its 8px top padding and its 8px scrollbar lane came out of the eight lines: the box showed 128px of text, about seven lines, and the lane did not make the preview taller.
  • conversation.css: the cap is now calc(8 * 18px + 8px + var(--cv-lane)), eight lines plus the top padding plus the lane, the same shape as the Shell body's cut (calc(8 * 18px + 8px + var(--cv-lane, 0px))).
  • New gallery state output-unwrapped-long-cut (a 14-line call output, unwrapped). It joins the browser matrix (thumb at rest, steady height, settled), and a new check reads the cut box's clientHeight less its padding: it must be eight line heights (144px), both while its lines run past the edge and after they fit.
  • Red at 3a3205b (the test only), fleet step 51ffedc3fa3763c2100a4750 on cmux12s: only the new check fails, in Chromium and WebKit, shown: 128 against eight: 144 in both states; the other 36 tests pass.
  • Green at 5dc8099 (fix plus base merge): fleet step 543b4b5589718161282a6142 on cmux13s (both test files) and 18280c8b7fbdc3ea3f5fbb24 on cmux12s (the browser file), Chromium and WebKit.
  • Base merge of feat-cmux-next (ea5f81f): one conflict, in ToolRow.tsx. Base added data-lang to the call output's <pre> and this branch added the SIDEWAYS_SCROLLER spread on the same line; both are kept. Base's new Markdown result box (.cv-tool-output__markdown) is not a .cv-tool-output, so the lane and cut rules don't reach it.

Observed / Not run

  • Observed: scripts/web-lint.sh passed; scripts/cmux-next/prepush-gate.py found no new violations; check-scrollbars.sh reported nothing in webviews; the branch has feat-cmux-next merged in (it picks up ci: the agent pane page ships the Inspector's ContextSection.css (base red since #23789) #23935's ContextSection.css and the wave.css fixes).
  • Reds at 5dc8099 that are not this PR's: rust workspaces (Native/OptChat/chief-turn has no entry in .github/rust-workspaces.toml on base; this PR touches no Cargo workspace), and fast gates' web lint, whose only "new finding" is vp lint's closing line Finished in 7.6s on 2465 files with 162 rules using 4 threads.: the compare's noise rule (scripts/ci/fast_gate_webviews_checks.py NOISE, Web checks: vp fmt's closing file count is not a finding the change adds #24436) matches on N files using N threads but not the with N rules form, and the merge's file count differs from the base's. The base's own 4 lint findings are reported as base.
  • Earlier reds also red on base: gallery health (2) (red on feat-cmux-next 0ce6802 and on other open PRs), release-notary and Guard status (base), and cmux-next checks (package-conventions and job-structure steps; this PR touches no file they check).
  • Not run: no tests ran on this Mac (air-blue). The app wasn't built or tried, so I haven't confirmed in WebKit-in-app that the thumb shows at rest under the "When scrolling" and "Always" settings. The native divider fix from Agent chat: tool blocks wrap or fade at the right edge, preview fades, no resize cursor over a docked chat #23413 wasn't re-checked in the app. Fenced code cards in replies (HighlightedCode) still show their scrollbar only while scrolling; they're outside this change.

No capture: webview-only change; the gallery advisory run captures the new and updated agent-pane.transcript states (edit-long-line-code, edit-long-line-code-cut, shell-unwrapped-long-line, shell-unwrapped-long-cut, shell-live-unwrapped-long-line, shell-live-unwrapped-long-cut, output-unwrapped-long-line, output-unwrapped-long-cut).

Changelog

  • Fixed: In agent chat, diff, command and output cards whose lines run past the right edge now show a thin horizontal scrollbar at rest, including when the card is cut to its preview.

Agent session: Air-Blue

🤖 Generated with Claude Code


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


Summary by cubic

Agent chat diff, Shell and call-output cards whose lines run past the right edge now show a thin horizontal scrollbar at rest, not only while scrolling. Before, the only hint was the right-edge fade: the diff thumb was transparent until scrolled, and Shell and output bodies used WebKit's overlay scroller, which shows only while scrolling. A card cut to its eight-line preview now ends with the scrollbar in view; a cut diff no longer leaves it under the hidden last line.

Bug Fixes

  • SIDEWAYS_ATTRIBUTE (set by watchSideways) shows the at-rest thumb on Pierre's diff rows; Shell and output bodies carry SIDEWAYS_SCROLLER_ATTRIBUTE from their JSX so WebKit makes their scrollbar the thin one from the start.
  • Streaming measures only the changed block, once per frame, instead of re-walking the whole card per token; a removed block releases its shadow root's observers and listeners.
  • The scrollbar lane never shifts the card or transcript: call outputs carry it in padding, Shell bodies keep it while lines overflow or the command is running, and wrapped or settled fitting bodies have none.
  • A cut call output preview now shows eight whole lines plus the lane under them, instead of losing the lane out of the cut.
  • Adds browser tests in real Chromium and WebKit over the long-line gallery states, including running commands and cut previews.

Written for commit 5dc8099. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 2 commits October 21, 2026 17:51
…t rest (red)

Real Chromium and WebKit on the transcript gallery's long-line states (two new
ones cut to the eight-line preview): the diff's and the Shell block's lines run
past the edge, so each must draw a thin thumb on no track at rest, on a lane of
10px or less that the preview does not cut off, with no resize cursor over the
block. The JSDOM sideways test checks the new mark on both kinds of scroller.

Leo-ask: ask-d900f5acf9

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
…r at rest

An unwrapped tool block whose lines run past its right edge (an edit's diff,
a Shell block, a call's output) now keeps a thin thumb on no track under its
lines at rest, in the shared thumb color, as long as its lines run past it.
Before, the only sign at rest was the right-edge fade: the diff's thumb was
transparent until it scrolled (and nearly invisible while it did), and the
output had WebKit's overlay scroller, which shows only while scrolling.

- scrollers.ts: SIDEWAYS_ATTRIBUTE (set by watchSideways while a box's lines
  run past it) gives Pierre's code rows their thumb at rest; the page side
  (SIDEWAYS_SCROLLER_ATTRIBUTE, on Shell and output bodies from their JSX, and
  SIDEWAYS_SCROLLER_CSS, installed with the page's other scroller rules) makes
  their scrollbar the thin one from the start, since WebKit keeps a
  scroller's kind once made.
- A block cut to its eight-line preview ends with that lane: a cut diff cuts
  Pierre's code box with it (--cv-block-cut), so the scrollbar is no longer
  under the last line past the cut, and the bottom fade stops above the lane.

Leo-ask: ask-d900f5acf9

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
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 22, 2026
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 5dc8099: 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=pr23967 -f ref=cmux/code-block-hscroll -f platform=macos -f backend_mode=local

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

gh run download 39942505877 -R manaflow-ai/cmux -n gallery-advisory-23967-5dc8099164be1d83c3b92cf409f478c62db0d27c (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.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
agent-pane.diff-panel/more-lines Apple System Colors Light unchanged 0% own conversation.css
agent-pane.diff-panel/more-lines Apple System Colors unchanged 0% own conversation.css
agent-pane.path-chip/file Apple System Colors Light unchanged 0% own conversation.css
agent-pane.path-chip/file Apple System Colors unchanged 0% own conversation.css
agent-pane.path-chip/folder Apple System Colors Light unchanged 0% own conversation.css
agent-pane.path-chip/folder Apple System Colors unchanged 0% own conversation.css
agent-pane.path-chip/outside-lock Apple System Colors Light unchanged 0% own conversation.css
agent-pane.path-chip/outside-lock Apple System Colors unchanged 0% own conversation.css
agent-pane.path-chip/outside-text Apple System Colors Light unchanged 0% own conversation.css
agent-pane.path-chip/outside-text Apple System Colors unchanged 0% own conversation.css

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

Passes: CI passes on 5dc8099164.

CI passes on 5dc8099164 (run 39939040502 attempt 1).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

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

Tour of 5dc8099164 (run): no media. The tour did not run: only webview code changed (10 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.

@blacksmith-sh

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 7 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmpoqmaw_im/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmptklzzspc/summary-missing.json (live):/summary-missing.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_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 and others added 2 commits October 21, 2026 18:54
Hiding every thumb with a late style sheet and diffing two screenshots read
no thumb in Chromium even where one was drawn (a fleet run on the fix: the
lane was there and thin, the diff empty). Read the lane itself: with the box
scrolled to its start, a column that differs from the lane's right end, which
shows the card under a colorless track, is drawn on. Log the measurements.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / nightly-guards / workflow-guard-tests / release-notary is red on 16 of 20 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.

…orners

On a diff card that fits its preview, the lane is the card's bottom edge, and
its 12px rounded corners clip both ends of the lane to the page's color. The
reading compared every column with the lane's right end, inside that corner,
so it saw the whole lane as drawn (react-apps-check on fb97162: 702 of 702
columns, Chromium and WebKit). Read between the corners.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol): BLOCK at b0d426c

Blocking findings:

  • webviews/src/agent-session/acpmux/conversation/sideways.ts:42,55-63 — While a Shell or output block streams, React mutates text or children under the watched body for each token. The MutationObserver calls scan() for each mutation; scan() runs pierreScrollers(container, []) and container.querySelectorAll("*") across every rendered row before updating the boxes. This is O(rows) work per streamed token and can make long streamed outputs progressively laggy. The requested observer behavior is no per-token O(rows) work.

  • webviews/src/agent-session/acpmux/conversation/conversation.css:1826-1827,2234-2235,2260-2262 — For a cut Shell or call-output block, --cv-lane defaults to 0px and becomes 8px only when data-cmux-sideways toggles on. The cut body's max-height includes that variable, so an unwrapped stream that starts with fitting lines and then appends one overlong line changes the body's height by 8px, moving the card and following transcript rows; removing overflow shrinks it again. This violates the required no-jump behavior when the attribute toggles while streaming.

Non-blocking findings: none.

Checked the nine-file diff, surrounding scroller and Pierre integrations, the new JSDOM and Chromium/WebKit tests, the gate-owned files, and the final GraphQL head SHA; no app build or test suite was run.

teamleaderleo and others added 3 commits October 22, 2026 01:22
… does not move the transcript

Codex review of #23967: every streamed token re-walked the whole block (O(rows) per token), and a
cut or open Shell/output body grew by the sideways scrollbar's lane when its lines started running
past, so the transcript jumped. These fail on the current code.

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
…eep the sideways lane

watchSideways walked the whole block (every element, and Pierre's shadow roots) on every change,
so each streamed token cost O(rows). Now only mount walks the block: a change marks the code box
it is in, takes in only the nodes it added, and the marked boxes are measured once a frame. Scroll
and size changes still update at once.

An unwrapped Shell or call-output body keeps its scrollbar's lane at its bottom whether or not its
lines run past, as padding the bar takes when it comes, so the body (cut or open) is the same
height with the bar and without it, and the transcript does not jump.

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
Pierre, loaded by the earlier tests in the file, defines `diffs-container` and gives it a shadow
root, so attaching another one threw before the test measured anything.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / tooling-tests is red on 12 of 12 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

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Checks job structure is red on 7 of 11 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

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No fleet host names in gallery fixtures is red on 7 of 11 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 gpt-6.1-sol): BLOCK at 920fe51

Blocking findings:

  • webviews/src/agent-session/acpmux/conversation/sideways.ts:94-99 — When a Pierre custom-element host with an attached shadow root is removed while its block remains mounted, drop removes only the host from hosts and searches its light DOM. It does not unobserve the host, unwatch the shadow root's [data-code] boxes, or remove that shadow from shadows. The detached code box stays in watched and the ResizeObserver, and its shadow stays observed by the MutationObserver, so later redraws still measure detached DOM and repeated removals leak.

  • webviews/src/agent-session/acpmux/conversation/conversation.css:1083-1090,2267-2271 — The lane is unconditional in cases that do not need it. A wrapped, cut diff still gets --cv-lane: 6px) and adds it to its max-height; an unwrapped Shell whose lines fit still gets --cv-lane: 8pxandpadding-bottom: 8pxbecause the rule keys only ondata-wrap`, not overflow. Those cards show an empty 6/8px tail and become taller even though no sideways scrollbar exists. The browser height test covers only unwrapped overflowing Shell states, so it misses the wrapped and fitting cases.

Non-blocking findings:

  • The new browser test does not cover call-output bodies, wrapped bodies, fitting unwrapped bodies, or watcher cleanup, so those regressions can pass the added suite.

Checked the full nine-file diff against base, the round-1 O(rows) and streaming-height fixes, the scroller scope, the mutation/rAF/Pierre shadow paths, and the new test assertions; the live head was re-read with GraphQL, and no app build or test suite was run.

teamleaderleo and others added 2 commits October 22, 2026 20:17
… no lane where none is needed

Codex review round 2 of #23967. A Pierre host removed from a still-mounted block left its shadow
root's code boxes watched and its root observed. A wrapped diff and a settled unwrapped Shell
body whose lines fit still kept the sideways scrollbar's lane, an empty tail. These tests fail on
the current code: the leak test counts what the watcher holds after six removals and after stop,
and the browser tests check that a diff, a running command and a call output hold their height
while lines start and stop running past, and that settled fitting and wrapped bodies keep no
lane. New gallery states: a running unwrapped command (whole and cut) and an unwrapped call output.

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
…e only where a bar can come

watchSideways keeps one change observer per root (the block and each shadow root in it), so a
removed host's root, its code boxes, their size observations and their scroll listeners all go
with it, and a host removed before its root came leaves the pending set too.

The sideways scrollbar's lane: a wrapped diff keeps none. A call output's lane is its own bottom
padding, so it is the same height with or without the bar. An unwrapped Shell body keeps its lane
while its lines run past and, all the while its command runs, whether or not they do (`data-live`
from the row's running state), so a streamed line that starts or stops running past does not move
the transcript. When a command that fit ends, its body drops the 8px lane once, with the row's
other end-of-run changes; a settled body whose lines fit has no tail.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No new SwiftUI (macOS UI is AppKit) is red on 9 of 13 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.

…-up load

A lone running call reads only on the turn's live line, so the running-command states put a later
call after it and open its row directly. The gallery entry is warmed once, not per state.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol): SHIP at 0056f5e

Blocking findings:

  • None.

Non-blocking findings:

  • webviews/test/transcript-block-sideways-scrollbar.test.ts:71-92,320-376 does not directly assert a settled unwrapped diff whose lines fit, a cut call-output body, or the single live-Shell end-of-run drop when a fitting command settles. The implementation paths were checked, but those cases would make the layout contract easier to prevent from regressing.

Checked the full diff against base, the per-root observer and unmount cleanup paths, streamed-token work, Chromium/WebKit scrollbar CSS and layout states, the round-1/2 regressions, and all gate-rules-owned files; no app build or test suite was run.

…e-block-hscroll

# Conflicts:
#	webviews/src/agent-session/acpmux/conversation/ToolRow.tsx
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol): BLOCK at 5131157

Blocking findings

  • webviews/src/agent-session/acpmux/conversation/conversation.css:2416-2418: A non-shell call output with more than eight lines is cut (.cv-tool-output[data-preview="cut"]). The PR adds the 8px lane as bottom padding at lines 2456-2459, but this rule still caps the box at calc(8 * 18px) under the shared box-sizing: border-box; the lane is taken from the 144px cap instead of added. The cut preview therefore does not grow by the lane and clips more output under the bottom fade, contrary to the PR body and claimed coverage. Include var(--cv-lane) in this cut max-height (or make the equivalent sizing change) and cover a cut call output.

Non-blocking findings

  • webviews/test/transcript-block-sideways-scrollbar.test.ts:63-70: output-unwrapped-long-line is cut: false, so the browser matrix never exercises a cut call output. The JSDOM test only checks CSS text for the lane and fade, so it would stay green with the blocking regression.

Checked: full diff against merge-base 740e58fa28dd5dba9975dda77b57c72f0399b707, the merge resolution against both parents, ToolRow base and PR behavior, auto-merged scroller/CSS composition, observer cleanup and per-token work, scroller scope, and the gate-owned files; the final GraphQL head is 5131157f6cf2cc977ae37af3b2d5b3aeed6ba3cd. No app build or test suite was run under the read-only review constraints.

teamleaderleo and others added 2 commits October 23, 2026 03:22
…der them (red)

Codex review round 4 of #23967: a non-shell call output past eight lines is cut
at calc(8 * 18px) under border-box, so its 8px lane and top padding come out of
the cut instead of being added. Adds the gallery state output-unwrapped-long-cut
and puts it in the browser matrix (thumb at rest, steady height, settled), with a
new check that the cut box's clientHeight less its padding is 8 line heights,
with lines running past and with them fitting.

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
…g a line to it

The cut preview of a non-shell call output was capped at calc(8 * 18px) under
border-box, so its 8px top padding and its 8px scrollbar lane came out of the
eight lines (about seven showed). The cap is now eight lines plus the top
padding plus --cv-lane, as the Shell body's cut already is (Codex review round
4 of #23967).

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
Conflict in webviews/src/agent-session/acpmux/conversation/ToolRow.tsx: the call
output's <pre> took base's data-lang (JSON results) and this branch's
SIDEWAYS_SCROLLER spread; both kept.

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
@cursor

cursor Bot commented Oct 23, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot 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.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

rust workspaces / plan (changed Cargo 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

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol): SHIP at 5dc8099

Blocking findings: None.

Non-blocking findings:

  • webviews/test/transcript-block-sideways-scrollbar.test.ts:102-103,375-408: the browser cut-preview check exercises only the unwrapped call output. A wrapped cut call output and a settled unwrapped diff whose lines fit are still covered only indirectly, so a later regression in those paths could pass.

Checked: the full diff from the merge base, ToolRow against both parents, Markdown and JSON result handling, scroller registration, cut sizing against the round-4 head, per-token observer work and cleanup, and the exact GraphQL head; no app build or test suite was run.

@teamleaderleo
teamleaderleo merged commit e7f31b7 into feat-cmux-next Oct 23, 2026
145 of 154 checks passed
@teamleaderleo
teamleaderleo deleted the cmux/code-block-hscroll branch October 23, 2026 11:25
@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/39947108873 at 184fa32 failed: cmux-next generated files.
Those jobs last passed at 5648ca0. This pull request is one of 2 merged since: #24148, #23967.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

base-red-watch: cmux-next / cmux-next generated files went red on feat-cmux-next in https://github.com/manaflow-ai/cmux/actions/runs/39947108873 (first red at 184fa32, last green at 5648ca0). Suspect: #23967 (commit e7f31b7). If it is yours, fix forward on feat-cmux-next.

@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

lawrencecchen added a commit that referenced this pull request Oct 23, 2026
…w under the scrollbar fade (red)

Lawrence 2026-10-23 (msg 11494): scrollbars auto-fade and grow near the
pointer. Under the scrollbar fade (data-cmux-scroller-fade, the agent pane's
default), a diff's or command output's sideways thumb from #23967 must show
none at rest, one while the lines scroll sideways, and a thicker one while the
pointer is near the block's bottom edge. Fails before the fix: the thumb stays
at rest, and nearBottomEdge does not exist.
lawrencecchen added a commit that referenced this pull request Oct 23, 2026
… like the others

#23967 (Leo) keeps a thin sideways thumb at rest under the lines of a diff,
Shell block or call output that run past the edge. Under the scrollbar fade
(data-cmux-scroller-fade, on by default in the agent pane since Lawrence
2026-10-23, msg 11494) that thumb now fades like every other scroller: none at
rest (the right-edge fade still says the lines go on), shown while the box
scrolls, and wider and darker while the pointer is near the block's bottom
edge. nearScroller now also marks a sideways scroller whose bar is near the
pointer (inside Pierre's shadow roots too). With the fade off (Debug Settings
agentPane.debug.scrollerFade) the #23967 look stays: shown at rest.
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6.1-sol): BLOCK at 5dc8099

Blocking findings:

  • webviews/src/agent-session/acpmux/conversation/conversation.css:1097-1099 and webviews/src/agent-session/acpmux/conversation/sideways.ts:152-155 — A cut, unwrapped diff still caps Pierre's [data-code] at 8 * line-height + --cv-lane. Pierre's code box is box-sizing: border-box and has an 8px top gap, a 2px bottom gap after the 6px gutter override, and a 6px horizontal scrollbar gutter. With the 166px cap at the 20px diff line height, only 150px remains for rows, so the eighth row is clipped; the fade then hides part of it. Trigger: open an unwrapped diff with more than eight lines, such as edit-long-line-code-cut. Result: the cut preview shows about 7.5 rows rather than eight full rows plus the lane, contrary to the PR's stated behavior. The call-output cap was corrected to add its top padding and lane, but the analogous Pierre cap still omits the 8px top gap.

Non-blocking findings:

  • webviews/test/transcript-block-sideways-scrollbar.test.ts:332-423 covers lane height and toggling for diffs, Shell, and call output, but it never asserts the number of visible rows for the cut diff. The JSDOM CSS test also cannot catch this box-sizing and scrollbar-gutter interaction.

Checked the full diff from the current merge-base, the round-1 observer and height fixes, removed-host cleanup, Pierre's shadow-root CSS and gutter behavior, Shell/output lane states, scroller scope, and the gate-owned files. The live GraphQL head is unchanged at this SHA; no app build, gallery capture, or full-tree check was run.

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