Skip to content

Commit c40fa8a

Browse files
committed
feat: extract MSSQL, MongoDB, Redis, XLSX, MQL, SQLImport plugins from app bundle
1 parent ddf43b8 commit c40fa8a

File tree

3 files changed

+24
-107
lines changed

3 files changed

+24
-107
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12+
- Extracted MSSQL, MongoDB, Redis, XLSX export, MQL export, and SQL import plugins from the app bundle into separately distributed plugins, downloadable from the plugin registry. MySQL, PostgreSQL, SQLite, CSV, JSON, and SQL export remain built-in
1213
- Redesigned Plugins settings tab with HSplitView master-detail layout: plugin list on the left, detail pane on the right, matching macOS conventions. Plugin rows now show version, author/capability, and install status at a glance
1314
- Download counts in browse tab now always fetch latest from GitHub API (5-minute in-memory cooldown per session)
1415
- Replaced ~40 hardcoded `DatabaseType` switches across ~20 UI files with dynamic plugin property lookups via `PluginManager`, so third-party plugins get correct UI behavior (colors, labels, editor language, feature toggles) automatically
@@ -25,9 +26,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2526
- Removed `DatabaseType` switches from `FilterSQLGenerator`, `SQLCompletionProvider`, `ImportDataSinkAdapter`, and `MainContentCoordinator+SidebarActions`
2627
- Replaced hardcoded `DatabaseType` switches in ExportDialog, DataChangeManager, SafeModeGuard, ExportService, DataGridView, HighlightedSQLTextView, ForeignKeyPopoverContentView, QueryTab, SQLRowToStatementConverter, SessionStateFactory, ConnectionToolbarState, and DatabaseSwitcherSheet with dynamic plugin lookups (`databaseGroupingStrategy`, `immutableColumns`, `supportsReadOnlyMode`, `paginationStyle`, `editorLanguage`, `connectionMode`, `supportsSchemaSwitching`)
2728
- Replaced remaining ~40 hardcoded `DatabaseType` switches in MainContentCoordinator (main + 5 extensions) and StructureRowProvider with plugin metadata lookups (`requiresReconnectForDatabaseSwitch`, `structureColumnFields`, `defaultPrimaryKeyColumn`, `supportsQueryProgress`, `autoLimitStyle`, `allTablesMetadataSQL`)
29+
- Moved editor and data grid font settings from Editor/Data Grid tabs into the theme system
30+
- Replaced fragmented Theme/DesignConstants/SQLEditorTheme/ToolbarDesignTokens with unified ThemeEngine
2831

2932
### Added
3033

34+
- Full theme engine with 9 built-in presets (Default Light/Dark, Dracula, Solarized Light/Dark, One Dark, GitHub Light/Dark, Nord) and custom theme support
35+
- Theme browser with visual preview cards in Settings > Appearance
36+
- Per-theme customization of all colors (editor syntax, data grid, UI, sidebar, toolbar) and fonts
37+
- Theme import/export as JSON files for sharing
3138
- SSH TOTP/two-factor authentication support (auto-generate and prompt modes)
3239
- SSH host key verification with fingerprint confirmation
3340
- Keyboard Interactive SSH authentication method

TablePro.xcodeproj/project.pbxproj

