refactor(editor): rebuild the query tab around pure presentation models - #2892
Merged
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
datlechin
force-pushed
the
refactor/query-tab-native-revamp
branch
from
September 15, 2026 09:07
ac07176 to
f4b47b6
Compare
datlechin
force-pushed
the
refactor/query-tab-native-revamp
branch
from
September 15, 2026 09:24
f4b47b6 to
705bb5f
Compare
This was referenced Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
A rebuild of the query editor tab, editor through results, so that its behaviour is decided by pure
models instead of by conditionals inside SwiftUI bodies, and so that each control is the one macOS
actually provides for the job.
Root cause
The tab implemented three platform roles by hand inside its own content:
QueryEditorView'sHStack, opening withText("Query").font(.headline)namingthe pane it was already inside, then three borderless icon buttons, a
Divider, a.bordered.smallExplain and a.borderedProminent.smallExecute: three control sizes, two buttonstyles, five controls, no grouping),
ResultTabBar, a 32pt band of hand-drawn tabs with hover-revealed closebuttons, spent in every query tab including the common one with a single result),
Underneath,
MainEditorContentView.resultsSectionwas a ~100-line switch overResultsViewModewhose five arms each repeated the result chrome, wrapped around a five-way
if/else if/else. Threeof those arms tested
lastExecutedAt != nil && !isExecutingbeside a different companion condition,so the result states were only accidentally exclusive, and none of them could be checked without
mounting a view.
What changed
A pure resolver decides the results pane.
QueryResultPresentationtakesQueryResultInputsand returns which content case to draw plus whether the filter chrome, find bar, result chooser,
error banner and status bar show.
resultsSectionis now a render of that one value. This followsthe pattern
ResultStatusModel,StatusBarSnapshotandResultsModeAvailabilityalready set inthis same directory.
A pure model decides the commands.
QueryCommandAvailabilityanswers which of Run, Stop,Explain, Format, Favorite, Clear Query and Clear Results are live, and carries the reason a dimmed
one is dimmed so the control can say why rather than only greying out.
The result strip is gone; result sets are chosen from the status bar. There is no native control
on macOS 14 that draws a closable, pinnable tab strip for non-window content, so the collection is
re-expressed rather than redrawn. Measured against the SDK headers for this toolchain:
NSTabViewItemhas exactly ten properties and not one is a close, pin or reorder affordance;grepping
closable|closeButton|isPinned|canReorderacrossNSTabView*.handNSWindowTabGroup.hreturns nothing.
NSWindowTabGroup.windowsis[NSWindow], so the one native closable, reorderable, overflowingstrip takes windows only.
Tab,TabSection,SidebarAdaptableTabViewStyleandTabCustomizationBehavior.reorderableare all macOS 15+, and none declares a close affordance.The HIG names the replacement: a pop-up button is the "reasonable alternative in cases where there
are too many panes" for a tab view, and result sets are unbounded.
ResultSetMenuis that control,in the bar's leading zone, which is where Postico 2 puts the same thing. Pin, Unpin, Close and Close
Others move across from the strip's context menu unchanged. It is deliberately shown even for a
single result, because Pin lives in its menu and hiding it there would leave the View menu as the
only route to pinning;
ResultSetPolicyTestspins that invariant.Query history moved to the trailing pane.
TrailingPaneSurfacegains a.historycase beside.inspectorand.assistant, so the tab's third stacked band and its nested split view are gone.HistoryPanelView's own list/detail split rotates from horizontal to vertical, which is what makesit fit: the trailing pane is one shared
NSSplitViewItemwith a 270pt floor, and that file recordsthat per-surface minimums were measured and rejected because raising one force-grows the pane past
the width the reader chose. Its autosave name moves with the orientation, since a stored horizontal
divider offset means nothing in a vertical split.
Clear Query and Clear Results are two commands. They were one trash button whose tooltip and
accessibility label both read "Clear Query" while it also cleared the results, the execution record
and collapsed the results pane. Neither half had a menu-bar command. Both now do, and both ride the
Run button's menu.
Why the commands are in the tab and not the window toolbar
This was built the other way first, with Run, Stop, Explain, Format and Favorite as
NSToolbarItems,and reverted.
NSToolbarbelongs to the window, and a window here hosts table, structure, dashboardand diagram tabs that have nothing to run, so the items sat permanently dimmed on all of them.
Varying the item set per tab is not available:
NSToolbar.itemIdentifiersis macOS 15 (above the14.0 target) and its own header says it "will override any customizations the user has made" when
allowsUserCustomizationis enabled, which this toolbar has. A control that belongs to the editorlives with the editor, which is what Xcode's jump bar and Script Editor's navigation bar do. The
window toolbar is unchanged by this PR.
Run and Stop are one control rather than two, because the bar is inside the pane and a permanently
dimmed second button costs width the editor wants. TablePlus does the same: its Cancel appears for a
long query rather than standing there dimmed.
QueryCommandAvailabilityTestsasserts the two arenever both actionable and never both dead.
Defects fixed along the way
MainContentCoordinator+SidebarActions.swiftclearActiveQueryResultsreturned before clearingexecution.errorMessage, which the other branch cleared. It now clears it.QueryResultPresentation,StatusBarSnapshotisQueryPlanflag the status model reads.QueryEditorView.swiftVerification
verify.sh build: PASSverify.sh test QueryResultPresentationTests ResultSetMenuModelTests ResultSetPolicyTests ResultStatusBarLayoutTests ResultPinningTests MainWindowToolbarValidationTests QueryCommandAvailabilityTests: PASS, 144 executed, 144 passedverify.sh lint TablePro: PASS, 0 violationsverify.sh docs: PASSNew suites:
QueryResultPresentationTests(17 cases over the state matrix, including one that walksevery reachable combination),
QueryCommandAvailabilityTests,ResultSetMenuModelTests.ResultTabBarPolicyTestsbecameResultSetPolicyTestsand now asks the resolver, so the pane andthe bar cannot disagree about whether there is a result to choose.
UI suites rewritten against the new controls:
QueryRunUITests(wasQueryExecuteMenuUITests),ResultSetPinUITests(wasResultTabPinUITests),ResultStatementLinkUITests,QueryPlanResultUITests. They were not executed locally; they run in CI.Also in the diff: a pre-existing
legacy_swiftui_aspect_ratioviolation inWelcomeOutlineRows.swift, unrelated to this work and already onmain. SwiftLint scans all ofTablePro/regardless of the path passed, so it made this branch's lint gate red. One line.Not done
No before and after screenshots.
osascriptdoes not have Accessibility permission on thismachine, so the app cannot be driven to a query tab with results, and capturing the installed 0.74.0
copy instead would have put real connections and data into this PR. The visual claims here are the
removed 32pt result strip, the removed "Query" heading, the rebuilt command bar and history in the
trailing pane. Screenshots for
docs/features/query-results.mdxanddocs/features/query-history.mdxare consequently still the old ones and need re-capturing; theprose on both pages is updated.