Skip to content

fix(coordinator): stop a Users & Roles tab from pinning the window split dividers (#1872)#1875

Merged
datlechin merged 2 commits into
mainfrom
fix/1872-users-roles-inspector-resize
Jul 14, 2026
Merged

fix(coordinator): stop a Users & Roles tab from pinning the window split dividers (#1872)#1875
datlechin merged 2 commits into
mainfrom
fix/1872-users-roles-inspector-resize

Conversation

@datlechin

Copy link
Copy Markdown
Member

Fixes #1872.

The bug

The right sidebar divider wasn't broken, it was outvoted.

NSSplitViewItem.minimumThickness is a required constraint, so the nested split view inside a Users & Roles tab reported fittingSize = 200 + 560 + divider = 764pt. SwiftUI adopts that number for an NSViewControllerRepresentable, and the enclosing NSHostingView turned it into a minWidth at 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: QuerySplitView and ServerDashboardSplitView are also nested split views, but both set isVertical = 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's detailHosting and inspectorHosting were bare NSHostingControllers with the default sizingOptions (.standardBounds). They now use [], and AutosavingSplitView returns the proposal from sizeThatFits, 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 and recomputeWindowMinSize() 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. .sidebar behaviour, canCollapseFromWindowResize and minimumThicknessForInlineSidebars auto-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 reported isCollapsed == false. No form of collapsibility lowers fittingSize; only an actual isCollapsed = true does. 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

MainSplitViewControllerDetailWidthTests adds 11 cases pinning the geometry that caused the bug, including the 560pt tab minimum and the 1112pt window minimum. swiftlint lint --strict is clean on every changed file.

No docs/ change needed: users-roles.mdx documents 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +115 to +116
primaryItem.isCollapsed = true
didAutoCollapsePrimary = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@datlechin

Copy link
Copy Markdown
Member Author

Second root cause found: holdingPriority = .defaultHigh

Testing this branch surfaced that the dividers inside the Users & Roles tab still would not drag. Probing the real AppKit runtime with a synthetic drag harness found a second, independent bug, and it reproduces with zero SwiftUI involved.

AutosavingSplitView defaulted primaryHoldingPriority to .defaultHigh (750). A divider drag is applied at priority 490 (dragThatCannotResizeWindow), so a pane at 750 outranks the drag: the width constraint wins and the divider physically cannot move.

Measured on a bare NSSplitViewController:

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.
  • ServerDashboardSplitView set .defaultHigh on 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.

@datlechin datlechin merged commit 6bc532e into main Jul 14, 2026
2 checks passed
@datlechin datlechin deleted the fix/1872-users-roles-inspector-resize branch July 14, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

roles users

1 participant