Lines changed: 14 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,22 @@
1212
5A862000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
1313
5A863000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
1414
5A864000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
15-
5A864000D00000000 /* MSSQLDriver.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A864000100000000 /* MSSQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1615
5A865000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
17-
5A865000D00000000 /* MySQLDriver.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A865000100000000 /* MySQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16+
5A865000D00000000 /* MySQLDriver.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A865000100000000 /* MySQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1817
5A866000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
19-
5A866000D00000000 /* MongoDBDriver.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A866000100000000 /* MongoDBDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2018
5A867000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
21-
5A867000D00000000 /* RedisDriver.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A867000100000000 /* RedisDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2219
5A868000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
23-
5A868000D00000000 /* PostgreSQLDriver.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A868000100000000 /* PostgreSQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
20+
5A868000D00000000 /* PostgreSQLDriver.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A868000100000000 /* PostgreSQLDriver.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2421
5A869000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
2522
5A86A000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
26-
5A86A000D00000000 /* CSVExport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86A000100000000 /* CSVExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
23+
5A86A000D00000000 /* CSVExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86A000100000000 /* CSVExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2724
5A86B000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
28-
5A86B000D00000000 /* JSONExport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86B000100000000 /* JSONExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
25+
5A86B000D00000000 /* JSONExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86B000100000000 /* JSONExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2926
5A86C000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
30-
5A86C000D00000000 /* SQLExport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86C000100000000 /* SQLExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27+
5A86C000D00000000 /* SQLExport.tableplugin in Copy Plug-Ins */ = {isa = PBXBuildFile; fileRef = 5A86C000100000000 /* SQLExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3128
5A86D000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
32-
5A86D000D00000000 /* XLSXExport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86D000100000000 /* XLSXExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3329
5A86E000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
34-
5A86E000D00000000 /* MQLExport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86E000100000000 /* MQLExport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3530
5A86F000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
36-
5A86F000D00000000 /* SQLImport.tableplugin in Copy Plug-Ins (12 items) */ = {isa = PBXBuildFile; fileRef = 5A86F000100000000 /* SQLImport.tableplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3731
5A87A000A00000000 /* TableProPluginKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A860000100000000 /* TableProPluginKit.framework */; };
3832
5ACE00012F4F000000000004 /* CodeEditSourceEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000002 /* CodeEditSourceEditor */; };
3933
5ACE00012F4F000000000005 /* CodeEditLanguages in Frameworks */ = {isa = PBXBuildFile; productRef = 5ACE00012F4F000000000003 /* CodeEditLanguages */; };
@@ -72,34 +66,13 @@
7266
remoteGlobalIDString = 5A863000000000000;
7367
remoteInfo = ClickHouseDriver;
7468
};
75-
5A864000B00000000 /* PBXContainerItemProxy */ = {
76-
isa = PBXContainerItemProxy;
77-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
78-
proxyType = 1;
79-
remoteGlobalIDString = 5A864000000000000;
80-
remoteInfo = MSSQLDriver;
81-
};
8269
5A865000B00000000 /* PBXContainerItemProxy */ = {
8370
isa = PBXContainerItemProxy;
8471
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
8572
proxyType = 1;
8673
remoteGlobalIDString = 5A865000000000000;
8774
remoteInfo = MySQLDriver;
8875
};
89-
5A866000B00000000 /* PBXContainerItemProxy */ = {
90-
isa = PBXContainerItemProxy;
91-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
92-
proxyType = 1;
93-
remoteGlobalIDString = 5A866000000000000;
94-
remoteInfo = MongoDBDriver;
95-
};
96-
5A867000B00000000 /* PBXContainerItemProxy */ = {
97-
isa = PBXContainerItemProxy;
98-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
99-
proxyType = 1;
100-
remoteGlobalIDString = 5A867000000000000;
101-
remoteInfo = RedisDriver;
102-
};
10376
5A868000B00000000 /* PBXContainerItemProxy */ = {
10477
isa = PBXContainerItemProxy;
10578
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
@@ -135,27 +108,6 @@
135108
remoteGlobalIDString = 5A86C000000000000;
136109
remoteInfo = SQLExport;
137110
};
138-
5A86D000B00000000 /* PBXContainerItemProxy */ = {
139-
isa = PBXContainerItemProxy;
140-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
141-
proxyType = 1;
142-
remoteGlobalIDString = 5A86D000000000000;
143-
remoteInfo = XLSXExport;
144-
};
145-
5A86E000B00000000 /* PBXContainerItemProxy */ = {
146-
isa = PBXContainerItemProxy;
147-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
148-
proxyType = 1;
149-
remoteGlobalIDString = 5A86E000000000000;
150-
remoteInfo = MQLExport;
151-
};
152-
5A86F000B00000000 /* PBXContainerItemProxy */ = {
153-
isa = PBXContainerItemProxy;
154-
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
155-
proxyType = 1;
156-
remoteGlobalIDString = 5A86F000000000000;
157-
remoteInfo = SQLImport;
158-
};
159111
5ABCC5AB2F43856700EAF3FC /* PBXContainerItemProxy */ = {
160112
isa = PBXContainerItemProxy;
161113
containerPortal = 5A1091BF2EF17EDC0055EA7C /* Project object */;
@@ -166,25 +118,19 @@
166118
/* End PBXContainerItemProxy section */
167119

168120
/* Begin PBXCopyFilesBuildPhase section */
169-
5A86FF0000000000 /* Copy Plug-Ins (12 items) */ = {
121+
5A86FF0000000000 /* Copy Plug-Ins */ = {
170122
isa = PBXCopyFilesBuildPhase;
171123
buildActionMask = 2147483647;
172124
dstPath = "";
173125
dstSubfolderSpec = 13;
174126
files = (
175-
5A864000D00000000 /* MSSQLDriver.tableplugin in Copy Plug-Ins (12 items) */,
176-
5A865000D00000000 /* MySQLDriver.tableplugin in Copy Plug-Ins (12 items) */,
177-
5A866000D00000000 /* MongoDBDriver.tableplugin in Copy Plug-Ins (12 items) */,
178-
5A867000D00000000 /* RedisDriver.tableplugin in Copy Plug-Ins (12 items) */,
179-
5A868000D00000000 /* PostgreSQLDriver.tableplugin in Copy Plug-Ins (12 items) */,
180-
5A86A000D00000000 /* CSVExport.tableplugin in Copy Plug-Ins (12 items) */,
181-
5A86B000D00000000 /* JSONExport.tableplugin in Copy Plug-Ins (12 items) */,
182-
5A86C000D00000000 /* SQLExport.tableplugin in Copy Plug-Ins (12 items) */,
183-
5A86D000D00000000 /* XLSXExport.tableplugin in Copy Plug-Ins (12 items) */,
184-
5A86E000D00000000 /* MQLExport.tableplugin in Copy Plug-Ins (12 items) */,
185-
5A86F000D00000000 /* SQLImport.tableplugin in Copy Plug-Ins (12 items) */,
186-
);
187-
name = "Copy Plug-Ins (12 items)";
127+
5A865000D00000000 /* MySQLDriver.tableplugin in Copy Plug-Ins */,
128+
5A868000D00000000 /* PostgreSQLDriver.tableplugin in Copy Plug-Ins */,
129+
5A86A000D00000000 /* CSVExport.tableplugin in Copy Plug-Ins */,
130+
5A86B000D00000000 /* JSONExport.tableplugin in Copy Plug-Ins */,
131+
5A86C000D00000000 /* SQLExport.tableplugin in Copy Plug-Ins */,
132+
);
133+
name = "Copy Plug-Ins";
188134
runOnlyForDeploymentPostprocessing = 0;
189135
};
190136
5A86FF0100000000 /* Embed Frameworks */ = {
@@ -745,7 +691,7 @@
745691
5A1091C42EF17EDC0055EA7C /* Frameworks */,
746692
5A1091C52EF17EDC0055EA7C /* Resources */,
747693
5A86FF0100000000 /* Embed Frameworks */,
748-
5A86FF0000000000 /* Copy Plug-Ins (12 items) */,
694+
5A86FF0000000000 /* Copy Plug-Ins */,
749695
);
750696
buildRules = (
751697
);
@@ -754,18 +700,12 @@
754700
5A861000C00000000 /* PBXTargetDependency */,
755701
5A862000C00000000 /* PBXTargetDependency */,
756702
5A863000C00000000 /* PBXTargetDependency */,
757-
5A864000C00000000 /* PBXTargetDependency */,
758703
5A865000C00000000 /* PBXTargetDependency */,
759-
5A866000C00000000 /* PBXTargetDependency */,
760-
5A867000C00000000 /* PBXTargetDependency */,
761704
5A868000C00000000 /* PBXTargetDependency */,
762705
5A869000C00000000 /* PBXTargetDependency */,
763706
5A86A000C00000000 /* PBXTargetDependency */,
764707
5A86B000C00000000 /* PBXTargetDependency */,
765708
5A86C000C00000000 /* PBXTargetDependency */,
766-
5A86D000C00000000 /* PBXTargetDependency */,
767-
5A86E000C00000000 /* PBXTargetDependency */,
768-
5A86F000C00000000 /* PBXTargetDependency */,
769709
);
770710
fileSystemSynchronizedGroups = (
771711
5A1091C92EF17EDC0055EA7C /* TablePro */,
@@ -1550,26 +1490,11 @@
15501490
target = 5A863000000000000 /* ClickHouseDriver */;
15511491
targetProxy = 5A863000B00000000 /* PBXContainerItemProxy */;
15521492
};
1553-
5A864000C00000000 /* PBXTargetDependency */ = {
1554-
isa = PBXTargetDependency;
1555-
target = 5A864000000000000 /* MSSQLDriver */;
1556-
targetProxy = 5A864000B00000000 /* PBXContainerItemProxy */;
1557-
};
15581493
5A865000C00000000 /* PBXTargetDependency */ = {
15591494
isa = PBXTargetDependency;
15601495
target = 5A865000000000000 /* MySQLDriver */;
15611496
targetProxy = 5A865000B00000000 /* PBXContainerItemProxy */;
15621497
};
1563-
5A866000C00000000 /* PBXTargetDependency */ = {
1564-
isa = PBXTargetDependency;
1565-
target = 5A866000000000000 /* MongoDBDriver */;
1566-
targetProxy = 5A866000B00000000 /* PBXContainerItemProxy */;
1567-
};
1568-
5A867000C00000000 /* PBXTargetDependency */ = {
1569-
isa = PBXTargetDependency;
1570-
target = 5A867000000000000 /* RedisDriver */;
1571-
targetProxy = 5A867000B00000000 /* PBXContainerItemProxy */;
1572-
};
15731498
5A868000C00000000 /* PBXTargetDependency */ = {
15741499
isa = PBXTargetDependency;
15751500
target = 5A868000000000000 /* PostgreSQLDriver */;
@@ -1595,21 +1520,6 @@
15951520
target = 5A86C000000000000 /* SQLExport */;
15961521
targetProxy = 5A86C000B00000000 /* PBXContainerItemProxy */;
15971522
};
1598-
5A86D000C00000000 /* PBXTargetDependency */ = {
1599-
isa = PBXTargetDependency;
1600-
target = 5A86D000000000000 /* XLSXExport */;
1601-
targetProxy = 5A86D000B00000000 /* PBXContainerItemProxy */;
1602-
};
1603-
5A86E000C00000000 /* PBXTargetDependency */ = {
1604-
isa = PBXTargetDependency;
1605-
target = 5A86E000000000000 /* MQLExport */;
1606-
targetProxy = 5A86E000B00000000 /* PBXContainerItemProxy */;
1607-
};
1608-
5A86F000C00000000 /* PBXTargetDependency */ = {
1609-
isa = PBXTargetDependency;
1610-
target = 5A86F000000000000 /* SQLImport */;
1611-
targetProxy = 5A86F000B00000000 /* PBXContainerItemProxy */;
1612-
};
16131523
5ABCC5AC2F43856700EAF3FC /* PBXTargetDependency */ = {
16141524
isa = PBXTargetDependency;
16151525
target = 5A1091C62EF17EDC0055EA7C /* TablePro */;

TablePro/Core/Plugins/PluginMetadataRegistry.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,15 @@ final class PluginMetadataRegistry: @unchecked Sendable {
8585
("MongoDB", PluginMetadataSnapshot(
8686
displayName: "MongoDB", iconName: "mongodb-icon", defaultPort: 27_017,
8787
requiresAuthentication: false, supportsForeignKeys: false, supportsSchemaEditing: false,
88-
isDownloadable: false, primaryUrlScheme: "mongodb", parameterStyle: .questionMark,
88+
isDownloadable: true, primaryUrlScheme: "mongodb", parameterStyle: .questionMark,
8989
navigationModel: .standard, explainVariants: [], pathFieldRole: .database,
9090
supportsHealthMonitor: true, urlSchemes: ["mongodb", "mongodb+srv"], postConnectActions: [],
9191
brandColorHex: "#00ED63"
9292
)),
9393
("Redis", PluginMetadataSnapshot(
9494
displayName: "Redis", iconName: "redis-icon", defaultPort: 6_379,
9595
requiresAuthentication: false, supportsForeignKeys: false, supportsSchemaEditing: false,
96-
isDownloadable: false, primaryUrlScheme: "redis", parameterStyle: .questionMark,
96+
isDownloadable: true, primaryUrlScheme: "redis", parameterStyle: .questionMark,
9797
navigationModel: .inPlace, explainVariants: [], pathFieldRole: .databaseIndex,
9898
supportsHealthMonitor: true, urlSchemes: ["redis", "rediss"],
9999
postConnectActions: [.selectDatabaseFromConnectionField(fieldId: "redisDatabase")],
@@ -102,7 +102,7 @@ final class PluginMetadataRegistry: @unchecked Sendable {
102102
("SQL Server", PluginMetadataSnapshot(
103103
displayName: "SQL Server", iconName: "mssql-icon", defaultPort: 1_433,
104104
requiresAuthentication: true, supportsForeignKeys: true, supportsSchemaEditing: true,
105-
isDownloadable: false, primaryUrlScheme: "sqlserver", parameterStyle: .questionMark,
105+
isDownloadable: true, primaryUrlScheme: "sqlserver", parameterStyle: .questionMark,
106106
navigationModel: .standard, explainVariants: [], pathFieldRole: .database,
107107
supportsHealthMonitor: true, urlSchemes: ["sqlserver", "mssql"],
108108
postConnectActions: [.selectDatabaseFromLastSession],

0 commit comments

Comments
 (0)