Skip to content

Commit effcaf0

Browse files
committed
fix: eliminate new tab flicker, fix tab ordering, add unified toolbar style
1 parent 4b64cd5 commit effcaf0

9 files changed

Lines changed: 63 additions & 8 deletions

File tree

TablePro/AppDelegate+WindowConfig.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ extension AppDelegate {
277277
$0 !== window && isMainWindow($0) && $0.isVisible
278278
&& $0.tabbingIdentifier == resolvedIdentifier
279279
}) {
280-
existingWindow.addTabbedWindow(window, ordered: .above)
280+
let targetWindow = existingWindow.tabbedWindows?.last ?? existingWindow
281+
targetWindow.addTabbedWindow(window, ordered: .above)
281282
window.makeKeyAndOrderFront(nil)
282283
}
283284
}

TablePro/ContentView.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,24 @@ struct ContentView: View {
4848
defaultTitle = "SQL Query"
4949
}
5050
_windowTitle = State(initialValue: defaultTitle)
51+
52+
// For Cmd+T (new tab), the session already exists. Resolve synchronously
53+
// to avoid the "Connecting..." flash while waiting for async onChange.
54+
var resolvedSession: ConnectionSession?
55+
if let connectionId = payload?.connectionId {
56+
resolvedSession = DatabaseManager.shared.activeSessions[connectionId]
57+
}
58+
_currentSession = State(initialValue: resolvedSession)
59+
60+
if let session = resolvedSession {
61+
_rightPanelState = State(initialValue: RightPanelState())
62+
_sessionState = State(initialValue: SessionStateFactory.create(
63+
connection: session.connection, payload: payload
64+
))
65+
} else {
66+
_rightPanelState = State(initialValue: nil)
67+
_sessionState = State(initialValue: nil)
68+
}
5169
}
5270

