Repository navigation
cmux NEXT panes: a pane in a column with rows closes in the click's turn, never after the daemon's tree - #32907
Conversation
The layout's optimistic close now takes a pane out of its row as cmux-tui's tree does after the close (remove_pane_from_screen_layout, normalize_rows): an emptied row leaves its column, the rows left keep their ids and heights, one row left is a plain column (and a lone one the screen's split tree), and the compat chain refolds from the heights. Close sizing and the focus successor read the row's tree. The agent chat dock's role is released as the daemon releases it when its top pane leaves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-ed9226b71907d8aaa0b6
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Shipped in:
|
|
Review (codex, router-review): SHIP at 9955ef3 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 1. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at 9955ef3 No correctness findings from reviewing the close paths against daemon normalization and snapshot mapping. Swift tests could not run because this box lacks the toolchain. Follow-ups:
|
|
Swift logic follow-up (tip watcher swift-logic row, STATUS 13027 "SWIFT LOGIC CULPRITS"; lead order). This PR raised
plans/cmux-next/layer-ownership.md says Rust owns every state, list and rule and Swift is the platform shell. Please move the new logic of this PR in a follow-up: to Rust (the crate that owns the area; the owner lists in STATUS 12147-12165 name the target per area), or at least out of the app into its package. If a declaration above is platform code (AppKit, Core Animation, OS calls, strings, tokens, DEBUG tooling), reply here and the table gets a PLATFORM row with your reason instead. The swift-logic row is a report, not a gate: nothing blocks on this comment. |
Leo (2026-11-03): "it's so slow to open and close idk if there's some sort of threading issue but it's nigh instant for claude and codex so we nede to be at least as fast". (2026-10-25): "closing the right pane seems be like... waiting for the right tab's closing animation to play before closing".
leo-named-fix: closing a pane never waits for anything; the close takes effect on the click.
What changed
Before this change, a pane in a column with rows (rows-v1) waited for the daemon's tree when its last tab closed or on Close Pane:
removingSplitPanereturned nil forhasRows, soshowPaneClosehad nothing to show. Now such a pane leaves in the click's turn, the same way #25163 made a column's only pane leave.LayoutModel.removingRowPanemirrors cmux-tui's close (remove_pane_from_screen_layout, thenLayoutColumn::normalize_rowsandproject_layout_columns):normalize_rowsdrops it;rows = [],root= that row's tree), and a lone column like that becomes the screen's split tree again unless it is a pinned dock;rootchain is folded again from the rows: chain split ids are kept, and each ratio is the heights above over the heights through, computed in f32 asfolddoes. That keepsratio(of:)andhasSameStructureequal to the echo's;release_agent_chat_role). The same applies to the plain-column path.CloseSizing.tree(in:holding:)now reads the row's tree, so even sizing equalizes a chain inside a row. A row's only pane leaves no chain to size, and the rows that stay keep their heights.successor(of:in:)picks the row sibling's first pane, then for a row's only pane the row above, else the one below. In the app the focus reducer still makes the pick.The close-animation DESIGN.md is updated to match.
Tests (PaneCloseControllerTests, through the real controller and the daemon store's echo)
aRowsOnlyPaneLeavesInTheKeysTurnAndItsEchoShowsTheSame: rows beside a column. The bottom row's pane leaves, the column becomes plain, and the echo's layout==what was shown.closingRowsOfALoneColumnShowWhatTheDaemonsTreeShows: three rows. The top row's close keeps rows 22 and 23 with their heights, then the bottom row's close leaves the screen as a split tree. Each echo==what was shown.aPaneLeavingARowsChainEvensTheChainThroughItsEcho: even sizing inside a row. The echo keeps the ratio, and it is sent once.aRowsPaneLeavesFasterThanTheDaemonsTreeBringsIt: the timing below.Without the fix, each
#require(showPaneClose())fails, because it returns nil for a row pane. All five PaneClose suites pass at 9955ef3 (fleet step 022f196b56f713a7b1d67db5), and the CmuxNext compile check passed (job d93c8e0e9905194939551043).Performance
aRowsPaneLeavesFasterThanTheDaemonsTreeBringsIt, 20 rounds, fleet step 022f196b56f713a7b1d67db5The harness times the echo's apply alone with the daemon's reply already in hand, so the "before" numbers are a lower bound: in the app the pane also waits for the close-pane round trip and the snapshot that follows it.
Risk
This changes layout state, but only the shown overlay: nothing new is sent to the daemon, and a refused close brings the pane back (
cancelPaneClose). If the mirror got a daemon row rule wrong, the echo would make a one-frame structural jump. The echo-equality tests cover the row-leaves, top-row-leaves, last-two-rows and chain-sizing cases. A chain that is not the daemon's fold, or a pane in no mapped row, falls back to a plain removal or to waiting for the daemon's tree. The terminal close guard and the browser beforeunload path are untouched.Agent session: agent session
🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Makes closing a pane in a column with rows take effect in the click's turn instead of waiting for the daemon's tree, so the right pane closes as fast as other panes.
normalize_rowsfor row panes: an emptied row leaves its column, remaining rows keep their heights, and a lone column becomes the screen's split tree. Close sizing and the focus successor read the row's tree, and the agent chat dock releases its role when its top pane leaves.Written for commit 9955ef3. Summary will update on new commits.