Skip to content

Agent pane: in Minimal Mode another agent's message is one line that opens in place - #25002

Merged
teamleaderleo merged 10 commits into
feat-cmux-nextfrom
claude/minimal-message-collapse
Oct 25, 2026
Merged

teamleaderleo merged 10 commits into
feat-cmux-nextfrom
claude/minimal-message-collapse

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-24:

"I also wonder if we should, like in minimal mode, have the option for, like, the entire thing to be just collapsed into one line or something. Like, I don't know if we need to, like, prepend, like, received message from blah blah blah, but potentially, like, we could just have the first line be the thing that's there, and then you can expand that, and then that would expand outwards towards, like, the entire thing. I don't know. Like, we can surely do some sort of elegant UI thing, but I just feel like I'm not entirely sure when the previews would make sense for these. I think it's okay right now, but I'm not sure how other people would feel. You know, especially in minimal mode. Because we could just, like the preview. I'm pretty sure, like one line is kind of enough sometimes for the, like the preview of the message or whatnot."

What changed

With cmux's Minimal Mode on (sidebar.minimal, the palette's Enable Minimal Mode), another agent's message in the agent pane is one line:

  • Closed: the sender's chip, then the message's first non-empty line, then a chevron. The chip is unchanged and still opens that chat. The line has its Markdown marks and link targets dropped and ends in an ellipsis when it is too long. There is no "Received message from" prefix.
  • Opening and closing: clicking the line, or pressing Enter or Space on it (it is a native <button> with aria-expanded), opens today's full glass card in place. Clicking again closes it.
  • Motion: the open and close use the transcript's shared fold (useAnchoredCollapse / useFold, the same one tool rows use). The line stays where it is drawn, and Reduce Motion or the host's animations-off setting makes it happen at once.
  • Images: a closed message hides its images.
  • Normal mode is unchanged. The base and head renders of the same gallery chat with Minimal Mode off are byte-identical PNGs, in both themes.

How the page knows about Minimal Mode: AgentPaneDisplaySetting (Swift) now also sends sidebar.minimal in the chat's display push, and chatDisplay.ts reads it as minimal. The page cannot write that key (writablePath refuses it, and a test covers this). The row estimator (model.ts) counts a closed message in Minimal Mode as its line alone.

Transcript placement (App.tsx), after review:

  • A Minimal Mode toggle makes one O(rows) pass, once per toggle and never per event or token. It forgets the drawn heights of incoming-message rows, so offscreen rows are placed by the new estimate.
  • A new chat look, including a Minimal Mode toggle, now places every row again: the placement keeps no unchanged prefix across a look change.
  • Opening has no fade-in. The fold's height moves at full opacity (useFold's new fadeIn: false, which defaults to true, so other folds are unchanged). Closing still fades out.
  • The "Replied" note and images stay hidden while the message is folded.
  • The preview skips code-fence markers (a fence closes only on its own marker), lone > lines and link definitions. It strips links (parentheses in the URL, titles, references, defined shortcut references, images and autolinks) and single */_ emphasis, but not snake_case.

Other changes:

  • messagePreview (incomingMessage.ts, which nothing else used) now drops a link's target, so the line shows "Opened fix(next): keep composer drafts on whole lines #24772", not the URL.
  • New string message.collapsedLabel ("Message from {from}: {preview}"), the closed line's accessible name, is in Localizable.xcstrings with all 21 locales.
  • The gallery's agent-pane host accepts display on a variant, which is how the Minimal Mode variants turn it on.

Hooks and cleanup: the change adds no timer, observer or animation loop of its own. The fold is the existing useAnchoredCollapse/useFold: its Web Animation is cancelled on the next toggle and on unmount, and its motion-policy MutationObserver is disconnected when the fold ends. The text box's existing ResizeObserver measure keeps its disconnect() cleanup and now also re-runs when the mode switches, because the card moves into a new box. The change does no per-token work: the preview is memoized on the message body.

Before / after (gallery agent-pane.incoming-message, Linux Chromium, 560 px)

Before is the base with the same gallery file and Minimal Mode on. On base, Minimal Mode does not reach the chat.

Before (base, Minimal Mode on) After (this PR, Minimal Mode on)
dark
light

