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 11d5425b..750c6f80 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 24c0028b..aafb4359 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 ded1d91f..65d6a87c 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 466fd324..e8426165 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 00000000..175ae467 --- /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 569f2d66..239233cc 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 5a569b3b..9d57a5de 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 91ba91c4..7912dba6 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 f8bc43ec..63a68c67 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 e164e87e..cd73fcf9 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 3a7ba328..64191629 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 aafb4359..0eeff7e1 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 bf815934..a7d96206 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 ae8b7844..6f3477b2 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 1fe59a74..dd05ef4d 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 8f9b90ba..fa9dcf4f 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 00000000..17581f2a --- /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 00000000..cb56b087 --- /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 175ae467..00000000 --- a/Sequel-Ace +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 175ae46749d5c1d72b094e3b4d085a9b0dae15e2 diff --git a/licenseapp b/licenseapp deleted file mode 160000 index cb56b087..00000000 --- a/licenseapp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cb56b087bc540e689c6033f150cca0d2d10ec379