5371
var body: some View {

TablePro/Core/Services/Infrastructure/SessionStateFactory.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ enum SessionStateFactory {
4848
toolbarSt.databaseName = String(dbIndex)
4949
}
5050

51-
// Initialize single tab based on payload
51+
// Initialize single tab based on payload.
52+
// For isConnectionOnly (Cmd+T new tab), create a default query tab eagerly
53+
// so MainContentView doesn't flash "No tabs open" before initializeAndRestoreTabs runs.
5254
if let payload, !payload.isConnectionOnly {
5355
switch payload.tabType {
5456
case .table:
@@ -87,6 +89,8 @@ enum SessionStateFactory {
8789
sourceFileURL: payload.sourceFileURL
8890
)
8991
}
92+
} else if payload?.isNewTab == true {
93+
tabMgr.addTab(databaseName: payload?.databaseName ?? connection.database)
9094
}
9195

9296
let coord = MainContentCoordinator(

TablePro/Models/Query/EditorTabPayload.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ internal struct EditorTabPayload: Codable, Hashable {
3535
internal let initialFilterState: TabFilterState?
3636
/// Source file URL for .sql files opened from disk (used for deduplication)
3737
internal let sourceFileURL: URL?
38+
/// Whether this is a Cmd+T new tab (creates default tab eagerly, skips disk restoration)
39+
internal let isNewTab: Bool
3840

3941
internal init(
4042
id: UUID = UUID(),
@@ -48,7 +50,8 @@ internal struct EditorTabPayload: Codable, Hashable {
4850
skipAutoExecute: Bool = false,
4951
isPreview: Bool = false,
5052
initialFilterState: TabFilterState? = nil,
51-
sourceFileURL: URL? = nil
53+
sourceFileURL: URL? = nil,
54+
isNewTab: Bool = false
5255
) {
5356
self.id = id
5457
self.connectionId = connectionId
@@ -62,6 +65,7 @@ internal struct EditorTabPayload: Codable, Hashable {
6265
self.isPreview = isPreview
6366
self.initialFilterState = initialFilterState
6467
self.sourceFileURL = sourceFileURL
68+
self.isNewTab = isNewTab
6569
}
6670

6771
internal init(from decoder: Decoder) throws {
@@ -78,6 +82,7 @@ internal struct EditorTabPayload: Codable, Hashable {
7882
isPreview = try container.decodeIfPresent(Bool.self, forKey: .isPreview) ?? false
7983
initialFilterState = try container.decodeIfPresent(TabFilterState.self, forKey: .initialFilterState)
8084
sourceFileURL = try container.decodeIfPresent(URL.self, forKey: .sourceFileURL)
85+
isNewTab = try container.decodeIfPresent(Bool.self, forKey: .isNewTab) ?? false
8186
}
8287

8388
/// Whether this payload is a "connection-only" payload — just a connectionId
@@ -101,5 +106,6 @@ internal struct EditorTabPayload: Codable, Hashable {
101106
self.isPreview = false
102107
self.initialFilterState = nil
103108
self.sourceFileURL = tab.sourceFileURL
109+
self.isNewTab = false
104110
}
105111
}

TablePro/Resources/Localizable.xcstrings

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11305,6 +11305,9 @@
1130511305
}
1130611306
}
1130711307
}
11308+
},
11309+
"Enable AI Features" : {
11310+
1130811311
},
1130911312
"Enable inline suggestions" : {
1131011313
"localizations" : {
@@ -18097,6 +18100,9 @@
1809718100
}
1809818101
}
1809918102
}
18103+
},
18104+
"Location" : {
18105+
1810018106
},
1810118107
"Maintenance" : {
1810218108
"localizations" : {
@@ -18296,6 +18302,9 @@
1829618302
}
1829718303
}
1829818304
}
18305+
},
18306+
"Max Bytes Billed" : {
18307+
1829918308
},
1830018309
"Max schema tables: %lld" : {
1830118310
"localizations" : {
@@ -21411,6 +21420,12 @@
2141121420
}
2141221421
}
2141321422
}
21423+
},
21424+
"OAuth Client ID" : {
21425+
21426+
},
21427+
"OAuth Client Secret" : {
21428+
2141421429
},
2141521430
"Offset" : {
2141621431
"localizations" : {
@@ -23887,6 +23902,9 @@
2388723902
}
2388823903
}
2388923904
}
23905+
},
23906+
"Project ID" : {
23907+
2389023908
},
2389123909
"Prompt at Connect" : {
2389223910
"localizations" : {
@@ -27409,6 +27427,9 @@
2740927427
}
2741027428
}
2741127429
}
27430+
},
27431+
"Service Account Key" : {
27432+
2741227433
},
2741327434
"Service Name" : {
2741427435
"extractionState" : "stale",

TablePro/TableProApp.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ struct TableProApp: App {
468468
.background(OpenWindowHandler())
469469
}
470470
.windowStyle(.automatic)
471+
.windowToolbarStyle(.unified)
471472
.defaultSize(width: 1_200, height: 800)
472473

473474
// Settings Window - opens with Cmd+,

TablePro/Views/Main/MainContentCommandActions.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,8 @@ final class MainContentCommandActions {
295295
let payload = EditorTabPayload(
296296
connectionId: connection.id,
297297
tabType: .query,
298-
initialQuery: initialQuery
298+
initialQuery: initialQuery,
299+
isNewTab: true
299300
)
300301
WindowOpener.shared.openNativeTab(payload)
301302
}

TablePro/Views/Main/MainContentView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,9 @@ struct MainContentView: View {
497497
// If other windows already exist for this connection, this is a "new tab"
498498
// from the native macOS "+" button -- just add a single empty query tab.
499499
if WindowLifecycleMonitor.shared.hasOtherWindows(for: connection.id, excluding: windowId) {
500-
tabManager.addTab(databaseName: connection.database)
500+
if tabManager.tabs.isEmpty {
501+
tabManager.addTab(databaseName: connection.database)
502+
}
501503
return
502504
}
503505

TableProTests/Views/Main/SessionStateFactoryTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ struct SessionStateFactoryTests {
122122
#expect(state.tabManager.tabs.isEmpty)
123123
}
124124

125-
@Test("Connection-only payload creates empty tab manager")
125+
@Test("Connection-only payload creates a default query tab eagerly")
126126
@MainActor
127-
func connectionOnlyPayload_createsEmptyTabManager() {
127+
func connectionOnlyPayload_createsDefaultTab() {
128128
let conn = TestFixtures.makeConnection()
129129
// isConnectionOnly is true when tabType == .query, tableName == nil, initialQuery == nil
130130
let payload = makePayload(connectionId: conn.id, tabType: .query)
131131

132132
let state = SessionStateFactory.create(connection: conn, payload: payload)
133133

134-
#expect(state.tabManager.tabs.isEmpty)
134+
#expect(state.tabManager.tabs.count == 1)
135+
#expect(state.tabManager.tabs.first?.tabType == .query)
135136
}
136137

137138
@Test("Factory is idempotent: two calls produce fresh but equivalent instances")

0 commit comments

Comments
 (0)