The long report's line opened (minimal-open), and Minimal Mode off (minimal-off, identical to base):

Opened Minimal Mode off
dark
light

Variants (gallery only, except A)

A (shipped): chip, first line, ellipsis, chevron B: fade instead of ellipsis, "+N lines" C: no chip, name in bold inline

I picked A.

  • It keeps the chip, so a click still opens the sender's chat, and it keeps the harness mark.
  • Its chevron is the same affordance every other transcript fold uses, so the line reads as expandable.
  • The ellipsis is the quietest way to show that the text was cut.

Why not B or C:

  • B's "+N lines" adds a second dim label per row, and its fade hides the last words.
  • C drops the clickable chip and the mark, so the sender's chat can no longer be opened from the line.

B and C are drawn in the gallery only (minimal-fade-count, minimal-inline-name), as a stylesheet over the shipped line.

Risk

Small and contained: this only changes how incoming agent messages render in Minimal Mode, plus one read-only key added to the host's display push. Nothing changes with Minimal Mode off. No default changes, and no existing component is removed or replaced.

Overlap: #24085 (anchored, animated open in normal mode) edits the same toggle in IncomingMessageCard.tsx. This PR leaves normal mode's toggle as it is, and whichever lands second rebases.

The Swift part (AgentPaneDisplaySetting + its test) was not compiled locally, because Mac builds don't run on this machine. CI's Swift compile and tests cover it.

Tests

On huge-blue (Linux):

  • New test webviews/test/incoming-minimal-collapse.test.tsx. It checks:
    • Minimal Mode resolves only from sidebar.minimal === true.
    • With Minimal Mode off, the line is drawn over the whole card, as before.
    • In Minimal Mode:
      • the chip and the first line are shown, the Markdown is stripped, and there is no "Received message from" prefix;
      • the chip still opens the chat;
      • the line toggles open and closed, with aria-expanded and aria-controls set;
      • turning the mode off draws the card again.
  • messagePreview link case added to incomingMessage.test.ts.
  • AgentPaneDisplaySettingTests.thePageFollowsMinimalMode, and sidebar.minimal added to the not-writable keys (Swift, runs in CI).
  • bun test on the new test, incoming-sender-chip, incomingMessage, agent-pane-chat-display, transcript-received-messages and rowEstimates: 70 pass, 0 fail.
  • vp check --no-fmt --no-lint (typecheck): no errors. vp fmt --check and vp lint on the touched files: clean.
  • check:catalog-keys, check:motion-scale, check:design-tokens and check:layers pass.

No capture: the one native change (AgentPaneDisplaySetting.swift) draws no UI. It forwards the existing sidebar.minimal setting into the chat page, and everything that draws is the webview, captured above from the gallery in light and dark at the base and at this head (gallery agent-pane.incoming-message).

Agent session: cosmic-gray

🤖 Generated with Claude Code

teamleaderleo and others added 2 commits October 24, 2026 15:48
In cmux's Minimal Mode (sidebar.minimal, which the host now sends with the
chat's display keys) a closed incoming agent message draws as its line
alone: the sender's chip (still opening that chat), the message's first
line with an ellipsis, and a chevron. The line opens the full card in place
through the transcript's shared fold and closes it again. Normal mode is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray
@cursor

cursor Bot commented Oct 25, 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

Owned by claude-sr2/subsession-card on cosmic-gray · session

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 25, 2026
…mal-message-collapse

# Conflicts:
#	webviews/src/gallery/format.ts
#	webviews/src/gallery/frame/agentPane.ts
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 41a7ecf: 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=pr25002 -f ref=claude/minimal-message-collapse -f platform=macos -f backend_mode=local

Probably yours: agent-pane-tool-group-alignment.test.ts, composer-footer-essentials.test.ts, accent-glass-invariants.test.ts fails here but not on main's latest full suite. Seen on other PRs too (likely flaky): agent-pane-chat-width.test.ts, agent-pane-queue-collapse.test.ts, agent-pane-queue-collapse.test.ts and 10 more. This PR merged before its CI finished: fix forward on main.

