From 7946b6f5fa55e16acbb58917f79efa80c66214e0 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 30 Mar 2026 12:42:27 +0700 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20enable=20Open=20Database=20menu=20(?= =?UTF-8?q?=E2=8C=98K)=20for=20schema-switching=20databases=20like=20BigQu?= =?UTF-8?q?ery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TablePro/ContentView.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TablePro/ContentView.swift b/TablePro/ContentView.swift index 11d5425ba..750c6f80b 100644 --- a/TablePro/ContentView.swift +++ b/TablePro/ContentView.swift @@ -116,6 +116,7 @@ struct ContentView: View { AppState.shared.currentDatabaseType = session.connection.type AppState.shared.supportsDatabaseSwitching = PluginManager.shared.supportsDatabaseSwitching( for: session.connection.type) + || PluginManager.shared.supportsSchemaSwitching(for: session.connection.type) } } else { currentSession = nil @@ -156,6 +157,7 @@ struct ContentView: View { AppState.shared.currentDatabaseType = session.connection.type AppState.shared.supportsDatabaseSwitching = PluginManager.shared.supportsDatabaseSwitching( for: session.connection.type) + || PluginManager.shared.supportsSchemaSwitching(for: session.connection.type) } else { AppState.shared.isConnected = false AppState.shared.safeModeLevel = .silent @@ -408,6 +410,7 @@ struct ContentView: View { AppState.shared.currentDatabaseType = newSession.connection.type AppState.shared.supportsDatabaseSwitching = PluginManager.shared.supportsDatabaseSwitching( for: newSession.connection.type) + || PluginManager.shared.supportsSchemaSwitching(for: newSession.connection.type) } // MARK: - Actions From 007958f4d4bc4893ba7f36ef76de2800060aed8d Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 30 Mar 2026 12:54:34 +0700 Subject: [PATCH 2/4] fix: hide Databases/Schemas picker when only schema switching is supported (BigQuery) --- TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift index 24c0028b5..aafb43596 100644 --- a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift +++ b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift @@ -69,8 +69,10 @@ struct DatabaseSwitcherSheet: View { .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .semibold)) .padding(.vertical, 12) - // Databases / Schemas toggle (PostgreSQL only) - if PluginManager.shared.supportsSchemaSwitching(for: databaseType) { + // Databases / Schemas toggle — only show when both modes are available + if PluginManager.shared.supportsSchemaSwitching(for: databaseType) + && PluginManager.shared.supportsDatabaseSwitching(for: databaseType) + { Picker("", selection: $viewModel.mode) { Text(String(localized: "Databases")) .tag(DatabaseSwitcherViewModel.Mode.database) From 872477ba5bbb71d3b7a3ebfedec245dc2d0efb75 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 30 Mar 2026 13:07:44 +0700 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20dynamic=20container=20entity=20name?= =?UTF-8?q?=20=E2=80=94=20BigQuery=20shows=20Dataset,=20MySQL=20shows=20Da?= =?UTF-8?q?tabase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BigQueryDriverPlugin/BigQueryPlugin.swift | 1 + Plugins/TableProPluginKit/DriverPlugin.swift | 2 + Sequel-Ace | 1 + TablePro/Core/Plugins/PluginManager.swift | 5 + ...PluginMetadataRegistry+CloudDefaults.swift | 2 + ...ginMetadataRegistry+RegistryDefaults.swift | 10 ++ .../Core/Plugins/PluginMetadataRegistry.swift | 8 ++ TablePro/Resources/Localizable.xcstrings | 95 +++++++++++++++++++ TablePro/TableProApp.swift | 2 +- .../DatabaseSwitcherSheet.swift | 4 +- .../Main/Child/MainEditorContentView.swift | 2 +- .../MainContentCoordinator+Navigation.swift | 2 +- TablePro/Views/Sidebar/SidebarView.swift | 3 +- .../Views/Toolbar/TableProToolbarView.swift | 4 +- docs/newsletters/2026-03-29-v0.25-0.26.md | 60 ++++++++++++ licenseapp | 1 + 16 files changed, 193 insertions(+), 9 deletions(-) create mode 160000 Sequel-Ace create mode 100644 docs/newsletters/2026-03-29-v0.25-0.26.md create mode 160000 licenseapp diff --git a/Plugins/BigQueryDriverPlugin/BigQueryPlugin.swift b/Plugins/BigQueryDriverPlugin/BigQueryPlugin.swift index ded1d91f9..65d6a87c3 100644 --- a/Plugins/BigQueryDriverPlugin/BigQueryPlugin.swift +++ b/Plugins/BigQueryDriverPlugin/BigQueryPlugin.swift @@ -41,6 +41,7 @@ final class BigQueryPlugin: NSObject, TableProPlugin, DriverPlugin { static let supportsSSH = false static let supportsSSL = false static let tableEntityName = "Tables" + static let containerEntityName = "Dataset" static let supportsForeignKeyDisable = false static let supportsReadOnlyMode = true static let databaseGroupingStrategy: GroupingStrategy = .bySchema diff --git a/Plugins/TableProPluginKit/DriverPlugin.swift b/Plugins/TableProPluginKit/DriverPlugin.swift index 466fd3246..e84261650 100644 --- a/Plugins/TableProPluginKit/DriverPlugin.swift +++ b/Plugins/TableProPluginKit/DriverPlugin.swift @@ -37,6 +37,7 @@ public protocol DriverPlugin: TableProPlugin { static var sqlDialect: SQLDialectDescriptor? { get } static var statementCompletions: [CompletionEntry] { get } static var tableEntityName: String { get } + static var containerEntityName: String { get } static var supportsCascadeDrop: Bool { get } static var supportsForeignKeyDisable: Bool { get } static var immutableColumns: [String] { get } @@ -95,6 +96,7 @@ public extension DriverPlugin { static var sqlDialect: SQLDialectDescriptor? { nil } static var statementCompletions: [CompletionEntry] { [] } static var tableEntityName: String { "Tables" } + static var containerEntityName: String { "Database" } static var supportsCascadeDrop: Bool { false } static var supportsForeignKeyDisable: Bool { true } static var immutableColumns: [String] { [] } diff --git a/Sequel-Ace b/Sequel-Ace new file mode 160000 index 000000000..175ae4674 --- /dev/null +++ b/Sequel-Ace @@ -0,0 +1 @@ +Subproject commit 175ae46749d5c1d72b094e3b4d085a9b0dae15e2 diff --git a/TablePro/Core/Plugins/PluginManager.swift b/TablePro/Core/Plugins/PluginManager.swift index 569f2d66f..239233cc6 100644 --- a/TablePro/Core/Plugins/PluginManager.swift +++ b/TablePro/Core/Plugins/PluginManager.swift @@ -811,6 +811,11 @@ final class PluginManager { .schema.tableEntityName ?? "Tables" } + func containerEntityName(for databaseType: DatabaseType) -> String { + PluginMetadataRegistry.shared.snapshot(forTypeId: databaseType.pluginTypeId)? + .schema.containerEntityName ?? "Database" + } + func supportsCascadeDrop(for databaseType: DatabaseType) -> Bool { PluginMetadataRegistry.shared.snapshot(forTypeId: databaseType.pluginTypeId)? .capabilities.supportsCascadeDrop ?? false diff --git a/TablePro/Core/Plugins/PluginMetadataRegistry+CloudDefaults.swift b/TablePro/Core/Plugins/PluginMetadataRegistry+CloudDefaults.swift index 5a569b3bd..9d57a5de4 100644 --- a/TablePro/Core/Plugins/PluginMetadataRegistry+CloudDefaults.swift +++ b/TablePro/Core/Plugins/PluginMetadataRegistry+CloudDefaults.swift @@ -37,6 +37,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [], @@ -177,6 +178,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "", defaultGroupName: "default", tableEntityName: "Tables", + containerEntityName: "Dataset", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [], diff --git a/TablePro/Core/Plugins/PluginMetadataRegistry+RegistryDefaults.swift b/TablePro/Core/Plugins/PluginMetadataRegistry+RegistryDefaults.swift index 91ba91c42..7912dba62 100644 --- a/TablePro/Core/Plugins/PluginMetadataRegistry+RegistryDefaults.swift +++ b/TablePro/Core/Plugins/PluginMetadataRegistry+RegistryDefaults.swift @@ -533,6 +533,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Collections", + containerEntityName: "Database", defaultPrimaryKeyColumn: "_id", immutableColumns: ["_id"], systemDatabaseNames: ["admin", "local", "config"], @@ -604,6 +605,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "db0", tableEntityName: "Keys", + containerEntityName: "Database", defaultPrimaryKeyColumn: "Key", immutableColumns: [], systemDatabaseNames: [], @@ -644,6 +646,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "dbo", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["master", "tempdb", "model", "msdb"], @@ -691,6 +694,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [ @@ -747,6 +751,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["information_schema", "INFORMATION_SCHEMA", "system"], @@ -777,6 +782,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["information_schema", "pg_catalog"], @@ -819,6 +825,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "default", tableEntityName: "Tables", + containerEntityName: "Keyspace", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [ @@ -873,6 +880,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "default", tableEntityName: "Tables", + containerEntityName: "Keyspace", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [ @@ -926,6 +934,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Keys", + containerEntityName: "Database", defaultPrimaryKeyColumn: "Key", immutableColumns: ["Version", "ModRevision", "CreateRevision"], systemDatabaseNames: [], @@ -1008,6 +1017,7 @@ extension PluginMetadataRegistry { defaultSchemaName: "main", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [], diff --git a/TablePro/Core/Plugins/PluginMetadataRegistry.swift b/TablePro/Core/Plugins/PluginMetadataRegistry.swift index f8bc43ec0..63a68c673 100644 --- a/TablePro/Core/Plugins/PluginMetadataRegistry.swift +++ b/TablePro/Core/Plugins/PluginMetadataRegistry.swift @@ -69,6 +69,7 @@ struct PluginMetadataSnapshot: Sendable { let defaultSchemaName: String let defaultGroupName: String let tableEntityName: String + let containerEntityName: String let defaultPrimaryKeyColumn: String? let immutableColumns: [String] let systemDatabaseNames: [String] @@ -81,6 +82,7 @@ struct PluginMetadataSnapshot: Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [], @@ -348,6 +350,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["information_schema", "mysql", "performance_schema", "sys"], @@ -378,6 +381,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["information_schema", "mysql", "performance_schema", "sys"], @@ -419,6 +423,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["postgres", "template0", "template1"], @@ -462,6 +467,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: ["postgres", "template0", "template1"], @@ -505,6 +511,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: "public", defaultGroupName: "main", tableEntityName: "Tables", + containerEntityName: "Database", defaultPrimaryKeyColumn: nil, immutableColumns: [], systemDatabaseNames: [], @@ -664,6 +671,7 @@ final class PluginMetadataRegistry: @unchecked Sendable { defaultSchemaName: driverType.defaultSchemaName, defaultGroupName: driverType.defaultGroupName, tableEntityName: driverType.tableEntityName, + containerEntityName: driverType.containerEntityName, defaultPrimaryKeyColumn: driverType.defaultPrimaryKeyColumn, immutableColumns: driverType.immutableColumns, systemDatabaseNames: driverType.systemDatabaseNames, diff --git a/TablePro/Resources/Localizable.xcstrings b/TablePro/Resources/Localizable.xcstrings index e164e87ef..cd73fcf90 100644 --- a/TablePro/Resources/Localizable.xcstrings +++ b/TablePro/Resources/Localizable.xcstrings @@ -251,6 +251,7 @@ } }, "(%lld active)" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -3016,8 +3017,12 @@ } } } + }, + "Add Filter" : { + }, "Add Filter (Cmd+Shift+F)" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -3038,6 +3043,9 @@ } } } + }, + "Add filter row" : { + }, "Add Folder..." : { "localizations" : { @@ -4050,8 +4058,12 @@ } } } + }, + "Apply" : { + }, "Apply All" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -4094,8 +4106,12 @@ } } } + }, + "Apply filters" : { + }, "Apply this filter" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -4118,6 +4134,7 @@ } }, "Apply This Filter" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -5978,6 +5995,7 @@ } }, "Clear search" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -6000,6 +6018,7 @@ } }, "Clear Search" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -6333,6 +6352,9 @@ } } } + }, + "Close preview" : { + }, "Close Tab" : { "localizations" : { @@ -6491,6 +6513,7 @@ } }, "Column" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -7660,6 +7683,9 @@ } } } + }, + "Copied to clipboard" : { + }, "Copied!" : { "localizations" : { @@ -10791,6 +10817,7 @@ } }, "Duplicate filter" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13835,8 +13862,12 @@ } } } + }, + "Filter column" : { + }, "Filter column: %@" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13901,8 +13932,12 @@ } } } + }, + "Filter operator" : { + }, "Filter operator: %@" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13923,8 +13958,12 @@ } } } + }, + "Filter options" : { + }, "Filter presets" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13947,6 +13986,7 @@ } }, "Filter settings" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13969,6 +14009,7 @@ } }, "Filter Settings" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -13989,6 +14030,12 @@ } } } + }, + "Filter Settings..." : { + + }, + "Filter value" : { + }, "Filter with column" : { "localizations" : { @@ -14897,6 +14944,7 @@ } }, "History Limit:" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -21710,6 +21758,12 @@ } } } + }, + "Open File" : { + + }, + "Open File..." : { + }, "Open MQL Editor" : { "extractionState" : "stale", @@ -23482,6 +23536,9 @@ } } } + }, + "Preview Query" : { + }, "Preview Schema Changes" : { "localizations" : { @@ -24221,6 +24278,7 @@ } }, "Query History:" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -24287,6 +24345,7 @@ } }, "Quick search across all columns..." : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -25072,6 +25131,9 @@ } } } + }, + "Remove all filters and reload" : { + }, "Remove filter" : { "localizations" : { @@ -25116,6 +25178,9 @@ } } } + }, + "Remove filter row" : { + }, "Remove Folder" : { "localizations" : { @@ -26182,6 +26247,7 @@ } }, "Save and load filter presets" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -26224,6 +26290,9 @@ } } } + }, + "Save As" : { + }, "Save as Favorite" : { "localizations" : { @@ -26313,6 +26382,9 @@ } } } + }, + "Save As..." : { + }, "Save Changes" : { "localizations" : { @@ -26473,6 +26545,9 @@ } } } + }, + "Save SQL file" : { + }, "Save Table Template" : { "extractionState" : "stale", @@ -26895,6 +26970,9 @@ } } } + }, + "Second filter value" : { + }, "Second value is required for BETWEEN" : { "localizations" : { @@ -27213,8 +27291,12 @@ } } } + }, + "Select filter column" : { + }, "Select filter for %@" : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -27235,6 +27317,9 @@ } } } + }, + "Select filter operator" : { + }, "Select Plugin" : { "localizations" : { @@ -27280,6 +27365,9 @@ } } } + }, + "Select SQL files to open" : { + }, "Select Tab %lld" : { "localizations" : { @@ -30058,6 +30146,7 @@ } }, "Syncs connections, settings, and history across your Macs via iCloud." : { + "extractionState" : "stale", "localizations" : { "tr" : { "stringUnit" : { @@ -30078,6 +30167,9 @@ } } } + }, + "Syncs connections, settings, and SSH profiles across your Macs via iCloud." : { + }, "Syncs passwords via iCloud Keychain (end-to-end encrypted)." : { "localizations" : { @@ -34125,6 +34217,9 @@ } } } + }, + "WHERE clause" : { + }, "WHERE clause..." : { "localizations" : { diff --git a/TablePro/TableProApp.swift b/TablePro/TableProApp.swift index 3a7ba328d..641916292 100644 --- a/TablePro/TableProApp.swift +++ b/TablePro/TableProApp.swift @@ -176,7 +176,7 @@ struct AppMenuCommands: Commands { } .disabled(!appState.isConnected || appState.isReadOnly) - Button("Open Database...") { + Button("Open \(AppState.shared.currentDatabaseType.map { PluginManager.shared.containerEntityName(for: $0) } ?? "Database")...") { actions?.openDatabaseSwitcher() } .optionalKeyboardShortcut(shortcut(for: .openDatabase)) diff --git a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift index aafb43596..0eeff7e10 100644 --- a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift +++ b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift @@ -63,9 +63,7 @@ struct DatabaseSwitcherSheet: View { var body: some View { VStack(spacing: 0) { // Header - Text(isSchemaMode - ? String(localized: "Open Schema") - : String(localized: "Open Database")) + Text("Open \(PluginManager.shared.containerEntityName(for: databaseType))") .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .semibold)) .padding(.vertical, 12) diff --git a/TablePro/Views/Main/Child/MainEditorContentView.swift b/TablePro/Views/Main/Child/MainEditorContentView.swift index bf815934c..a7d96206c 100644 --- a/TablePro/Views/Main/Child/MainEditorContentView.swift +++ b/TablePro/Views/Main/Child/MainEditorContentView.swift @@ -600,7 +600,7 @@ struct MainEditorContentView: View { RoundedRectangle(cornerRadius: 4) .fill(Color(nsColor: .quaternaryLabelColor)) ) - Text("Switch Database") + Text("Switch \(PluginManager.shared.containerEntityName(for: connection.type))") .font(.callout) .foregroundStyle(.tertiary) } diff --git a/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift b/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift index ae8b7844e..6f3477b25 100644 --- a/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift +++ b/TablePro/Views/Main/Extensions/MainContentCoordinator+Navigation.swift @@ -411,7 +411,7 @@ extension MainContentCoordinator { navigationLogger.error("Failed to switch database: \(error.localizedDescription, privacy: .public)") AlertHelper.showErrorSheet( - title: String(localized: "Database Switch Failed"), + title: "\(PluginManager.shared.containerEntityName(for: connection.type)) Switch Failed", message: error.localizedDescription, window: NSApplication.shared.keyWindow ) diff --git a/TablePro/Views/Sidebar/SidebarView.swift b/TablePro/Views/Sidebar/SidebarView.swift index 1fe59a74d..dd05ef4d3 100644 --- a/TablePro/Views/Sidebar/SidebarView.swift +++ b/TablePro/Views/Sidebar/SidebarView.swift @@ -180,7 +180,8 @@ struct SidebarView: View { private var emptyState: some View { let entityName = PluginManager.shared.tableEntityName(for: viewModel.databaseType) let noItemsLabel = String(localized: "No \(entityName)") - let noItemsDetail = String(localized: "This database has no \(entityName.lowercased()) yet.") + let containerName = PluginManager.shared.containerEntityName(for: viewModel.databaseType).lowercased() + let noItemsDetail = "This \(containerName) has no \(entityName.lowercased()) yet." return VStack(spacing: 6) { Image(systemName: "tablecells") .font(.system(size: 28, weight: .thin)) diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index 8f9b90ba6..fa9dcf4f9 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -81,9 +81,9 @@ struct TableProToolbar: ViewModifier { Button { actions?.openDatabaseSwitcher() } label: { - Label("Database", systemImage: "cylinder") + Label(PluginManager.shared.containerEntityName(for: state.databaseType), systemImage: "cylinder") } - .help("Open Database (⌘K)") + .help("Open \(PluginManager.shared.containerEntityName(for: state.databaseType)) (⌘K)") .disabled( state.connectionState != .connected || PluginManager.shared.connectionMode(for: state.databaseType) == .fileBased) diff --git a/docs/newsletters/2026-03-29-v0.25-0.26.md b/docs/newsletters/2026-03-29-v0.25-0.26.md new file mode 100644 index 000000000..17581f2aa --- /dev/null +++ b/docs/newsletters/2026-03-29-v0.25-0.26.md @@ -0,0 +1,60 @@ +--- +subject: "SQL files, new filters, connection sharing, BigQuery - TablePro v0.25-0.26" +--- + +Two releases this week. Here's what's new. + +--- + +### SQL File Management + +Open, edit, and save .sql files directly. The file name shows in the title bar - same as any native Mac app. Cmd+S to save, Cmd+Shift+S to save as. Drag-and-drop works too. + +*[Screenshot: editor with a .sql file open, file name visible in the title bar]* + +--- + +### New Filter System + +Rebuilt from scratch with native macOS controls. 18 operators (contains, between, regex, IS NULL, etc.), raw SQL mode for custom WHERE clauses, and saved presets so you don't retype the same filters. + +Cmd+F to open. Apply with Enter. + +*[Screenshot: filter panel with 2-3 rows, one raw SQL and one column/operator/value]* + +--- + +### Connection Sharing + +Export connections as .tablepro files. Share them with your team - import preview shows duplicates and lets you pick what to keep. + +**Encrypted export (Pro):** Include passwords, protected by AES-256-GCM with a passphrase. + +**Linked Folders (Pro):** Point a folder at a shared drive. Connections inside appear read-only in the sidebar, always up to date. + +**Environment variables (Pro):** Use `$DB_HOST` or `${DB_PASSWORD}` in connection fields. Resolved at connect time. + +*[Screenshot: import preview dialog showing a list of connections with status badges]* + +--- + +### BigQuery Support + +Google BigQuery via the plugin registry. Connect with a service account JSON key or Application Default Credentials. Browse datasets, run SQL, export results. + +*[Screenshot: BigQuery query result with dataset sidebar]* + +--- + +### More in these releases + +- **Nested groups** - organize connections up to 3 levels deep +- **Column reorder** - drag columns in the Structure tab (MySQL/MariaDB) +- **AI kill switch** - one toggle to turn off all AI features +- **JSON viewer** - JSON fields in Row Details now display in a scrollable monospaced area +- **Safety dialogs** - confirmation prompts for deep links, imports, and startup scripts +- **Bug fixes** - MariaDB JSON hex dumps, MongoDB Atlas TLS, SSH profile sync, editor focus/scrolling, and more + +--- + +Update from the app (Sparkle auto-update) or download at [tablepro.app](https://tablepro.app). diff --git a/licenseapp b/licenseapp new file mode 160000 index 000000000..cb56b087b --- /dev/null +++ b/licenseapp @@ -0,0 +1 @@ +Subproject commit cb56b087bc540e689c6033f150cca0d2d10ec379 From a40ab5a5509e05a7a270f788146e4282792f5e9b Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Mon, 30 Mar 2026 13:08:14 +0700 Subject: [PATCH 4/4] chore: remove accidentally committed embedded repos --- Sequel-Ace | 1 - licenseapp | 1 - 2 files changed, 2 deletions(-) delete mode 160000 Sequel-Ace delete mode 160000 licenseapp diff --git a/Sequel-Ace b/Sequel-Ace deleted file mode 160000 index 175ae4674..000000000 --- a/Sequel-Ace +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 175ae46749d5c1d72b094e3b4d085a9b0dae15e2 diff --git a/licenseapp b/licenseapp deleted file mode 160000 index cb56b087b..000000000 --- a/licenseapp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cb56b087bc540e689c6033f150cca0d2d10ec379