Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e9bd37a
feat(sidebar): add favorite tables
J2TeamNNL May 25, 2026
8189ee1
feat(sidebar): add recent tables, star toggle, create-table button, o…
J2TeamNNL May 25, 2026
91e4126
feat(sidebar): handle tableFavorite in conflict resolution, fix showE…
J2TeamNNL May 26, 2026
392c650
Update .gitignore
J2TeamNNL May 26, 2026
df7fbef
refactor(sidebar): remove ER diagram context menu item, drop SidebarT…
J2TeamNNL May 26, 2026
c5d72f6
fix(sidebar): address PR review blockers and design concerns
claude May 28, 2026
c3ab76c
fix(sidebar): address review — node-id schema, list selection, lock i…
J2TeamNNL May 29, 2026
ceb4edb
@salmonumbrella has signed the CLA in TableProApp/TablePro#1476
github-actions[bot] May 29, 2026
a75f17b
fix(sidebar): scope table favorites by database
datlechin May 29, 2026
7c522c3
fix(sidebar): show create-table button only on the Tables tab
datlechin May 29, 2026
93f3267
fix(launch): skip live iCloud sync under TABLEPRO_UI_TESTING
datlechin May 29, 2026
11e5772
refactor(sidebar): drop unused RecentTablesStore lastAccessedAt
datlechin May 29, 2026
b9764dd
fix(sync): propagate connection group membership changes to other dev…
datlechin May 29, 2026
975de19
Merge remote-tracking branch 'origin/main' into sidebar
datlechin May 29, 2026
089807f
fix(sidebar): use system colors in TableRowLogic to match color tests
datlechin May 29, 2026
0ad1148
Merge remote-tracking branch 'origin/main' into sidebar
datlechin May 29, 2026
d8adced
feat(connections): resolve connection passwords from file, env, or co…
datlechin May 29, 2026
94a0a9a
Merge branch 'main' into sidebar
datlechin May 29, 2026
84deed7
refactor(sidebar): move create-table action into the sidebar bottom bar
datlechin May 29, 2026
5bf506d
refactor(sidebar): reveal favorite star on hover and refine favorites…
datlechin May 29, 2026
c35f49f
fix(sidebar): drop hardcoded footer divider, use native bottom bar (s…
datlechin May 29, 2026
09608d2
fix(sidebar): footer inherits sidebar vibrancy instead of opaque mate…
datlechin May 29, 2026
0b945ba
fix(sidebar): use hard scroll-edge style so footer divider appears on…
datlechin May 29, 2026
532576c
refactor(sidebar): use a static footer divider, drop scroll-edge expe…
datlechin May 29, 2026
4b76452
refactor(sidebar): remove the Recent tables section
datlechin May 29, 2026
722cf59
fix(sidebar): drop duplicate context menu and add accessibility label…
datlechin May 29, 2026
6685712
refactor(sidebar): type-safe favorite selection and drop AnyView from…
datlechin May 29, 2026
5aa5cf2
fix(test): use explicit self for connectionStorage in GroupStorageTes…
datlechin May 29, 2026
5fc896c
test(sidebar): cover FavoriteSelection round-trip and scope table sel…
datlechin May 29, 2026
3f9ffba
feat(sidebar): restore recent tables section
J2TeamNNL May 29, 2026
908a15b
feat(settings): add toggle for the sidebar recent tables section
J2TeamNNL May 29, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ Thumbs.db
*.p12
*.mobileprovision
Secrets.xcconfig
Local.xcconfig

# Debug
*.log
Expand Down Expand Up @@ -154,3 +155,4 @@ fix-1322-plugin-abi-and-registry-overhaul.diff

# Issue analysis blueprints (local only)
.analysis/
.docs/
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Mark a table as a favorite by clicking the star button at the end of its sidebar row. Favorites are scoped to the connection, database, and schema, pinned to the top of their section, appear in a dedicated Tables group in the Favorites tab, and sync through iCloud when the Table Favorites toggle is on.
- A plus button in the bottom bar of the Tables sidebar opens a menu to create a new table or view, without right-clicking. It's disabled while safe mode blocks writes.
- Recent section at the top of the Tables sidebar tracks the last 10 tables you opened per connection and database, in-memory for the session. Off by default, turn it on in Settings > General > Sidebar. (#1352)
- A connection can read its password from a file, environment variable, or command at connect time instead of the Keychain, so scripts can provision a connection without entering the password by hand. (#1254)

### Changed

- The Maintenance submenu in the sidebar context menu is hidden when no maintenance operations are available or the target is read-only, instead of showing an empty disabled menu.
- The window minimum width now adjusts to the visible panes, so opening the inspector on a small window no longer pushes content off-screen.

### Removed

- "Create New Table…" from the sidebar right-click menu. Use the plus button in the Tables sidebar footer instead.

### Fixed

- Moving a connection into or out of a group now syncs across devices, instead of leaving it ungrouped on your other Macs.

## [0.46.0] - 2026-05-28

### Added
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ Missing a case produces a wrong "{Language} Query" title on the first frame.
| Tab state | JSON persistence | `TabPersistenceService` / `TabStateStorage` |
| Filter presets | UserDefaults | `FilterSettingsStorage` |
| Per-table filters | UserDefaults | `FilterSettingsStorage` (saves `appliedFilters` only) |
| Favorite tables | UserDefaults | `FavoriteTablesStorage` (global, by table name) |

### Logging & Debugging

Expand Down
118 changes: 118 additions & 0 deletions TablePro.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,13 @@
remoteGlobalIDString = 5A1091C62EF17EDC0055EA7C;
remoteInfo = TablePro;
};
5AF00A112FB9000000000001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5A1091C62EF17EDC0055EA7C;
remoteInfo = TablePro;
};
5ABQR00000000000000000C0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
Expand Down Expand Up @@ -297,6 +304,7 @@
5A87A000100000000 /* CassandraDriver.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CassandraDriver.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; };
5ABBED792FB55E1400A78382 /* CSVInspectorPlugin.tableplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CSVInspectorPlugin.tableplugin; sourceTree = BUILT_PRODUCTS_DIR; };
5ABCC5A72F43856700EAF3FC /* TableProTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableProTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5AF00A102FB9000000000001 /* TableProUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TableProUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5ABQR00200000000000000A1 /* BigQueryAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryAuth.swift; sourceTree = "<group>"; };
5ABQR00200000000000000A2 /* BigQueryConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryConnection.swift; sourceTree = "<group>"; };
5ABQR00200000000000000A3 /* BigQueryPlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BigQueryPlugin.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -677,6 +685,11 @@
path = TableProTests;
sourceTree = "<group>";
};
5AF00A122FB9000000000001 /* TableProUITests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = TableProUITests;
sourceTree = "<group>";
};
5AE4F4812F6BC0640097AC5B /* Plugins/CloudflareD1DriverPlugin */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
Expand Down Expand Up @@ -708,6 +721,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5AF00A132FB9000000000001 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5A3BE6F52F97DA8100611C1F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -939,6 +959,7 @@
5A86E000500000000 /* Plugins/MQLExportPlugin */,
5A86F000500000000 /* Plugins/SQLImportPlugin */,
5ABCC5A82F43856700EAF3FC /* TableProTests */,
5AF00A122FB9000000000001 /* TableProUITests */,
5A32BC012F9D5F1300BAEB5F /* mcp-server */,
5A1091C82EF17EDC0055EA7C /* Products */,
5A05FBC72F3EDF7500819CD7 /* Recovered References */,
Expand Down Expand Up @@ -968,6 +989,7 @@
5A86E000100000000 /* MQLExport.tableplugin */,
5A86F000100000000 /* SQLImport.tableplugin */,
5ABCC5A72F43856700EAF3FC /* TableProTests.xctest */,
5AF00A102FB9000000000001 /* TableProUITests.xctest */,
5AEA8B2A2F6808270040461A /* EtcdDriverPlugin.tableplugin */,
5ADDB00300000000000000A0 /* DynamoDBDriverPlugin.tableplugin */,
5ABQR00300000000000000A0 /* BigQueryDriverPlugin.tableplugin */,
Expand Down Expand Up @@ -1524,6 +1546,27 @@
productReference = 5ABCC5A72F43856700EAF3FC /* TableProTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
5AF00A142FB9000000000001 /* TableProUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */;
buildPhases = (
5AF00A152FB9000000000001 /* Sources */,
5AF00A132FB9000000000001 /* Frameworks */,
5AF00A162FB9000000000001 /* Resources */,
);
buildRules = (
);
dependencies = (
5AF00A172FB9000000000001 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
5AF00A122FB9000000000001 /* TableProUITests */,
);
name = TableProUITests;
productName = TableProUITests;
productReference = 5AF00A102FB9000000000001 /* TableProUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
5ABQR00600000000000000B0 /* BigQueryDriverPlugin */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5ABQR00800000000000000B0 /* Build configuration list for PBXNativeTarget "BigQueryDriverPlugin" */;
Expand Down Expand Up @@ -1671,6 +1714,10 @@
CreatedOnToolsVersion = 26.2;
TestTargetID = 5A1091C62EF17EDC0055EA7C;
};
5AF00A142FB9000000000001 = {
CreatedOnToolsVersion = 26.5;
TestTargetID = 5A1091C62EF17EDC0055EA7C;
};
5AE4F4732F6BC0640097AC5B = {
CreatedOnToolsVersion = 26.3;
LastSwiftMigration = 2630;
Expand Down Expand Up @@ -1725,6 +1772,7 @@
5A86E000000000000 /* MQLExport */,
5A86F000000000000 /* SQLImport */,
5ABCC5A62F43856700EAF3FC /* TableProTests */,
5AF00A142FB9000000000001 /* TableProUITests */,
5AEA8B292F6808270040461A /* EtcdDriverPlugin */,
5AE4F4732F6BC0640097AC5B /* CloudflareD1DriverPlugin */,
5ADDB00600000000000000B0 /* DynamoDBDriverPlugin */,
Expand All @@ -1744,6 +1792,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5AF00A162FB9000000000001 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5A3BE6F62F97DA8100611C1F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -1929,6 +1984,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5AF00A152FB9000000000001 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5A3BE6F42F97DA8100611C1F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
Expand Down Expand Up @@ -2212,6 +2274,11 @@
target = 5A1091C62EF17EDC0055EA7C /* TablePro */;
targetProxy = 5ABCC5AB2F43856700EAF3FC /* PBXContainerItemProxy */;
};
5AF00A172FB9000000000001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5A1091C62EF17EDC0055EA7C /* TablePro */;
targetProxy = 5AF00A112FB9000000000001 /* PBXContainerItemProxy */;
};
5ABQR00000000000000000C1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5ABQR00600000000000000B0 /* BigQueryDriverPlugin */;
Expand Down Expand Up @@ -3713,6 +3780,48 @@
};
name = Release;
};
5AF00A182FB9000000000001 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.9;
TEST_TARGET_NAME = TablePro;
};
name = Debug;
};
5AF00A1A2FB9000000000001 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 14.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.TablePro.TableProUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = NO;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.9;
TEST_TARGET_NAME = TablePro;
};
name = Release;
};
5ABQR00700000000000000B1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
Expand Down Expand Up @@ -4116,6 +4225,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5AF00A192FB9000000000001 /* Build configuration list for PBXNativeTarget "TableProUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5AF00A182FB9000000000001 /* Debug */,
5AF00A1A2FB9000000000001 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5ABQR00800000000000000B0 /* Build configuration list for PBXNativeTarget "BigQueryDriverPlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
Expand Down
11 changes: 11 additions & 0 deletions TablePro.xcodeproj/xcshareddata/xcschemes/TablePro.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@
ReferencedContainer = "container:TablePro.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5AF00A142FB9000000000001"
BuildableName = "TableProUITests.xctest"
BlueprintName = "TableProUITests"
ReferencedContainer = "container:TablePro.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
6 changes: 6 additions & 0 deletions TablePro/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {

private var hasRunPostLaunchActivation = false

private static var isUITesting: Bool {
ProcessInfo.processInfo.environment["TABLEPRO_UI_TESTING"] == "1"
}

// MARK: - URL & File Open

func applicationWillFinishLaunching(_ notification: Notification) {
Expand Down Expand Up @@ -93,12 +97,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {

func applicationDidBecomeActive(_ notification: Notification) {
runPostLaunchActivationIfNeeded()
guard !Self.isUITesting else { return }
SyncCoordinator.shared.syncIfNeeded()
}

private func runPostLaunchActivationIfNeeded() {
guard !hasRunPostLaunchActivation else { return }
hasRunPostLaunchActivation = true
guard !Self.isUITesting else { return }

ConnectionStorage.shared.migratePluginSecureFieldsIfNeeded()
AnalyticsService.shared.startPeriodicHeartbeat()
Expand Down
3 changes: 3 additions & 0 deletions TablePro/Core/Database/DatabaseDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,9 @@ enum DatabaseDriverFactory {
return try await resolveIAMPassword(for: connection, fields: fields)
}
if let override { return override }
if let passwordSource = connection.passwordSource {
return try await PasswordSourceResolver.resolve(passwordSource)
}
if connection.usePgpass {
let pgpassHost = connection.additionalFields["pgpassOriginalHost"] ?? connection.host
let pgpassPort = connection.additionalFields["pgpassOriginalPort"]
Expand Down
1 change: 1 addition & 0 deletions TablePro/Core/Database/DatabaseManager+Tunnel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ extension DatabaseManager {
type: connection.type,
sshConfig: SSHConfiguration(),
sslConfig: tunnelSSL,
passwordSource: connection.passwordSource,
additionalFields: effectiveFields
)
}
Expand Down
2 changes: 2 additions & 0 deletions TablePro/Core/Services/AppServices.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct AppServices {
let schemaService: SchemaService
let schemaProviderRegistry: SchemaProviderRegistry
let sqlFavoriteManager: SQLFavoriteManager
let favoriteTablesStorage: FavoriteTablesStorage
let aiChatStorage: AIChatStorage
let aiKeyStorage: AIKeyStorage
let groupStorage: GroupStorage
Expand Down Expand Up @@ -43,6 +44,7 @@ struct AppServices {
schemaService: .shared,
schemaProviderRegistry: .shared,
sqlFavoriteManager: .shared,
favoriteTablesStorage: .shared,
aiChatStorage: .shared,
aiKeyStorage: .shared,
groupStorage: .shared,
Expand Down
Loading
Loading