You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When adding a new driver: create a new plugin bundle under `Plugins/`, implement `DriverPlugin` + `PluginDatabaseDriver`, add target to pbxproj. See `docs/development/plugin-system/` for details.
94
+
When adding a new driver: create a new plugin bundle under `Plugins/`, implement `DriverPlugin` + `PluginDatabaseDriver`, add target to pbxproj, add `DatabaseType` static constant, add case to `resolve_plugin_info()` in `.github/workflows/build-plugin.yml`, add row to `docs/index.mdx` supported databases table, and add CHANGELOG entry. See `docs/development/plugin-system/` for details.
95
95
96
96
When adding a new method to the driver protocol: add to `PluginDatabaseDriver` (with default implementation), then update `PluginDriverAdapter` to bridge it to `DatabaseDriver`.
97
97
98
-
**PluginKit ABI versioning**: When `DriverPlugin` or `PluginDatabaseDriver` protocol changes (new methods, changed signatures), bump `currentPluginKitVersion` in `PluginManager.swift` AND `TableProPluginKitVersion` in every plugin's `Info.plist`. Stale user-installed plugins with mismatched versions crash on load with `EXC_BAD_INSTRUCTION` (not catchable in Swift).
98
+
**PluginKit ABI versioning**: When `DriverPlugin` or `PluginDatabaseDriver` protocol changes (new methods, changed signatures), bump `currentPluginKitVersion` in `PluginManager.swift` AND `TableProPluginKitVersion` in every plugin's `Info.plist`. Stale user-installed plugins with mismatched versions crash on load with `EXC_BAD_INSTRUCTION` (not catchable in Swift). Removing protocol methods that have default `nil` implementations does NOT require a version bump — old plugins have dead code, new plugins fall back to defaults.
99
99
100
100
### DatabaseType (String-Based Struct)
101
101
@@ -124,6 +124,8 @@ When adding a new method to the driver protocol: add to `PluginDatabaseDriver` (
124
124
125
125
`MainContentCoordinator` is the central coordinator, split across 7+ extension files in `Views/Main/Extensions/` (e.g., `+Alerts`, `+Filtering`, `+Pagination`, `+RowOperations`). When adding coordinator functionality, add a new extension file rather than growing the main file.
126
126
127
+
**Tab replacement guard**: `openTableTab` checks for active work (unsaved edits, applied filters, sorting) before replacing the current tab. Tabs with active work open a new native window tab instead. This check runs before the preview tab branch.
128
+
127
129
### Source Organization
128
130
129
131
`Core/Services/` is split into domain subdirectories:
@@ -150,7 +152,8 @@ When adding a new method to the driver protocol: add to `PluginDatabaseDriver` (
150
152
| User preferences | UserDefaults |`AppSettingsStorage` / `AppSettingsManager`|
151
153
| Query history | SQLite FTS5 |`QueryHistoryStorage`|
152
154
| Tab state | JSON persistence |`TabPersistenceService` / `TabStateStorage`|
@@ -198,14 +201,14 @@ These are **non-negotiable** — never skip them:
198
201
199
202
1. **CHANGELOG.md**: Update under `[Unreleased]`section (Added/Fixed/Changed) for new features and notable changes. But do **not** add a "Fixed" entry for fixing something that is itself still unreleased — if a feature under `[Unreleased]`has a bug, just fix it without adding another CHANGELOG entry. "Fixed" entries are only for bugs in already-released features. Documentation-only changes (`docs/`) do **not** need a CHANGELOG entry.
200
203
201
-
2. **Localization**: Use `String(localized:)`for new user-facing strings in computed properties, AppKit code, alerts, and error descriptions. SwiftUI view literals (`Text("literal")`, `Button("literal")`) auto-localize. Do NOT localize technical terms (font names, database types, SQL keywords, encoding names).
204
+
2. **Localization**: Use `String(localized:)`for new user-facing strings in computed properties, AppKit code, alerts, and error descriptions. SwiftUI view literals (`Text("literal")`, `Button("literal")`) auto-localize. Do NOT localize technical terms (font names, database types, SQL keywords, encoding names).Never use `String(localized:)`with string interpolation — `String(localized: "Preview \(name)")`creates a dynamic key that never matches the strings catalog. Use static keys or `String(format: String(localized: "Preview %@"), name)`.
202
205
203
206
3. **Documentation**: Update docs in `docs/` (Mintlify-based) when adding/changing features. Key mappings:
204
207
- New keyboard shortcuts → `docs/features/keyboard-shortcuts.mdx`
- Update both English (`docs/`) and Vietnamese (`docs/vi/`) pages
211
+
- Update English docs in `docs/` (no Vietnamese `docs/vi/` directory currently exists)
209
212
210
213
4. **Test-first correctness**: When tests fail, fix the **source code** — never adjust tests to match incorrect output. Tests define expected behavior.
211
214
@@ -229,6 +232,7 @@ These are **non-negotiable** — never skip them:
229
232
230
233
These have caused real production bugs — be aware when working in editor/autocomplete/persistence code:
231
234
235
+
- **Never use `ForEach($bindable.array) { $item in }`** on `@Observable` arrays that can be cleared externally — index-based bindings crash with out-of-bounds when the array shrinks during SwiftUI evaluation. Use `ForEach(array) { item in` with a manual `Binding` via `binding(for: item)` instead.
232
236
- **Never use `string.count`** on large strings — O(n) in Swift. Use `(string as NSString).length` for O(1).
233
237
- **Never use `string.index(string.startIndex, offsetBy:)` in loops** on bridged NSStrings — O(n) per call. Use `(string as NSString).character(at:)` for O(1) random access.
234
238
- **Never call `ensureLayout(forCharacterRange:)`** — defeats `allowsNonContiguousLayout`. Let layout manager queries trigger lazy local layout.
0 commit comments