cmux-next sidebar: remember collapsed sections
Summary
First of the sidebar-categories PRs. A collapsed layout section now stays collapsed after a relaunch.
Projects and Chats already collapse from their headers, with a chevron on hover. Before this PR, only the workspace sections kept their collapsed state (in the sidebar snapshot). Now the layout sections keep theirs too, in UserDefaults (SidebarCollapsedSections, wired through SidebarModel.onCollapsedLayoutSectionsChange in SidebarBridge).
An earlier version of this PR also moved Recents into the list's scroll document, right under the last workspace row. Base has since replaced Recents with the optional Chats band below the list, which is newer, so this PR keeps base's layout and drops that part.
Next PRs: Hide section, user groups, Group by folder.
Testing
- Failing tests first (6dc8b70d226):
SidebarCollapsedSectionsTests(save and load, the model reports toggles) failed to compile before the fix. - Fleet step 17be3407c2003f0f06e1283f at eebc91d8b72 (Xcode 27.0):
SidebarCollapsedSectionsTests2/2 passed. The collapse code hasn't changed since. - Base's
SidebarChatsFooterTestscovers the layout this PR no longer touches; hostedcmux-next swift testruns it on this head. - Local: package conventions lint, god files, concurrency and crash-safety checks pass.
Captures
Recents collapsed from its header (left), then the app quit and relaunched (right): it stays collapsed. This was shot before base renamed the section to Chats and before this PR dropped the layout change, so the section sits under the list here. The collapse behavior is the same.

Changelog
- Changed: A collapsed sidebar section stays collapsed after relaunch.
🤖 Generated with Claude Code
[!NOTE]
Low Risk
Client-only UI state in UserDefaults with a small model callback; no auth, daemon, or data-path changes.
Overview
Layout sidebar sections (e.g. Chats and other app/item bands) now remember collapsed vs expanded across app relaunch. Workspace/machine sections are unchanged—they still persist per window in the sidebar snapshot.
The app introduces SidebarCollapsedSections, which reads and writes a Set<LayoutSectionID> in standard UserDefaults (cmux.next.sidebar.collapsedSections). SidebarBridge builds the sidebar model through restoring(SidebarModel()) so saved collapse state is applied at window open and every toggle is saved.
SidebarModel gains a didSet on collapsedLayoutSections and an onCollapsedLayoutSectionsChange hook (observation-ignored) so the app layer can persist without pulling UserDefaults into the sidebar package. Unit tests cover round-trip save/load and toggle callbacks.
Reviewed by Cursor Bugbot for commit 45cf407502ce334a569977ea0a78d077987a1781. Bugbot is set up for automated code reviews on this repo. Configure here.