fix(coordinator): stop a Users & Roles tab from pinning the window split dividers (#1872)#1875
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0f1628f88c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| primaryItem.isCollapsed = true | ||
| didAutoCollapsePrimary = true |
There was a problem hiding this comment.
Avoid persisting automatic collapses as user state
When a Users & Roles tab is narrowed, setting primaryItem.isCollapsed = true on this autosaving split view can persist the zero-width primary pane under autosaveName. If the tab/window is later recreated at a wide size, didAutoCollapsePrimary starts as false, so the restore path looks like a manual collapse and line 120 never expands the principal list. Users who once narrow the tab can therefore reopen it with the user list stuck hidden; avoid autosaving the automatic collapse or record enough state to undo it after restore.
Useful? React with 👍 / 👎.
Second root cause found:
|
| holdingPriority | real synthetic drag |
|---|---|
| 250 / 260 / 489 | moves |
| 490 | frozen |
| 750 (previous default) | frozen |
The mouse events land correctly (mouseDown reaches the split view, the tracking loop runs, splitViewDidResizeSubviews fires 18-30 times). The panes simply refuse to move.
This predates the branch: .defaultHigh arrived with the Users & Roles feature, which is why those dividers never worked since 0.57.0. QuerySplitView leaves the priority at the default 250, which is why the query editor split always worked.
Fix
New NSLayoutConstraint.Priority.splitPaneHolding (260, the value AppKit itself assigns a sidebar item). High enough to outrank a .defaultLow (250) sibling so the pane holds its width when the window resizes, low enough that a drag still wins. .defaultLow is not the fix: measured at 250 the pane grows with the window instead of holding.
Same bug fixed in two more places it had spread to:
TriggerDetailView(Structure > Triggers) inherited the 750 default.ServerDashboardSplitViewset.defaultHighon its metrics and slow query items.
Also lowered the privilege scope pane's minimum from 240 to 200: at the tab's 560pt minimum width the scope divider had only 19pt of travel, which still felt frozen. It now has 59pt.
sizeThatFits is kept. It is not the cause, and removing it makes split A's NSSplitView overflow its host (761pt wide inside a 560pt pane).
Tests
SplitPaneHoldingPriorityTests pins the invariant directly: the holding priority must stay below the 490 drag priority and above the 250 sibling priority. That is the regression test that would have caught this.
Fixes #1872.
The bug
The right sidebar divider wasn't broken, it was outvoted.
NSSplitViewItem.minimumThicknessis a required constraint, so the nested split view inside a Users & Roles tab reportedfittingSize = 200 + 560 + divider = 764pt. SwiftUI adopts that number for anNSViewControllerRepresentable, and the enclosingNSHostingViewturned it into aminWidthat priority 999.9. A divider drag is applied at priority 490 (dragThatCannotResizeWindow), so the drag lost every time.Sidebar 280 + detail 764 + inspector 270 + dividers is about 1316pt of required width. Below that the divider is frozen. Nothing ever broke, so there were no constraint-conflict logs to find.
Only this tab was affected:
QuerySplitViewandServerDashboardSplitVieware also nested split views, but both setisVertical = false, so their minimums never constrain width.The fix
Two defects, both addressed.
1. Tab content could leak a width floor into window chrome.
MainSplitViewController'sdetailHostingandinspectorHostingwere bareNSHostingControllers with the defaultsizingOptions(.standardBounds). They now use[], andAutosavingSplitViewreturns the proposal fromsizeThatFits, so an inner split's minimums can no longer escape into SwiftUI. This closes the class of bug rather than this one instance.2. The tab demanded 764pt from a pane promised 400pt. The principal list now collapses on its own when there isn't room for both panes (
CollapsingSplitViewController), which drops the tab's honest floor to 560pt.resolveDetailMinimumThickness(for:)declares that as a per-tab contract andrecomputeWindowMinSize()reads it live instead of hardcoding 400. Window minimum with both panels goes from 952 to 1112 only while a Users & Roles tab is frontmost.Why the collapse is explicit
AppKit does not help here.
.sidebarbehaviour,canCollapseFromWindowResizeandminimumThicknessForInlineSidebarsauto-collapse only on a window live-resize, which an embedded split view never sees. Probes against the real runtime squeezed a sidebar pane to 0px and it still reportedisCollapsed == false. No form of collapsibility lowersfittingSize; only an actualisCollapsed = truedoes. That is recorded as an Invariant in CLAUDE.md so it does not get simplified away later.HIG, Sidebars: "Consider automatically hiding and revealing a sidebar when its container window resizes."
Tests
MainSplitViewControllerDetailWidthTestsadds 11 cases pinning the geometry that caused the bug, including the 560pt tab minimum and the 1112pt window minimum.swiftlint lint --strictis clean on every changed file.No
docs/change needed:users-roles.mdxdocuments the tab's content, not window-chrome resize behaviour.Manual verification
Open a PostgreSQL connection, open Users & Roles, show the inspector (Opt-Cmd-I), drag its divider. Then narrow the window and confirm the user list collapses instead of pinning the divider.