Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Toolbar customizations now persist after closing and reopening a session window. (#1455)
- Pasting copied rows no longer misplaces values when a cell contains a comma (such as a user agent string); each value stays in its own column, and a real NULL is kept distinct from the literal text "NULL".
- BigQuery: switching to another table now loads its data right away, instead of leaving the grid empty until you close and reopen the tab.
- Custom and OpenAI-compatible AI providers now work when the base URL already ends in `/v1`, instead of building a doubled `/v1/v1/` path that failed. (#1400)
Expand Down
8 changes: 4 additions & 4 deletions TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import TableProPluginKit
internal final class MainWindowToolbar: NSObject, NSToolbarDelegate {
private static let lifecycleLogger = Logger(subsystem: "com.TablePro", category: "NativeTabLifecycle")

internal static let toolbarIdentifier = NSToolbar.Identifier("com.TablePro.main.toolbar")

weak var coordinator: MainContentCoordinator?

internal let managedToolbar: NSToolbar
Expand All @@ -26,14 +28,12 @@ internal final class MainWindowToolbar: NSObject, NSToolbarDelegate {

internal init(coordinator: MainContentCoordinator) {
self.coordinator = coordinator
// Unique identifier per toolbar instance prevents tab-group merging that would collapse
// all tabs into one toolbar and leave subsequent windows blank.
self.managedToolbar = NSToolbar(identifier: "com.TablePro.main.toolbar.\(UUID().uuidString)")
self.managedToolbar = NSToolbar(identifier: Self.toolbarIdentifier)
super.init()
self.managedToolbar.delegate = self
self.managedToolbar.displayMode = .iconOnly
self.managedToolbar.allowsUserCustomization = true
self.managedToolbar.autosavesConfiguration = false
self.managedToolbar.autosavesConfiguration = true
}

func invalidate() {
Expand Down
35 changes: 35 additions & 0 deletions TableProTests/Services/MainWindowToolbarValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,39 @@ struct MainWindowToolbarValidationTests {
let unknown = NSToolbarItem.Identifier("com.test.unknown")
#expect(MainWindowToolbar.isEnabled(itemIdentifier: unknown, context: context) == true)
}

@Test("Toolbar identifier is stable across instances so AppKit autosave can persist customizations")
func toolbarIdentifierIsStable() {
#expect(MainWindowToolbar.toolbarIdentifier.rawValue == "com.TablePro.main.toolbar")
}

@Test("Toolbar is configured for user customization and autosave")
func toolbarConfigurationEnablesAutosave() {
let coordinator = makeCoordinator()
defer { coordinator.teardown() }
let owner = MainWindowToolbar(coordinator: coordinator)
#expect(owner.managedToolbar.identifier == MainWindowToolbar.toolbarIdentifier)
#expect(owner.managedToolbar.allowsUserCustomization == true)
#expect(owner.managedToolbar.autosavesConfiguration == true)
}

@Test("Allowed item identifiers are a superset of defaults so restored items survive autosave")
func allowedItemIdentifiersAreSupersetOfDefaults() {
let coordinator = makeCoordinator()
defer { coordinator.teardown() }
let owner = MainWindowToolbar(coordinator: coordinator)
let toolbar = owner.managedToolbar
let defaults = Set(owner.toolbarDefaultItemIdentifiers(toolbar))
let allowed = Set(owner.toolbarAllowedItemIdentifiers(toolbar))
#expect(defaults.isSubset(of: allowed))
}

private func makeCoordinator() -> MainContentCoordinator {
MainContentCoordinator(
connection: TestFixtures.makeConnection(database: "db_a"),
tabManager: QueryTabManager(),
changeManager: DataChangeManager(),
toolbarState: ConnectionToolbarState()
)
}
}
Loading