CI failed on 41a7ecfa0f (run 40155781916 attempt 1): 2 code, 1 unknown.
macOS jobs did not run: compile and app tests were skipped: Fast static checks skipped.

Job Verdict Why
web / react-apps-base-check (1) code a web test failed
web / react-apps-base-check (4) code a web test failed
web / react-apps-check unknown no known signature; failed step: Check webview test shards

Failures:

  • seen on other PRs agent-pane-chat-width.test.ts (unnamed): failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > an opened long queue keeps Show less and the focused row in sight, never a half row (...: failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > an opened queue with a two-line row shows whole rows only, no row under another (capt...: failed (job)
  • seen on other PRs agent-pane-queue-collapse.test.ts a long queue > a folded queue with a long row opened shows only whole rows above the fold (capture, ...: failed (job)
  • new in this PR agent-pane-tool-group-alignment.test.ts (unnamed): failed (job)
  • seen on other PRs chat-palette-actions.test.ts (unnamed): failed (job)
  • seen on other PRs claude-reasoning-default.test.ts (unnamed): failed (job)
  • new in this PR composer-footer-essentials.test.ts (unnamed): failed (job)
  • seen on other PRs claimPaint.test.tsx a pooled Settings page claimed by the host reports its paint once its first screen is in: failed (job)
  • seen on other PRs claimPaint.test.tsx a Settings page loaded fresh reports its first paint once its first screen is in: failed (job)
  • ... 6 more in the job logs
Matched log lines
web / react-apps-base-check (1): (fail) (unnamed) [2313.44ms]
web / react-apps-base-check (4): (fail) a pooled Settings page claimed by the host reports its paint once its first screen is in [186.27ms]

Not re-run automatically: web / react-apps-base-check (1), web / react-apps-base-check (4), web / react-apps-check are not machine failures.

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); base red: the same failure on the base ce5e5c3; does not block
red: web / Web status; NEW: green on the base ce5e5c3; do not merge until green; see lane-rules
red: web / react-apps-base-check (1); no finished base run on ce5e5c3 to compare; do not merge until green; see lane-rules
red: web / react-apps-base-check (4); no finished base run on ce5e5c3 to compare; do not merge until green; see lane-rules
red: web / react-apps-check; no finished base run on ce5e5c3 to compare; do not merge until green; see lane-rules

Tour of 41a7ecfa0f (run): no media. The tour job failed before the tour ran (build or setup); the run log has the output. Both apps run cmux-tui from 6cae8ed (tree caa1ca9f08d5): the merge base's own tree d7a3fdb04841 was not published yet.

Gallery before/after for 41a7ecf: the capture produced no images (run).

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@blacksmith-sh

blacksmith-sh Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Found 21 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmpc4a9n0ke/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmppff425i8/summary-fail.json (live):/summary-fail.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames 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
bundle-cmux-tui.sh failed (/tmp/tmp.ufC6LdqaVl/new/cmux-tui) View Logs
bundle-cmux-tui.sh failed (/tmp/tmp.ufC6LdqaVl/new/cmux-tui) View Logs
bundle-cmux-tui.sh failed (tree, /tmp/tmp.9aXNOs2YKz/build/one/cmux-tui) View Logs
bundle-cmux-tui.sh failed (tree, /tmp/tmp.9aXNOs2YKz/build/one/cmux-tui) View Logs

...and 11 more test failures. View all on Blacksmith: run 40155781857, run 40155781870

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

…preview)

- A Minimal Mode toggle forgets incoming-message rows' drawn heights, so
  offscreen rows place by the new estimate (one pass on the toggle).
- The line's card opens with no fade-in: useFold/useAnchoredCollapse take
  fadeIn (default true, other users unchanged); closing still fades.
- A sent reply's "Replied" note hides while the message is folded.
- messagePreview skips code fence markers (first code line), lone quote
  markers and reference definitions, and strips links with parens in the
  URL, titles, references, images and autolinks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray
- A new chat look (a Minimal Mode toggle included) places every row again:
  the placement keeps no prefix across a look change, so offscreen incoming
  rows with no drawn height move to the new estimate too.
- messagePreview drops single *italic* and _italic_ (not snake_case), closes
  a fence only on its own marker at least as long with nothing after it, and
  reads a shortcut reference [docs] as its label when the message defines it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray
… refs)

