feat(cmux-next): closing a multi-row selection is one action, and empties no group

Closed
TE
7d ago
+5737-5478

Summary

When several sidebar rows are selected, closing one of them now closes all of them as a single action. This covers the row menu, the close shortcut and Delete. Before, each row closed by itself and asked its own question, and a group the close emptied was left behind empty.

  • The row menu reads Close N Workspaces while the right-clicked row is one of N selected rows. A single row keeps "Close Workspace".
  • The close goes through WorkspaceBulkClose, the path Close Others, Above and Below already use. It asks one question, and only when a terminal is running a program and app.warnBeforeClosingTab is on (#18628), or when no window stays open to undo. Then one "Closed N workspaces" toast offers Undo for every row.
  • A group whose every row is in the selection closes the way Close Group does (cx-rcby). An unpinned group is removed, and the same Undo brings it back with its workspaces. A pinned (saved) group stays collapsed, as with Close Group. A group that also lists a link, tab or app is left alone.
  • Rows outside an emptied group keep their instant close: they hide at once and the selection moves on.

The decisions about which groups close and what the menu says live in CmuxNextSidebar (SidebarModel.closePlan, closeTitle). The first commit moves four hand-written group lookups out of the app handlers onto SidebarModel (groups, group(containing:), SidebarSection.containers), with no behavior change. That leaves CmuxNextApp one source line smaller than its base, under the size cap.

Testing

  • SidebarSelectionCloseTests (CmuxNextSidebarTests) is committed before the code in 2026b05b, where it fails to compile because closePlan and closeTitle don't exist yet. It covers a group the selection empties, a group with a row left open, a group with a link left, and the menu title for one row and for several.
  • This PR has full-ci, so the cmux-next run compiles the app and runs swift test. The results will be in the PR thread once the run finishes.
  • check-app-size.sh --base passes locally: 84279 source lines against 84280 on the base.
  • Not checked live: the dialog and toast wiring in CmuxNextApp, which no unit test reaches. The before/after captures below are the live check.

Changelog

Changed: Closing several selected sidebar workspaces is now one action, with one confirmation and one Undo, and it removes any group it empties

Proof

Before/after captures will be posted in a comment once the dev build is ready.

Localization

sidebar.closeWorkspaces ("Close %lld Workspaces") is added to the CmuxNextSidebar catalog in all 21 locales. en and ja are marked translated; the rest are needs_review, as the catalog's other strings are.

šŸ¤– 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

Closing a multi-row sidebar selection is now one action: one confirmation asked before the close, one "Closed N workspaces" toast, and one Undo that reopens every row. Previously each row closed by itself, asked its own confirmation, and a group the close emptied was left behind empty.

The close routes through WorkspaceBulkClose, the same path Close Others and Above/Below use. A group whose every row closes empties like Close Group does: an unpinned group is removed and Undo brings it back, a pinned group stays collapsed, and a group that also lists a link, tab, or app is left alone. The row menu reads Close N Workspaces for multi-row selections. Rows that cannot close — Home, placeholders, rows already closing — neither count in the title nor anchor the close, so Delete on a selection starting with Home still closes the rest; a selection left with one closable row takes the plain close. Undo's group records carry the personal group id that WorkspaceGroupClose.reopen and DaemonClosedHistory match, not the sidebar document's itm_g_ item id. Group lookups moved onto SidebarModel, sidebar.closeWorkspaces was added in all 21 locales, and tests run under CmuxNextAppTests.

The dialog and toast wiring in CmuxNextApp isn't reachable from unit tests and still needs a live check.