All notable changes to TablePro will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.18.1 - 2026-03-14
- Plugin download counts now accumulate across all versions instead of only counting the current release
0.18.0 - 2026-03-14
- Theme engine: 4 built-in themes (Default Light/Dark, Dracula, Nord), custom themes with full color/font/layout customization, import/export as JSON
- Theme registry: browse, install, and update community themes from the plugin registry
- App-level appearance mode: Light, Dark, or Auto (follow system), independent of theme
- Cassandra and ScyllaDB database support (downloadable plugin)
- SSH TOTP/two-factor authentication with auto-generate and prompt modes
- SSH host key verification with fingerprint confirmation
- Keyboard Interactive SSH authentication
- Column visibility: toggle columns on/off via status bar or header context menu
- Copy as INSERT/UPDATE SQL from data grid context menu
~/.pgpasssupport for PostgreSQL/Redshift connections- Pre-connect script: run a shell command before each connection
- MSSQL query cancellation and lock timeout support
- Custom plugin registry URL for enterprise/private registries
- Extracted MSSQL, MongoDB, Redis, XLSX export, MQL export, and SQL import into downloadable plugins. MySQL, PostgreSQL, SQLite, CSV, JSON, and SQL export remain built-in
- Redesigned Plugins settings with master-detail layout and download counts
- All database-specific behavior now driven by plugin metadata instead of hardcoded switches, enabling third-party database plugins
- Connection form fields, sidebar labels, and SQL dialect features are now fully plugin-driven
- Plugin icon rendering now supports custom asset images alongside SF Symbols
0.17.0 - 2026-03-11
- DuckDB database support — connect to
.duckdbfiles, query CSV/Parquet/JSON files via SQL, schema navigation, and DuckDB extension management - MongoDB configurable auth database (
authSource) — authenticate against any database instead of hardcodedadmin
-
MongoDB Read Preference, Write Concern, and Redis Database not persisted across app restarts
-
Result truncation at 100K rows now reported to UI via
PluginQueryResult.isTruncatedinstead of being silently discarded -
DELETE and UPDATE queries using all columns in WHERE clause instead of just the primary key for PostgreSQL, Redshift, MSSQL, and ClickHouse
-
SSL/TLS always being enabled for MongoDB, Redis, and ClickHouse connections due to case mismatch in SSL mode string comparison (#249)
-
Redis sidebar click showing data briefly then going empty due to double-navigation race condition (#251)
-
MongoDB showing "Invalid database name: ''" when connecting without a database name
- Namespaced
disabledPluginsUserDefaults key tocom.TablePro.disabledPluginswith automatic migration - Removed unused plugin capability types (sqlDialect, aiProvider, cellRenderer, sidebarPanel)
- SQLite driver extracted from built-in bundle to downloadable plugin, reducing app size
- Unified error formatting across all database drivers via default
PluginDriverError.errorDescription, removing 10 per-driver implementations - Standardized async bridging: 5 queue-based drivers (MySQL, PostgreSQL, MongoDB, Redis, MSSQL) now use shared
pluginDispatchAsynchelper - Added localization to remaining driver error messages (MySQL, PostgreSQL, ClickHouse, Oracle, Redis, MongoDB)
- NoSQL query building moved from Core to MongoDB/Redis plugins via optional
PluginDatabaseDriverprotocol methods - Standardized parameter binding across all database drivers with improved default escaping (type-aware numeric handling, NUL byte stripping, NULL literal support)
- Open SQLite database files directly from Finder by double-clicking
.sqlite,.sqlite3,.db3,.s3db,.sl3, and.sqlitedbfiles (#262) - Export plugin options (CSV, XLSX, JSON, SQL, MQL) now persist across app restarts
- Plugins can declare settings views rendered in Settings > Plugins
- True prepared statements for MSSQL (
sp_executesql) and ClickHouse (HTTP query parameters), eliminating string interpolation for parameterized queries - Batch query operations for MSSQL, Oracle, and ClickHouse, eliminating N+1 query patterns for column, foreign key, and database metadata fetching; SQLite adds a batched
fetchAllForeignKeysoverride within PRAGMA limitations PluginDriverErrorprotocol in TableProPluginKit for structured error reporting from driver plugins, with richer connection error messages showing error codes and SQL statespluginDispatchAsyncconcurrency helper in TableProPluginKit for standardized async bridging in plugins- Shared
PluginRowLimitsconstant in TableProPluginKit with 100K row default, enforced across all 8 driver plugins (ClickHouse, MSSQL, Oracle previously had no cap) driverVariant(for:)method onDriverPluginprotocol for dynamic multi-type plugin dispatch, replacing hardcoded variant mapping- Safe mode levels: per-connection setting with 6 levels (Silent, Alert, Alert Full, Safe Mode, Safe Mode Full, Read-Only) replacing the boolean read-only toggle, with confirmation dialogs and Touch ID/password authentication for stricter levels
- Preview tabs: single-click opens a temporary preview tab, double-click or editing promotes it to a permanent tab
- Import plugin system: SQL import extracted into a
.tablepluginbundle, matching the export plugin architecture ImportFormatPluginprotocol in TableProPluginKit for building custom import format plugins- SQLImportPlugin as the first import format plugin (SQL files and .gz compressed SQL)
- Oracle and ClickHouse shipped as downloadable plugins, reducing app bundle size for most users
- Plugin install prompt when connecting to a database whose driver plugin is not installed
databaseTypeIdsfield on registry plugins for mapping registry entries to database typesbuild-plugin.shscript andbuild-plugin.ymlCI workflow for building standalone plugin releases
0.16.1 - 2026-03-09
- Stale filter causing repeated errors when restoring tabs after schema/database switch (#237)
- Sidebar showing old tables during database/schema switch instead of loading state
- Sidebar search field disappearing when no tables match filter on macOS 15 and earlier (#235)
- Disabled plugin database types still appearing in connection form picker
- Main window not closing before reopening welcome screen on connection failure
0.16.0 - 2026-03-09
- Inspector separator no longer bleeds into toolbar area with default connection color (#228)
- Inspector toggle no longer lags due to synchronous UserDefaults writes during animation (#229)
- Direct
.tablepluginbundle installation via file picker, Finder double-click, and drag-and-drop - Plugin capability enforcement — registration now gated on declared capabilities, with validation warnings for mismatches
- Plugin dependency declarations — plugins can declare required dependencies via
TableProPlugin.dependencies, validated at load time - Plugin state change notification (
pluginStateDidChange) posted when plugins are enabled/disabled - Restart recommendation banner in Settings > Plugins after uninstalling a plugin
- Startup commands — run custom SQL after connecting (e.g., SET time_zone) in Connection > Advanced tab
- Plugin system architecture — all 8 database drivers (MySQL, PostgreSQL, SQLite, ClickHouse, MSSQL, MongoDB, Redis, Oracle) extracted into
.tablepluginbundles loaded at runtime - Export format plugins — all 5 export formats (CSV, JSON, SQL, XLSX, MQL) extracted into
.tablepluginbundles with plugin-provided option views and per-table option columns - Settings > Plugins tab for plugin management — list installed plugins, enable/disable, install from file, uninstall user plugins, view plugin details
- Plugin marketplace — browse, search, and install plugins from the GitHub-hosted registry with SHA-256 checksum verification, ETag caching, and offline fallback
- TableProPluginKit framework — shared protocols and types for driver and export plugins
- ClickHouse database support with query progress tracking, EXPLAIN variants, TLS/HTTPS, server-side cancellation, and Parts view
- Reduce memory: eliminate dedicated ping driver (~30-50 MB per connection), use main driver for health checks
- Reduce memory: evict inactive native window-tab row data after 5s, re-fetch on focus
- Reduce memory: lazy-load plugin bundles on first use instead of at startup (~20-30 MB saved)
- Reduce memory: remove duplicate sourceQuery string from RowBuffer
- Reduce memory: InMemoryRowProvider references RowBuffer directly instead of copying rows (~3-10 MB per tab)
- Reduce memory: eliminate metadata driver entirely, multiplex all queries on main driver (~30-50 MB per connection)
- Reduce memory: lazy AIChatViewModel initialization (deferred until AI panel is first opened)
- Reduce memory: remove duplicate connections array from ContentView (use ConnectionStorage.shared directly)
- Reduce CPU: consolidate per-editor NSEvent monitors into shared EditorEventRouter singleton (O(n) → O(1) per event)
- Fix tab persistence: aggregate tabs from all windows at quit time instead of last-write-wins per-coordinator save
- Split DatabaseManager.sessionVersion into fine-grained connectionListVersion and connectionStatusVersion to reduce cascade re-renders
- Extract AppState property reads into local lets in view bodies for explicit granular observation tracking
- Reorganized project directory structure: Services, Utilities, Models split into domain-specific subdirectories
- Database driver code moved from monolithic app binary into independent plugin bundles under
Plugins/
0.15.0 - 2026-03-08
- Oracle Database support via OCI (Oracle Call Interface)
- Add database URL scheme support — open connections directly from terminal with
open "mysql://user@host/db" -a TablePro(supports MySQL, PostgreSQL, SQLite, MongoDB, Redis, MSSQL, Oracle) - SSH Agent authentication method for SSH tunnels (compatible with 1Password SSH Agent, Secretive, ssh-agent)
- Multi-jump SSH support — chain multiple SSH hops (ProxyJump) to reach databases through bastion hosts
- Replace CodeEditLanguages xcframework (38 grammars) with local package compiling only SQL, Bash, and JavaScript, reducing app binary size by ~55%
- Fix memory leak where session state objects were recreated on every tab open due to SwiftUI
@Stateinit trap, causing 785MB usage at 5 tabs with 734MB retained after closing - Fix per-cell field editor allocation in DataGrid creating 180+ NSTextView instances instead of sharing one
- Fix NSEvent monitor not removed on all popover dismissal paths in connection switcher
- Fix race condition in FreeTDS
disconnect()wheredbprocwas set to nil without holding the lock - Fix data race in
MainContentCoordinator.deinitreadingnonisolated(unsafe)flags from arbitrary threads - Fix JSON encoding and file I/O blocking the main thread in TabStateStorage
- Fix MySQL/MariaDB getting
BEGINinstead ofSTART TRANSACTIONin table operations and SQL preview - Fix port resetting to default value when editing a connection with a custom port
- Replace
.onTapGesturewithButtonin color pickers, section headers, group headers, and connection switcher for VoiceOver accessibility - Fix data race on
isAppTerminatingstatic var inMainContentCoordinatorusingOSAllocatedUnfairLock - Fix
MainActor.assumeIsolatedcrash risk inVimKeyInterceptornotification observer - Fix data race on
connpointer inLibPQConnectionduring disconnect and cancel - Fix SSH askpass script written with world-readable permissions; now uses atomic
0o700creation and immediate cleanup - Fix potential dict mutation during iteration in
DatabaseManager.disconnectAll() - Fix welcome screen showing blank panel when connections have orphaned group IDs
- Fix multiple tabs auto-executing queries simultaneously on connection restore, causing lag
- Fix welcome window becoming oversized after closing main windows due to AppKit scene restoration
- Fix unescaped identifiers in MySQL
SHOW CREATE TABLE/VIEWqueries allowing SQL injection via table names - Fix
QueryResultRowequality ignoring cell values, preventing SwiftUI from re-rendering updated rows - Fix status bar row info text rendering off-center due to duplicate spacer
- Fix
Cmd+Deletein sidebar search or right sidebar clearing the query editor - Fix SSH tunnel processes not terminated when closing connection window or quitting the app
0.14.1 - 2026-03-06
- Add database and schema switching for PostgreSQL connections via ⌘K
0.14.0 - 2026-03-05
- Microsoft SQL Server (MSSQL) database support via FreeTDS
- Support for editing and deleting rows in tables without a primary key
- Fix MSSQL connection losing selected database after disconnect and reconnect when no default database is configured
- DELETE operations on tables without a primary key now show an error if row data is missing instead of being silently dropped
- SQLite and MSSQL now use safe single-row limits for DELETE and UPDATE on tables without a primary key
- Fix high CPU/RAM on app launch from blocking storage init, unsynchronized health monitors, and excessive retry loops
- Fix O(n log n) row cache eviction in RowProvider by replacing sorted eviction with O(n) distance-threshold filter
- Fix O(n) string operations in GeometryWKBParser, RedisDriver, and autocomplete scoring by switching to NSString O(1) indexing
- Fix slow database switcher loading by replacing N+1 metadata queries with single batched queries (MySQL, PostgreSQL, Redshift)
- Fix slow Redis key browsing by pipelining TYPE and TTL commands in a single round trip instead of 3 sequential commands per key
- Fix slow SQL export startup by batching COUNT(*) queries via UNION ALL and batching dependent sequence/type lookups
- Fix slow AI Chat schema loading by fetching all foreign keys in a single bulk query instead of per-table
0.13.0 - 2026-03-04
- Redis database support with key-value browsing, database-level sidebar (db0–db15), TTL management, and interactive CLI
- TablePlus-compatible database URL handling:
open -a TablePro "postgresql://user@host/db"with support for schema switching, table opening, filters, color, and environment tags
- Fix sidebar search field and main content area background colors to blend with macOS vibrancy
- Fix POINT and geometry columns showing blank values in MySQL and wrong type label in sidebar
0.12.0 - 2026-03-03
- Amazon Redshift database support
- Deep link support via
tablepro://URL scheme for opening connections, tables, queries, and importing connections - "Copy as URL" context menu action on connections to copy connection details as a URL string (e.g.,
mysql://user:pass@host/db) - Auto-show inspector option: automatically open the right sidebar when selecting a row (Settings > Data Grid)
- ENUM and SET columns now open their picker on single click with a chevron indicator, matching boolean column behavior
- Homebrew Cask installation via
brew install --cask tablepro
- "Table not found" error when switching databases within the same connection (Cmd+K) while a table tab is open
- Right sidebar state now persists across native window-tabs instead of resetting to closed
0.11.1 - 2026-03-02
- MySQL second tab showing empty rows due to premature coordinator teardown during native macOS tab group merging
- MongoDB tab name showing "MQL Query" instead of collection name when using bracket notation
db["collection"].find()
0.11.0 - 2026-03-02
- Environment color indicator: subtle toolbar tint based on connection color for at-a-glance environment identification
- Import database connections from SSH tunnel URLs (e.g.,
mysql+ssh://,postgresql+ssh://) - Connection groups for organizing database connections into folders with colored headers
- Toolbar briefly showing "MySQL" and missing version (e.g., "MongoDB" instead of "MongoDB 8.2.5") when opening a new tab
- Keyboard shortcuts not working (beep sound) after connecting from welcome screen until a second tab is opened
- Toolbar overflow menu showing only one item and missing all other buttons when window is narrow
- AI chat showing "SQL" language label and missing syntax highlighting for MongoDB code blocks
- Refactored toolbar to use individual
ToolbarItementries withLabelfor native macOS overflow behavior, and moved History/Export/Import to.secondaryActionoverflow menu - Redesigned right sidebar detail pane with compact field layout and type-aware editors
0.10.0 - 2026-03-01
- Support for multiple independent database connections in separate windows with per-window session isolation
- MongoDB database support
- Custom About window with version info and links (Website, GitHub, Documentation)
- Import database connections from URL/connection string (e.g.,
postgresql://user:pass@host:5432/db) - Release notes in Sparkle update window
- New row (Cmd+I) and duplicated row not appearing in datagrid until manual refresh
- PostgreSQL SSH tunnel connections failing with "no encryption" due to SSL config not being preserved
- PostgreSQL SSL
sslrootcertpassed unconditionally to libpq, causing certificate verification failure even inRequiredmode
0.9.2 - 2026-02-28
- Fix app bundle not ad-hoc signed — signing step was unreachable when no dylibs were bundled
0.9.1 - 2026-02-28
- Fix Sparkle auto-update failing with "improperly signed" error — release ZIPs now preserve framework symlinks and include proper ad-hoc code signatures
0.9.0 - 2026-02-28
- Vim keybindings for SQL editor (Normal/Insert/Visual modes, motions, operators, :w/:q commands) with toggle in Editor Settings
^and_motions (first non-blank character) in Vim normal, visual, and operator-pending modes:qcommand to close current tab in Vim command-line mode- PostgreSQL schema switching via ⌘K database switcher (browse and switch between schemas like
public,auth, custom schemas)
- Convert QueryHistoryStorage and QueryHistoryManager from callback-based async dispatch to native Swift async/await — eliminates double thread hops per history operation
- Consolidate ExportService @Published properties into single state struct — reduces objectWillChange events from 7 per batch to 1
- Consolidate ImportService @Published properties into single state struct — reduces objectWillChange events during SQL import
- Replace DispatchQueue.main.asyncAfter chains in AppDelegate startup with structured Task-based retry loops
- Merge 3 identical Combine notification subscriptions in SidebarViewModel into Publishers.Merge3
- Make AIChatStorage encoder/decoder static — shared across all instances instead of duplicated
- Cell edit showing modified background but displaying original value until save (reloadData during active editing ignored by NSTableView, updateNSView blocked by editedRow guard)
- Undo on inserted row cell edit not syncing insertedRowData (stale values after undo)
- Vim Escape key not exiting Insert/Visual mode when autocomplete popup is visible (popup's event monitor consumed the key)
- Copy (Cmd+C) and Cut (Cmd+X) not working in SQL editor — clipboard retained old value due to CodeEditTextView's copy: silently failing
- Vim yank/delete operations not syncing to system clipboard (register only stored text internally)
- Vim word motions (
w,b,e) using two-class word boundary detection instead of correct three-class (word chars, punctuation, whitespace) - Vim visual mode selection now correctly includes cursor character (inclusive selection matching real Vim behavior)
- Arrow keys now work in Vim visual/normal mode (mapped to h/j/k/l instead of bypassing the Vim engine)
- Vim block cursor now follows the moving end of the selection in visual mode instead of staying at the anchor
- Vim visual mode selection highlight now renders visibly (trigger needsDisplay after programmatic selection)
- Fix event monitor leaks in SQL editor —
deinitnow cleans up NSEvent monitors, notification observers, and work items that leaked when CodeEditSourceEditor never calleddestroy() - Fix unbounded memory growth from NativeTabRegistry holding full QueryTab objects (including RowBuffer references) — registry now stores lightweight TabSnapshot structs
- Fix SortedRowsCache storing full row copies — now stores index permutations only, halving sorted-tab memory
- Fix schema provider memory leak — shared providers are now reference-counted with 5s grace period removal when all windows for a connection close
- Fix duplicate schema fetches in InlineSuggestionManager — now shares the coordinator's SQLSchemaProvider instead of maintaining a separate cache
- Fix background tabs retaining full result data indefinitely — RowBuffer eviction frees memory for inactive tabs (re-fetched on switch back)
- Fix InMemoryRowProvider bulk cache eviction — now uses proximity-based eviction keeping entries near current scroll position
- Fix stale tabRowProviders entries when tab IDs change without count changing
- Fix crash on macOS 14.x caused by
_strchrnulsymbol not found in libpq.5.dylib — switch libpq and OpenSSL from dynamic Homebrew linking to vendored static libraries built with MACOSX_DEPLOYMENT_TARGET=14.0 - Fix duplicate tabs and lag when inserting SQL from AI Chat or History panel with multiple window-tabs open — notification handlers now only fire in the key window
- Fix "Run in New Tab" race condition in History panel — replaced fragile two-notification + 100ms delay pattern with a single atomic notification
- Fix MainContentCoordinator deinit Task that may never execute — added explicit teardown() method with didTeardown guard and orphaned schema provider purge
- Fix SQLEditorCoordinator deinit deferring InlineSuggestionManager cleanup to Task — added explicit destroy() lifecycle and didDestroy guard with warning log
- Fix ExportService while-true batch loops not checking Task.isCancelled — cancelled exports now stop promptly instead of running all remaining batches
- Fix DataGridView full column reconfiguration on every resultVersion bump — narrowed rebuild condition to only trigger when transitioning from empty state
- Fix ConnectionHealthMonitor fixed 30s interval that delays failure detection — added checkNow() with wakeUpContinuation for immediate health checks and exponential backoff
- Fix HistoryPanelView and TableStructureView asyncAfter copy-reset timers not cancellable — replaced with cancellable Task pattern
- Fix MainContentView redundant onChange handler causing cascading re-renders on tab/table changes
- Fix DatabaseManager notification observer creating unnecessary Tasks when self is already deallocated — added guard let self before Task creation
0.8.0 - 2026-02-27
- Refactored sidebar table list to MVVM architecture with testable SidebarViewModel
- Extracted TableRow and context menu into separate files (TableRowView.swift, SidebarContextMenu.swift)
- Migrated to native macOS window tabs (
NSWindowtabbing) — tab bar is now rendered by macOS itself, identical to Finder/Safari/Xcode tabs with automatic dark/light mode support, drag-to-reorder, and "Merge All Windows" for free - Each tab is a full independent window with its own sidebar, editor, and state — no more shared tab manager or ZStack keep-alive pattern
- New Tab (Cmd+T) creates a native macOS window tab; Close Tab (Cmd+W) closes the native tab
- Tab switching (Cmd+Shift+[/], Cmd+1-9) now uses native macOS tab navigation
- Sidebar table selection is per-window-tab (independent of other tabs)
- Tab persistence now saves/restores combined state from all native window tabs via NativeTabRegistry; restored tabs reopen as individual native window tabs
- Sidebar table click navigates in-place when no unsaved changes; opens new native tab when dirty
- FK navigation follows the same in-place/new-tab behavior based on unsaved changes
- "Show All Tables" now opens metadata query in a new native tab instead of appending to the current window
- Create Table success closes the create-table window and opens the new table in a fresh native tab
- Window title updates dynamically after navigate-in-place (sidebar click, FK navigation)
- Sidebar loses keyboard focus (arrow key navigation) after opening a second table tab
- Sidebar active state flash and loss when clicking a table that opens in a new native window tab — removed the async revert; each window now re-syncs its sidebar via
NSWindow.didBecomeKeyNotification, and programmatic syncs skip navigation via an early-return guard - Sidebar loses active state when opening a second table in a new native window tab —
handleTabSelectionChangenow callssyncSidebarToCurrentTab()so the new window's emptylocalSelectedTablesis seeded from the restored tab - Sidebar now refreshes immediately after switching databases via Cmd+K — clears
session.tablesduring the switch soSidebarView.onChangetriggersloadTables()against the new database without requiring a manual refresh - Cmd+W in empty state (after all tabs are cleared) now closes the connection window and disconnects, instead of doing nothing
- Fix Cmd+K database switch flooding all windows with error alerts —
.refreshAllbroadcast caused every window to re-execute its table query against the wrong database; now only the current tab re-executes, and only if its table exists in the new database - Fix clicking a table in the sidebar replacing the current tab instead of opening a new native tab
- Fix clicking a table from a query tab overwriting the SQL editor instead of opening a separate table tab
- Tab persistence no longer overwrites combined state from all windows when a single window saves — uses NativeTabRegistry for combined state
- Query text editing in one window no longer corrupts other windows' persisted tab state
- Fix Cmd+W on any tab disconnecting the session and showing welcome screen — now only disconnects when the last main window is closed
- Fix Cmd+T from empty state creating two native tabs instead of one — now adds a query tab to the current window
- Fix clicking a table in the sidebar from empty state not opening the table — now creates a table tab in the current window
- Fix native tab title showing "SQL Query" instead of the table name when opening a table from empty state
- Fix Cmd+W on the last tab disconnecting the session instead of returning to empty state
- Removed broken SidebarFocusRestorer (non-functional NSViewRepresentable focus hack)
- Removed dead code: unused onTablePro callback, single-table toggle methods
- Custom AppKit tab bar (NativeTabBarView) — replaced by native macOS window tab bar
- Removed vestigial multi-tab code:
performDirectTabSwitch,skipNextTabChangeOnChange,tabPendingChanges,tabSelectionCache,lastFlushTime,filterStateSavedExternally,flushSelectionCache,duplicateTab,togglePin,selectTab,switchToDatabase(legacy)
- Cache SQLSchemaProvider per connection so new native tabs reuse the already-loaded schema instead of re-fetching tables and columns from the database (saves 500ms-2s per tab)
- Schema loading now runs in background, no longer blocks the data query from starting — table data appears immediately while autocomplete schema loads concurrently
- Remove unconditional 100ms sleep in
waitForConnectionAndExecutewhen connection is already established - Defer
loadTableMetadataIfNeededuntil after the tab's first query completes, avoiding a redundant DB round-trip during tab initialization - Replace
@ObservedObject dbManagerin ContentView with targeted@State+onReceive— eliminates O(N) view cascade where every window re-rendered on any DatabaseManager state change - Remove
@StateObject dbManagerfrom TableProApp — prevents app-level body re-evaluation on every DatabaseManager publish - Batch
connectToSessionsession mutations into a singleactiveSessionswrite — reduces 5 separateobjectWillChangepublishes to 1 - Remove redundant
DatabaseManager.updateSessioncalls in tab change handlers — NativeTabRegistry already handles persistence, eliminating unnecessary@Publishedcascades - Add initialization guard to
initializeAndRestoreTabspreventing duplicate query execution from racing.taskandonChange(of: selectedTabId)paths - Replace
onChange(of: DatabaseManager.shared.currentSession?.*)with per-windowonReceivefiltered by connection ID — stops SwiftUI from tracking the global DatabaseManager singleton as a dependency - Guard health monitor status writes to skip no-op
.connected→.connectedtransitions — eliminates idle 30-second cascade on all windows - Extract all menu commands into
AppMenuCommandsstruct —AppStatechanges now only re-evaluate menu items, not the Scene body / all WindowGroups - Add
isContentViewEquivalent(to:)comparison onConnectionSession— skips@Statewrites when onlytabs,selectedTabId, orlastActiveAtchanged, preventing O(N) MainContentView.init cascade across windows
0.7.0 - 2026-02-25
- Quick search and filter rows can now be combined — when both are active, their WHERE conditions are joined with AND
- Foreign key columns now show a navigation arrow icon in each cell — click to open the referenced table filtered by the FK value
- Metadata queries (columns, FKs, row count) now run on a dedicated parallel connection, eliminating 200-300ms delay for FK arrows and pagination count on initial table load
- Approximate row count from database metadata displays instantly with data; exact count refines silently in the background
- Show warning indicator on filter presets referencing columns not in current table
- Increase filter row height estimate for better accessibility support
- FK navigation now uses dedicated FilterStateManager.setFKFilter API instead of direct property manipulation
- Add syntax highlighting to Import SQL file preview
- XLSX export now enforces the Excel row limit (1,048,576) per sheet and uses autoreleasepool per row to reduce peak memory during large exports
- Multiline cell values now use a scrollable overlay editor instead of the constrained field editor, enabling proper vertical scrolling and line navigation during inline editing
- AnyChangeManager now uses a reference-type box for lazy initialization, avoiding Combine pipeline creation during SwiftUI body evaluation
- DataGridView identity check moved before AppSettingsManager read to skip settings access when nothing has changed
- DataGridView async column width write-back now uses an isWritingColumnLayout guard to prevent two-frame bounce
- Tab switch flushPendingSave debounced to skip redundant saves within 100ms of rapid tab switching
- SQL editor frame-change notification throttled to 50ms to avoid redundant syntax highlight viewport recalculation on every keystroke
- SQL editor text binding sync now uses O(1) NSString length pre-check before O(n) full string equality comparison
- Toolbar executing state now fires a single objectWillChange instead of double-publishing isExecuting and connectionState
- Row provider onChange handlers coalesced into a single trigger to avoid redundant InMemoryRowProvider rebuilds
- SQL import now uses file-size estimation instead of a separate counting pass, eliminating the double-parse overhead for large files
- History cleanup COUNT + DELETE now wrapped in a single transaction to reduce journal flushes
- SQLite
fetchTableMetadatanow caps row count scan at 100k rows to avoid full table scans on large tables - SQLite
fetchIndexesuses table-valued pragma functions in a single query instead of N+1 separate PRAGMA calls - MySQL empty-result DESCRIBE fallback now only triggers for SELECT queries, avoiding redundant round-trips for non-SELECT statements
- Remove redundant
String(query)copy in MariaDB query execution - MySQL result fetching now uses
mysql_use_result(streaming) instead ofmysql_store_result(full buffering), so only the capped row count is held in memory instead of the entire server result set - Instant pagination via approximate row count — MySQL/PostgreSQL tables now show "~N rows" immediately with data, then refine to exact count in background
- QueryTab uses value-based equality for SwiftUI diffing, eliminating unnecessary ForEach re-renders on tab array writes
- Cached static regex for
extractTableName,SQLiteDriver.stripLimitOffset, and SQL function expressions to avoid per-call compilation - Static NumberFormatter in status bar to avoid per-render locale resolution
- Batch
TableProTabSmartfield writes into single array store to avoid 14 CoW copies per query execution - Tab persistence writes moved off main thread via
Task.detached - Single history entry per SQL import instead of per-statement recording
- WAL mode enabled for query history SQLite database
- Merged
fetchDatabaseMetadatainto single query for MySQL and PostgreSQL - Health ping now uses dedicated metadata driver to avoid blocking user queries
- SSH tunnel setup extracted into shared helper to eliminate code duplication
- PostgreSQL DDL queries restructured with
async letfor cleaner dispatch (sequential on serial connection queue) - Cancel query connection now uses 5-second connect timeout
- PostgreSQL connection parameters properly escaped for special characters
- SQLite
fetchAllColumnsoverridden with singlesqlite_master+pragma_table_infoquery - Eliminated intermediate
[UInt8]buffer in MySQL and PostgreSQL field extraction - Column layout sync gated behind user-resize flag to skip O(n) loop on cursor moves
- Column width calculation uses monospace character arithmetic instead of per-row CoreText calls
- DataChangeManager maintains change index incrementally instead of full O(n) rebuild
- JSON export buffers writes per row instead of per field
SQLFormatterServiceuses NSMutableString for keyword uppercasing and integer counter for placeholders- SQLContextAnalyzer uses single alternation regex and single-pass state machine for string/comment detection
escapeJSONStringiterates UTF-8 bytes instead of grapheme clustersAppSettingsStoragecaches JSONDecoder/JSONEncoder as stored propertiesAppSettingsManagerstores validated settings in memory after didSetFilterSettingsStorageuses tracked key set instead of loading full plist- Keychain saves use
SecItemAdd+SecItemUpdateupsert pattern instead of delete + add - Autocomplete
detectFunctionContextuses index tracking instead of character-by-character string building
- Fix AND/OR filter logic mode ignored in query execution — preview showed correct OR logic but actual query always used AND
- Fix filter panel state (filters, visibility, quick search, logic mode) not preserved when switching between tabs
- Fix foreign key navigation filter being wiped when switching to a new tab (tab switch restore overwrote FK filter state)
- Fix pagination count appearing 200-300ms after data loads — approximate row count from database metadata now displays instantly with data, exact count refines silently in the background
- Fix foreign key navigation arrows and pagination count appearing with visible delay on initial table load — metadata now fetches on a dedicated parallel connection concurrent with the main query
- Fix LibPQ parameterized query using Swift
deallocate()forstrdup-allocated memory instead offree() - FTS5 search input now sanitized to prevent parse errors from special characters like *, OR, AND
- Fix SQL export corrupting newline/tab/backslash characters for PostgreSQL and SQLite (MySQL-style backslash escaping was incorrectly applied to all database types)
- Fix PostgreSQL SQL export failing to import when types/sequences already exist (
DROP IF EXISTSnow always emitted for dependent types and sequences) - Fix PostgreSQL SQL export missing
CREATE TYPEdefinitions for enum columns, causing import errors - Fix PostgreSQL DDL tab not showing enum type definitions used by table columns
- Fix compilation error for PostgreSQL dependent sequences export (
fetchDependentSequencesmissing fromDatabaseDriverprotocol) - Fix PostgreSQL LIKE/NOT LIKE expressions missing
ESCAPE '\'clause, causing wildcard escaping (\%,\_) to be treated as literal characters - Fix SQLite regex filter silently degrading to LIKE substring match instead of being excluded from the WHERE clause
0.6.4 - 2026-02-23
- Fix PostgreSQL SQL export failing to fetch DDL for tables (passed quoted identifier instead of raw table name to catalog queries)
0.6.3 - 2026-02-23
- Extract shared
performDirectTabSwitchintoMainContentCoordinatorto eliminate duplicate tab-switch logic - Welcome window now uses native macOS frosted glass translucency (NSVisualEffectView with behind-window blending)
- Auto-detect MySQL vs MariaDB server type from version string to use correct timeout variable (
max_execution_timefor MySQL,max_statement_timefor MariaDB) - Improved tab switching performance by caching row providers and change managers across SwiftUI render cycles
- Eliminated selection sync feedback loop causing redundant DataGridView updates during tab switch
- Enabled NSTableView row view recycling to reduce heap allocations during scrolling
- Reduced SwiftUI re-render cascades by batching @Published mutations during tab switch
- Improved DataGrid scrolling performance:
- Row views now recycled via NSTableView's reuse pool instead of allocating new objects per scroll
- Replaced O(n) String.count with O(1) NSString.length for large cell value truncation
- Replaced expensive NSFontDescriptor.symbolicTraits checks with O(1) pointer equality on cached fonts
- Added layerContentsRedrawPolicy and canDrawSubviewsIntoLayer to reduce compositing overhead
- Cached NULL display string locally instead of per-cell singleton access
- Cached AnyChangeManager to avoid per-render allocation with Combine subscriptions
- Deferred accessibility label generation to when VoiceOver is active
- Removed unnecessary async dispatch in focusedColumn, collapsed two reloadData calls into one
0.6.2 - 2026-02-23
- Replace generic SwiftUI colors with native macOS system colors (
Color(nsColor: .system*)instead ofColor.red/green/blue/orange) for proper dark mode, vibrancy, and accessibility adaptation - Replace hardcoded opacity on semantic colors with
quaternaryLabelColor/tertiaryLabelColor - Use
shadowColorinstead ofColor.blackfor shadows - Replace iOS-style Capsule badges with RoundedRectangle
0.6.1 - 2026-02-23
- Fixed all 45 performance issues identified in PERFORMANCE.md audit:
- Memory: RowBuffer reference wrapper for QueryTab (MEM-1/2), index-based sort cache (MEM-3), streaming XLSX export with inline strings (MEM-4/15), driver-level row limits cap at 100K rows (MEM-5), removed redundant String deep copies (MEM-6), weak driver reference in SQLSchemaProvider (MEM-9), undo stack depth cap (MEM-10), dictionary-based tab pending changes (MEM-11), weak self in Task captures (MEM-12), clear cached data on disconnect (MEM-13), AI chat message cap (MEM-14)
- CPU: Removed unicodeScalars.map in MariaDB/PostgreSQL drivers (CPU-1/2), cached 100+ regex patterns in SQLFormatterService (CPU-3/5/8/9/10), async Keychain reads (CPU-4), cached stripLimitOffset/extractTableName/isDangerousQuery regex (CPU-6/13/14), cached CSV decimal regex (CPU-7), O(1) change lookup index (CPU-11), removed unused loadPassword call (CPU-12)
- Data handling: Auto-append LIMIT 10000 for unprotected queries (DAT-1), driver-level row limit cap for MySQL/PostgreSQL (DAT-2), SQLite row limit cap at 100K (DAT-3), batch fetchAllColumns via INFORMATION_SCHEMA (DAT-4), index permutation sort cache (DAT-5), cached InMemoryRowProvider in @State (DAT-6), clipboard 50K row cap (DAT-7), Int-based row IDs replacing UUID allocation (DAT-8)
- Network: Phase 2 metadata cache check (NET-1), connect_timeout for LibPQ (NET-2), driver-level cancelQuery via mysql_kill/PQcancel/sqlite3_interrupt (NET-3), isLoading guard for sidebar (NET-4), reuse cached schema for AI chat (NET-5)
- I/O: Throttled history cleanup (IO-1), async history storage migration (IO-2), consolidated onChange handlers (IO-3)
0.6.0 - 2026-02-22
- Inline AI suggestions (ghost text) in the SQL editor — auto-triggers on typing pause, Tab to accept, Escape to dismiss
- Schema-aware inline suggestions — AI now uses actual table/column names from the connected database (cached with 30s TTL, respects
includeSchemaandmaxSchemaTablessettings) - AI feature highlight row on onboarding features page
- Added VoiceOver accessibility labels to custom controls: data grid (table view, column headers, cells), filter panel (logic toggle, presets, action buttons, filter row controls), toolbar buttons (connection switcher, database switcher, refresh, export, import, filter toggle, history toggle, inspector toggle), editor tab bar (tab items, close buttons, add tab button), and sidebar (table/view rows, search clear button)
- Migrated notification observers in
MainContentCommandActionsfrom Combine publishers (.publisher(for:).sink) to async sequences (for awaitoverNotificationCenter.default.notifications(named:)) — removesAnyCancellablestorage in favor ofTaskhandles with proper cancellation on deinit - Migrated tab state persistence from UserDefaults to file-based storage in Application Support — prevents large JSON payloads from bloating the plist loaded at app launch, with automatic one-time migration of existing data
- Refactored menu and toolbar commands from NotificationCenter to
@FocusedObjectpattern — menu commands and toolbar buttons now callMainContentCommandActionsmethods directly instead of posting global notifications, with context-aware routing for structure view operations - Redesigned connection form with tab-based layout (General / SSH Tunnel / SSL/TLS / Advanced), replacing the single-scroll layout
- Revamped connection form UI to use native macOS grouped form style (
Form/.formStyle(.grouped)) withLabeledContentfor automatic label-value alignment andSectionheaders — replacing the previous hand-rolledVStacklayout with customFormFieldcomponent - Removed unused
FormFieldcomponent and helper methods (iconForType,colorForType) - SQLite connections now only show General and Advanced tabs (SSH/SSL hidden)
- Added async/await wrapper methods to
QueryHistoryStorage— existing completion-handler API preserved for compatibility, newasyncoverloads usewithCheckedContinuationfor modern Swift concurrency callers
- Fixed TOCTOU race condition in
SQLiteDriver— replacednonisolated(unsafe)+ DispatchQueue pattern with a dedicated actor (SQLiteConnectionActor) that serializes all sqlite3 handle access, preventing concurrent task races on the connection state - Consolidated multiple
.sheet(isPresented:)modifiers inMainContentViewinto a single.sheet(item:)with anActiveSheetenum — fixes SwiftUI anti-pattern where only the last.sheetmodifier reliably activates - Replaced blocking
Process.waitUntilExit()calls inSSHTunnelManagerwith asyncwithCheckedContinuation-based waiting, and replaced the fixed 1.5s sleep with active port probing — SSH tunnel setup no longer blocks the actor thread, keeping the UI responsive during connection - Eliminated potential deadlocks in
MariaDBConnectionandLibPQConnection— replaced allqueue.synccalls (indisconnect,deinit,isConnected,serverVersion) with lock-protected cached state andqueue.asynccleanup, preventing deadlocks when callbacks re-enter the connection queue - SQL editor now respects the macOS accessibility text size preference (System Settings > Accessibility > Display > Text Size) — the user's chosen font size is scaled by the system's preferred text size factor, with live updates when the setting changes
- Fixed retain cycle in
UpdaterBridge—.assign(to:on:self)retains self strongly; replaced with.sinkusing[weak self] - Fixed leaked NotificationCenter observer in
SQLEditorCoordinator— observer token is now stored and removed indestroy() - Eliminated tab switching delay — replaced view teardown/recreation with
ZStack+ForEachto keep NSViews alive, moved tab persistence I/O to background threads, skipped unnecessary change-tracking deep copies, and coalesced redundant inspector/sidebar updates during tab switch - Reduced tab-switch CPU spikes from 40-60% to ~10-20% by eliminating redundant
reloadData()calls:configureForTableno longer triggers a reload during tab switch (single controlled bump instead of 2-3),onChange(of: resultColumns)is suppressed while the switch is in progress, andDataGridView.updateNSViewskips all heavy work when the data identity hasn't changed - Table open now shows data instantly — split
executeQueryInternalinto two phases: rows display immediately after SELECT completes, metadata (columns, FKs, enums, row count) loads in the background without blocking the grid - Eliminated 20-80ms overhead when clicking an already-open table in the sidebar —
openTableTabshort-circuits immediately, andTableProTabSmartno longer fires@Publishedwhen the selected tab hasn't changed - Keychain
SecItemAddreturn values are now checked and logged — previously, failed writes (e.g.errSecDuplicateItem,errSecInteractionNotAllowed) were silently discarded, risking password loss - Added
kSecAttrServiceto all Keychain queries acrossConnectionStorage,LicenseStorage, andAIKeyStorage— items now have a proper service identifier, preventing potential collisions with other apps - Ensured proper cleanup for
@Statereference type tokens — tracked untrackedTaskinstances inImportDialog(file selection),AIProviderEditorSheet(model fetching, connection test), and addedonDisappearcancellation to prevent leaked work after view dismissal - Replaced
.onAppearwith.taskfor I/O operations inConnectionTagEditor— uses SwiftUI-idiomatic lifecycle-tied loading instead ofonAppearwhich can re-fire on navigation
0.5.0 - 2026-02-19
- AI chat panel — native macOS inspector styling: removed iOS-style chat bubbles, flattened message layout with role headers and compact spacing, reduced heading sizes for narrow sidebar, inline typing indicator without pill background
- AppKit → SwiftUI migration: migrated 5 NSPopover controllers (Enum, Set, TypePicker, JSONEditor, ForeignKey) to SwiftUI content views with a shared
PopoverPresenterutility — eliminates manualNSEventmonitors,NSPopoverDelegate, and singleton patterns - AppKit → SwiftUI migration: replaced
KeyEventHandlerNSViewRepresentable with native.onKeyPress()modifiers (macOS 14+) in DatabaseSwitcherSheet and WelcomeWindowView - AppKit → SwiftUI migration: replaced AppKit history panel (5 files:
HistoryPanelController,HistoryListViewController,QueryPreviewViewController,HistoryTableView,HistoryRowView) with single pure SwiftUIHistoryPanelViewusingHSplitView,Listwith selection, context menus, and swipe-to-delete - AppKit → SwiftUI migration: replaced
ExportTableOutlineView(NSOutlineView, 757 lines across 2 files) with SwiftUIExportTableTreeViewusingList,DisclosureGroup, and tristate checkboxes (~146 lines) - Design tokens: replaced hardcoded
Color.secondary.opacity(0.6)with systemColor(nsColor: .tertiaryLabelColor)inDesignConstantsandToolbarDesignTokensfor proper semantic color
- AI chat panel shows "Set Up AI Provider" empty state when no AI provider is configured, with a button to open Settings
- AI chat panel — right-side panel for AI-assisted SQL queries with multi-provider support (Claude, OpenAI, OpenRouter, Ollama, custom endpoints)
- AI provider settings — configure multiple AI providers in Settings > AI with API key management (Keychain), endpoint configuration, model selection, and connection testing
- AI feature routing — map AI features (Chat, Explain Query, Fix Error, Inline Suggestions) to specific providers and models
- AI schema context — automatically includes database schema, current query, and query results in AI conversations for context-aware assistance
- AI chat code blocks — SQL code blocks in AI responses include Copy and Insert to Editor buttons
- AI chat markdown rendering — replaced custom per-line AttributedString parsing with MarkdownUI library for full CommonMark + GitHub Flavored Markdown support (proper lists, tables, blockquotes, headers, strikethrough)
- Per-connection AI policy — control AI access per connection (Always Allow, Ask Each Time, Never) in the connection form
- Toggle AI Chat keyboard shortcut (
⌘⇧L) and toolbar button - Tab reuse setting — opt-in option in Settings > Tabs to reuse clean table tabs when clicking a new table in the sidebar (off by default)
- Structure view: full undo/redo support (⌘Z / ⇧⌘Z) for all column, index, and foreign key operations
- Structure view: database-specific type picker popover for the Type column — searchable, grouped by category (Numeric, String, Date & Time, Binary, Other), supports freeform input for parametric types like
VARCHAR(255) - Structure view: YES/NO dropdown menu for Nullable, Auto Inc, and Unique columns (replaces freeform text input)
- Structure view: "Don't show again" toggle in SQL preview sheet now correctly skips the review step on future saves
- SQL autocomplete: new clause types — RETURNING, UNION/INTERSECT/EXCEPT, OVER/PARTITION BY, USING, DROP/CREATE INDEX/VIEW
- SQL autocomplete: smart clause transition suggestions (e.g., WHERE after FROM, HAVING after GROUP BY, LIMIT after ORDER BY)
- SQL autocomplete: qualified column suggestions (
table.column) in JOIN ON clauses andtable.*in SELECT - SQL autocomplete: compound keyword suggestions —
IS NULL,IS NOT NULL,NULLS FIRST,NULLS LAST,ON CONFLICT,ON DUPLICATE KEY UPDATE - SQL autocomplete: richer column metadata in suggestions (primary key, nullability, default value, comment)
- SQL autocomplete: keyword documentation in completion popover
- SQL autocomplete: expanded keyword and function coverage — window functions, PostgreSQL/MySQL-specific, transaction, DCL, aggregate, datetime, string, numeric, JSON
- SQL autocomplete: context-aware suggestions for ALTER TABLE, INSERT INTO, CREATE TABLE, and COUNT(*)
- SQL autocomplete: improved fuzzy match scoring — prefix and contains matches rank above fuzzy-only matches
- Keyboard shortcut customization in Settings > Keyboard — rebind any menu shortcut via press-to-record UI, with conflict detection and "Reset to Defaults" support
- Keyboard shortcut for Switch Connection (
⌘⌥C) — quickly open the connection switcher popover from the menu or keyboard
- Layout architecture: replaced
SplitViewMinWidthEnforcerNSViewRepresentable hack with proper AppDelegate-based inspector split view configuration — eliminates KVO observation, 300ms sleep, and recursive view tree traversal - Inspector data flow: replaced manual snapshot syncing (
syncRightPanelSnapshotData()+ 5onChangehandlers) withInspectorContextvalue type passed directly through the view hierarchy via@Binding - Right panel state:
RightPanelStateno longer holds snapshot copies of coordinator data or a weak coordinator reference — it now only manages panel visibility, tab state, and owned objects - AI chat panel: receives
currentQuery: String?parameter instead of aMainContentCoordinatorreference — better separation of concerns - Sidebar save: replaced
.saveSidebarChangesnotification with direct closure (RightPanelState.onSave) set by the notification handler - Structure tab grid columns now auto-size to fit content on data load
- Structure view column headers and status messages are now localized
- SQL autocomplete: 50ms debounce for completion triggers to reduce unnecessary work
- SQL autocomplete: fuzzy matching rewritten for O(1) character access performance
- Structure view: undo/redo (⌘Z / ⇧⌘Z) now works for all schema editing operations — previously non-functional
- Structure view: undo-delete no longer duplicates existing rows in the grid
- Structure view: deleting a new (unsaved) item then undoing correctly re-adds it
- Structure view: save button now disabled when validation errors exist (empty column names/types)
- Structure view: validation now rejects indexes and foreign keys referencing columns pending deletion
- Structure view: multi-column foreign keys are correctly preserved instead of being truncated to single-column
- Structure view: renaming a MySQL/MariaDB column now uses
CHANGE COLUMNinstead ofMODIFY COLUMN(which cannot rename) - Structure view: eliminated redundant
discardChanges()andloadSchemaForEditing()calls on save and initial load - PostgreSQL: DDL tab now includes PRIMARY KEY, UNIQUE, CHECK, and FOREIGN KEY constraints plus standalone indexes
- PostgreSQL: primary key columns are now correctly detected and displayed in the structure grid
- Security: escape table and database names in all driver schema queries to prevent SQL injection from names containing special characters
- SQL editor: undo/redo (⌘Z / ⇧⌘Z) now works correctly (was blocked by responder chain selector mismatch)
- SQL autocomplete: clause detection now works correctly inside subqueries
- SQL autocomplete: block comment detection no longer treats
--inside/* */as a line comment - SQL autocomplete: database-specific type keywords (e.g., PostgreSQL
JSONB, MySQLENUM) now appear in suggestions - SQL autocomplete: schema suggestions no longer disappear after CREATE TABLE
- SQL autocomplete: function completion now inserts
COUNT()with cursor between parentheses instead ofCOUNT( - SQL autocomplete: RETURNING suggestions now work after INSERT INTO and after closed
VALUES (...)parentheses - SQL autocomplete: CREATE INDEX ON suggests columns from the referenced table instead of table names
- SQL autocomplete: transition keywords (WHERE, JOIN, ORDER BY) no longer buried under columns at clause boundaries
- SQL autocomplete: schema-qualified names (e.g.,
schema.table.column) handled correctly - Data grid: column order no longer flashes/swaps when sorting (stable identifiers for layout persistence)
- Data grid: "Copy Column Name" and "Filter with column" context menu actions no longer copy sort indicators (e.g., "name 1▲")
- SQL generation: ALTER TABLE, DDL, and SQL Preview statements now consistently end with a semicolon
- AI chat: "Ask Each Time" connection policy now shows a confirmation dialog before sending data to AI — previously silently fell through to "Always Allow"
- Deleted unused
StructureTableCoordinator.swift(~275 lines of dead code) - Deleted 5 dead NSToolbar files (
ToolbarController,ToolbarWindowConfigurator,ToolbarItemFactory,ToolbarItemIdentifier,ToolbarHostingViews) — never referenced by active code - Removed
SplitViewMinWidthEnforcerstruct fromContentView.swift - Removed
.saveSidebarChangesnotification definition and subscription
0.4.0 - 2026-02-16
- SQL Preview button (eye icon) in toolbar to review all pending SQL statements before committing changes (⌘⇧P)
- Multi-column sorting: Shift+click column headers to add columns to the sort list; regular click replaces with single sort. Sort priority indicators (1▲, 2▼) are shown in column headers when multiple columns are sorted
- "Copy with Headers" feature (Shift+Cmd+C) to copy selected rows with column headers as the first TSV line, also available via context menu in the data grid
- Column width persistence within tab session: resized columns retain their width across pagination, sorting, and filtering reloads
- Dangerous query confirmation dialog for
DELETE/UPDATEstatements without aWHEREclause — summarizes affected queries before execution - SQL editor horizontal scrolling for long lines without word wrapping
- Scroll-to-match navigation in SQL editor find panel
- GitHub Sponsors funding configuration
- Raise minimum macOS version from 13.5 (Ventura) to 14.0 (Sonoma)
- Change Export/Import keyboard shortcuts from ⌘E/⌘I to ⇧⌘E/⇧⌘I to avoid conflicts with standard text editing shortcuts
- Configure URLSession to wait for network connectivity in analytics and license services
- Improve SQL statement parser to handle backslash escapes within string literals, preventing false positives in dangerous query detection
- Fix SQL editor not updating colors when switching between light and dark mode
- Fix sidebar retaining stale table selections and pending operations for tables that no longer exist after a database refresh
0.3.2 - 2026-02-14
- Fix launch crash on macOS 13 (Ventura) x86_64 caused by accessing
NSApp.appearancebeforeNSApplicationis initialized during settings singleton setup
0.3.1 - 2026-02-14
- Fix syntax highlighting not applying after paste in SQL editor — defer frame-change notification so the visible range recalculates after layout processes the new text
- Fix data grid not refreshing after inserting a new row by incrementing
reloadVersionon row insertion
0.3.0 - 2026-02-13
-
AI chat panel — right-side panel for AI-assisted SQL queries with multi-provider support (Claude, OpenAI, OpenRouter, Ollama, custom endpoints)
-
AI provider settings — configure multiple AI providers in Settings > AI with API key management (Keychain), endpoint configuration, model selection, and connection testing
-
AI feature routing — map AI features (Chat, Explain Query, Fix Error, Inline Suggestions) to specific providers and models
-
AI schema context — automatically includes database schema, current query, and query results in AI conversations for context-aware assistance
-
AI chat code blocks — SQL code blocks in AI responses include Copy and Insert to Editor buttons
-
Per-connection AI policy — control AI access per connection (Always Allow, Ask Each Time, Never) in the connection form
-
Toggle AI Chat keyboard shortcut (
⌘⇧L) and toolbar button -
Anonymous usage analytics with opt-out toggle in Settings > General > Privacy — sends lightweight heartbeat (OS version, architecture, locale, database types) every 24 hours to help improve TablePro; no personal data or queries are collected
-
ENUM/SET column editor: double-click ENUM columns to select from a searchable dropdown popover, SET columns show a multi-select checkbox popover with OK/Cancel buttons
-
PostgreSQL user-defined enum type support via
pg_enumcatalog lookup -
SQLite CHECK constraint pseudo-enum detection (e.g.,
CHECK(col IN ('a','b','c'))) -
Language setting in General preferences (System, English, Vietnamese) with full Vietnamese localization (637 strings)
-
Connection health monitoring with automatic reconnection for MySQL/MariaDB and PostgreSQL — pings every 30 seconds, retries 3 times with exponential backoff (2s/4s/8s) on failure
-
Manual "Reconnect" toolbar button appears when connection is lost or in error state
- Migrate
Libs/*.astatic libraries to Git LFS tracking to reduce repository clone size - Remove stale
.gitignoreentries for architecture-specific MariaDB libraries - Replace
filter { }.countwithcount(where:)across 7 files for more efficient collection counting - Replace
print()withLoggerin documentation examples and remove from#Previewblocks - Replace
.count > 0with!.isEmptyin documentation example
- Fix launch crash on macOS 13 caused by missing
asyncAndWaitsymbol in CodeEditSourceEditor 0.15.2 (API requires macOS 14+); updated dependency to trackmainbranch which usessyncinstead - Escape single quotes in PostgreSQL
pg_enumlookup and SQLitesqlite_masterqueries to prevent SQL injection - ENUM column nullable detection now uses actual schema metadata instead of heuristic rawType check
- PostgreSQL primary key modification now queries the actual constraint name from
pg_constraintinstead of assuming the{table}_pkeynaming convention, supporting tables with custom constraint names - Align Xcode
SWIFT_VERSIONbuild setting from 5.0 to 5.9 to match.swiftformattarget version
0.2.0 - 2026-02-11
-
AI chat panel — right-side panel for AI-assisted SQL queries with multi-provider support (Claude, OpenAI, OpenRouter, Ollama, custom endpoints)
-
AI provider settings — configure multiple AI providers in Settings > AI with API key management (Keychain), endpoint configuration, model selection, and connection testing
-
AI feature routing — map AI features (Chat, Explain Query, Fix Error, Inline Suggestions) to specific providers and models
-
AI schema context — automatically includes database schema, current query, and query results in AI conversations for context-aware assistance
-
AI chat code blocks — SQL code blocks in AI responses include Copy and Insert to Editor buttons
-
Per-connection AI policy — control AI access per connection (Always Allow, Ask Each Time, Never) in the connection form
-
Toggle AI Chat keyboard shortcut (
⌘⇧L) and toolbar button -
SSL/TLS connection support for MySQL/MariaDB and PostgreSQL with configurable modes (Disabled, Preferred, Required, Verify CA, Verify Identity) and certificate file paths
-
RFC 4180-compliant CSV parser for clipboard paste with auto-detection of CSV vs TSV format
-
Explain Query button in SQL editor toolbar and menu item (⌥⌘E) for viewing execution plans
-
Connection switcher popover for quick switching between active/saved connections from the toolbar
-
Date/time picker popover for editing date, datetime, timestamp, and time columns in the data grid
-
Read-only connection mode with toggle in connection form, toolbar badge, and UI-level enforcement (disables editing, row operations, and save changes)
-
Configurable query execution timeout in Settings > General (default 60s, 0 = no limit) with per-driver enforcement via
statement_timeout(PostgreSQL),max_execution_time(MySQL),max_statement_time(MariaDB), andsqlite3_busy_timeout(SQLite) -
Foreign key lookup dropdown for FK columns in the data grid — shows a searchable popover with values from the referenced table, displaying both the ID and a descriptive display column
-
JSON column editor popover for JSON/JSONB columns with pretty-print formatting, compact mode, real-time validation, and explicit save/cancel buttons
-
Excel (.xlsx) export format with lightweight pure-Swift OOXML writer — supports shared strings deduplication, bold header rows, numeric type detection, sheet name sanitization, and multi-table export to separate worksheets
-
View management: Create View (opens SQL editor with template), Edit View Definition (fetches and opens existing definition), and Drop View from sidebar context menu. Adds
fetchViewDefinition()to all database drivers (MySQL, PostgreSQL, SQLite)
- Fixed crash on launch on macOS 13 (Ventura) caused by missing Swift runtime symbol
- Fix redo functionality in data grid (Cmd+Shift+Z now works correctly)
- Fix redo stack not being cleared when new changes are made (standard undo/redo behavior)
- Fix
canRedo()always returning false in data grid coordinator - Wire undo/redo callbacks directly to data grid for proper responder chain validation
- Fix MariaDB connection error 1193 "Unknown system variable 'max_execution_time'" by using the correct
max_statement_timevariable for MariaDB - Query timeout errors no longer prevent database connections from being established
- Replace all
print()statements with structured OSLogLoggeracross 25 files for better debugging via Console.app
0.1.1 - 2026-02-09
-
AI chat panel — right-side panel for AI-assisted SQL queries with multi-provider support (Claude, OpenAI, OpenRouter, Ollama, custom endpoints)
-
AI provider settings — configure multiple AI providers in Settings > AI with API key management (Keychain), endpoint configuration, model selection, and connection testing
-
AI feature routing — map AI features (Chat, Explain Query, Fix Error, Inline Suggestions) to specific providers and models
-
AI schema context — automatically includes database schema, current query, and query results in AI conversations for context-aware assistance
-
AI chat code blocks — SQL code blocks in AI responses include Copy and Insert to Editor buttons
-
Per-connection AI policy — control AI access per connection (Always Allow, Ask Each Time, Never) in the connection form
-
Toggle AI Chat keyboard shortcut (
⌘⇧L) and toolbar button -
Auto-update support via Sparkle 2 framework (EdDSA signed)
-
"Check for Updates..." menu item in TablePro menu
-
Software Update section in Settings > General with auto-check toggle
-
CI appcast generation and auto-deploy on tagged releases
-
Migrate SQL editor to CodeEditSourceEditor (tree-sitter powered)
-
Multi-statement SQL execution support
-
"Show Structure" context menu for sidebar tables
-
Improved filter panel UI/UX
-
SwiftUI EditorTabBar (replacing AppKit NativeTabBarView)
-
GPL v3 license
- Fix MySQL 8+ connections failing with
caching_sha2_passwordplugin error by rebuilding libmariadb.a with the auth plugin compiled statically - Fix Delete key on data grid row from marking table as deleted
- Downgrade all APIs to support macOS 13.5 (Ventura)
- Code review fixes for multi-statement execution
- CI release notes now read from CHANGELOG.md instead of auto-generating from commits
- Removed
prepare-libsCI job to speed up build pipeline (~5 min savings) - Add SPM Package.resolved for CodeEditSourceEditor dependencies
- Add Claude Code project settings
- Update build/test commands with
-skipPackagePluginValidation
0.1.0 - 2026-02-05
TablePro is a native macOS database client built with SwiftUI and AppKit, designed as a fast, lightweight alternative to TablePlus.
-
Database Support
- MySQL/MariaDB connections
- PostgreSQL support
- SQLite database files
- SSH tunneling for secure remote connections
-
SQL Editor
- Syntax highlighting with TreeSitter
- Intelligent autocomplete for tables, columns, and SQL keywords
- Multi-tab editing support
- Query execution with result grid
-
Data Management
- Interactive data grid with sorting and filtering
- Inline editing capabilities
- Add, edit, and delete rows
- Pagination for large result sets
- Export data (CSV, JSON, SQL)
-
Database Explorer
- Browse tables, views, and schema
- View table structure and indexes
- Quick table information and statistics
- Search across database objects
-
User Experience
- Native macOS design with SwiftUI
- Dark mode support
- Customizable keyboard shortcuts
- Query history tracking
- Multiple database connections
-
Developer Features
- Import/export connection configurations
- Custom SQL query templates
- Performance optimized for large datasets