- The opened state of another agent's message moves to incomingOpened.ts,
  and the row estimator reads it: in Minimal Mode an opened message
  estimates as its whole card, a closed one as its line.
- messagePreview finds link definitions outside fenced code only, reads
  [label][id] and [label][] as the label only when the id is defined, and
  skips thematic breaks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16f9b-2fcb-7c72-b06e-af8fce00adcb&host=cosmic-gray
@teamleaderleo
teamleaderleo merged commit bdc2e73 into feat-cmux-next Oct 25, 2026
137 of 148 checks passed
@teamleaderleo
teamleaderleo deleted the claude/minimal-message-collapse branch October 25, 2026 03:04
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

teamleaderleo added a commit that referenced this pull request Oct 26, 2026
The envelope's fold ran in Minimal Mode too, animating Details and leaving the
envelope mounted while it closed; #25002 shows and hides it at once there. The
fold now runs only out of Minimal Mode. From the review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0
teamleaderleo added a commit that referenced this pull request Oct 26, 2026
…ithout moving the view (#24085)

* Agent pane: another agent's message opens below its line, animated, without moving the view

The card's line and its Details are anchored collapses (useAnchoredCollapse, the transcript's
shared hook from #24050): the line the reader clicked (chevron, plain sender name, Reply, or the
cut card) stays on its pixel while the card under it changes, inside the virtualized transcript.
Before, at the latest row the view followed the card's growth and the line jumped 235 px up.

A cut card slides open to its whole height and back (fold.ts playResize: height over
--cv-fold-duration, easing out); Details and its envelope fold in and out in height and opacity.
At once under Reduce Motion; Find opening a message for its match does not animate.

useAnchoredCollapse.ts, ANCHORED_COLLAPSE_EVENT and App.tsx's holdAsked listener are #24050's,
byte for byte, so this rebases onto it cleanly once it lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16028-eee2-75a1-b075-2e4023976100&host=Air-Blue

* Agent pane: Reply opens a message without holding its line, so the editor is in view

Holding the line while Reply opened a long message left the focused editor below the view at the
latest row. Reply now opens the card as before (the view follows the editor), still animated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16028-eee2-75a1-b075-2e4023976100&host=Air-Blue

* Agent pane: a cut message's card never shrinks a frame as it opens

Show more leaves at once while the text slides from its cut height, so the card
was a button shorter for one frame; at the latest row that pulled the transcript's
end, and the held line, 24 px up. The text now starts at the height that keeps the
card as tall as it was drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: Find opening a closing message stops its slide

A card sliding shut that Find opened again kept playing its resize, clipped
until it ended and then snapped open. Find's open now cancels it (fold.ts
cancelResize). From the review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: Minimal Mode's Details opens and closes at once, as before

The envelope's fold ran in Minimal Mode too, animating Details and leaving the
envelope mounted while it closed; #25002 shows and hides it at once there. The
fold now runs only out of Minimal Mode. From the review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: rows below a message's card follow its slide before paint

playResize now marks its row as folding (heightFoldInFlight), as useFold does, so
the transcript places the rows under a sliding card synchronously instead of a
frame late. A long message with no Details had nothing else marking it. From the
review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: Find stops a message's slide even when it is already open

Find's reveal cancelled the text's resize only through the [open] effect, so a
card the reader had just opened kept sliding when Find landed on it. The reveal
now cancels it directly. From the review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: a cancelled message slide lets go of its clip at once

cancelResize cleared the text's overflow only from the animation's cancel
event, which comes after the next paint, so Find opening a sliding card could
show it clipped for a frame. It now clears it before cancelling. From the
review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

* Agent pane: each display mode folds its own message box

Minimal Mode's card and the normal card's Details row shared one collapse, so
switching Minimal Mode mid-close moved its ref to a new box while the old fold
played on, and the whole card showed under the preview until it ended. Each
mode now has its own collapse; the one not drawn changes at once. From the
review of #24085.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-26b034d85e005a5da1d0

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 41a7ecfa Deployed Oct 25, 2026 by teamleaderleo via namespace autofix #2563
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