Skip to content

Releases: TableProApp/TablePro

v0.9.3

Choose a tag to compare

@github-actions github-actions released this 28 Feb 12:23
fc0cf26

Fixed

  • PostgreSQL SSH tunnel connections failing with "no encryption" due to SSL config not being preserved

v0.9.2

Choose a tag to compare

@github-actions github-actions released this 28 Feb 07:36
9b69ba7

Fixed

  • Fix app bundle not ad-hoc signed — signing step was unreachable when no dylibs were bundled

v0.9.1

Choose a tag to compare

@github-actions github-actions released this 28 Feb 07:21
e1b7ca5

Fixed

  • Fix Sparkle auto-update failing with "improperly signed" error — release ZIPs now preserve framework symlinks and include proper ad-hoc code signatures

v0.9.0

Choose a tag to compare

@github-actions github-actions released this 28 Feb 06:56
535e649

Added

  • 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
  • :q command 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)

Changed

  • 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

Fixed

  • 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 — deinit now cleans up NSEvent monitors, notification observers, and work items that leaked when CodeEditSourceEditor never called destroy()
  • 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 _strchrnul symbol 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

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 27 Feb 04:44
5fbf214

New Features

  • Native macOS Window Tabs: The tab bar is now rendered by macOS itself — identical to Finder, Safari, and Xcode tabs with automatic dark/light mode support, drag-to-reorder, and "Merge All Windows" for free
  • Independent Tab Windows: Each tab is a full independent window with its own sidebar, editor, and state — no more shared state between tabs

Improvements

  • Tab Switching Performance: Schema is now cached per connection so new native tabs reuse the already-loaded schema instead of re-fetching from the database (saves 500ms–2s per tab)
  • Background Schema Loading: Schema loads concurrently — table data appears immediately while autocomplete schema loads in the background
  • Sidebar MVVM Refactor: Sidebar table list migrated to a testable SidebarViewModel architecture with extracted TableRowView and context menu components
  • Window title updates dynamically after in-place navigation (sidebar click, FK navigation)
  • 10+ SwiftUI rendering optimizations to prevent O(N) view cascades across windows

Bug Fixes

  • Fixed sidebar losing keyboard focus (arrow key navigation) after opening a second table tab
  • Fixed sidebar active state flash when clicking a table that opens in a new native window tab
  • Fixed sidebar losing active state when opening a second table in a new native window tab
  • Fixed sidebar not refreshing after switching databases via Cmd+K
  • Fixed Cmd+W in empty state doing nothing — now closes the connection window and disconnects
  • Fixed Cmd+K database switch flooding all windows with error alerts
  • Fixed clicking a table in the sidebar replacing the current tab instead of opening a new one
  • Fixed Cmd+W on any tab disconnecting the entire session
  • Fixed Cmd+T from empty state creating two native tabs instead of one
  • Fixed native tab title showing "SQL Query" instead of the table name
  • Fixed Cmd+W on the last tab disconnecting the session instead of returning to empty state

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 25 Feb 03:32
85ff3d5

Added

  • 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

Changed

  • 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 fetchTableMetadata now caps row count scan at 100k rows to avoid full table scans on large tables
  • SQLite fetchIndexes uses 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 of mysql_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 TableProTabSmart field 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 fetchDatabaseMetadata into 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 let for 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 fetchAllColumns overridden with single sqlite_master + pragma_table_info query
  • 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
  • SQLFormatterService uses NSMutableString for keyword uppercasing and integer counter for placeholders
  • SQLContextAnalyzer uses single alternation regex and single-pass state machine for string/comment detection
  • escapeJSONString iterates UTF-8 bytes instead of grapheme clusters
  • AppSettingsStorage caches JSONDecoder/JSONEncoder as stored properties
  • AppSettingsManager stores validated settings in memory after didSet
  • FilterSettingsStorage uses tracked key set instead of loading full plist
  • Keychain saves use SecItemAdd + SecItemUpdate upsert pattern instead of delete + add
  • Autocomplete detectFunctionContext uses index tracking instead of character-by-character string building

Fixed

  • 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() for strdup-allocated memory instead of free()
  • 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 EXISTS now always emitted for dependent types and sequences)
  • Fix PostgreSQL SQL export missing CREATE TYPE definitions 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 (fetchDependentSequences missing from DatabaseDriver protocol)
  • 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

v0.6.4

Choose a tag to compare

@github-actions github-actions released this 23 Feb 09:01
v0.6.4
82cb109

Fixed

  • Fix PostgreSQL SQL export failing to fetch DDL for tables (passed quoted identifier instead of raw table name to catalog queries)

v0.6.3

Choose a tag to compare

@github-actions github-actions released this 23 Feb 07:47
v0.6.3
d3e5b88

Changed

  • Extract shared performDirectTabSwitch into MainContentCoordinator to eliminate duplicate tab-switch logic
  • Welcome window now uses native macOS frosted glass translucency (NSVisualEffectView with behind-window blending)

Fixed

  • Auto-detect MySQL vs MariaDB server type from version string to use correct timeout variable (max_execution_time for MySQL, max_statement_time for 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

v0.6.2

Choose a tag to compare

@github-actions github-actions released this 23 Feb 06:06
e323f7d
  • Bug fixes and improvements

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 22 Feb 10:06
98d9687

Added

  • 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 includeSchema and maxSchemaTables settings)
  • 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)

Changed

  • Migrated notification observers in MainContentCommandActions from Combine publishers (.publisher(for:).sink) to async sequences (for await over NotificationCenter.default.notifications(named:)) — removes AnyCancellable storage in favor of Task handles 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 @FocusedObject pattern — menu commands and toolbar buttons now call MainContentCommandActions methods 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)) with LabeledContent for automatic label-value alignment and Section headers — replacing the previous hand-rolled VStack layout with custom FormField component
  • Removed unused FormField component 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, new async overloads use withCheckedContinuation for modern Swift concurrency callers

Fixed

  • Fixed TOCTOU race condition in SQLiteDriver — replaced nonisolated(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 in MainContentView into a single .sheet(item:) with an ActiveSheet enum — fixes SwiftUI anti-pattern where only the last .sheet modifier reliably activates
  • Replaced blocking Process.waitUntilExit() calls in SSHTunnelManager with async withCheckedContinuation-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 MariaDBConnection and LibPQConnection — replaced all queue.sync calls (in disconnect, deinit, isConnected, serverVersion) with lock-protected cached state and queue.async cleanup, 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 .sink using [weak self]
  • Fixed leaked NotificationCenter observer in SQLEditorCoordinator — observer token is now stored and removed in destroy()
  • Eliminated tab switching delay — replaced view teardown/recreation with ZStack+ForEach to 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: configureForTable no 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, and DataGridView.updateNSView skips all heavy work when the data identity hasn't changed
  • Table open now shows data instantly — split executeQueryInternal into 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 — openTableTab short-circuits immediately, and TableProTabSmart no longer fires @Published when the selected tab hasn't changed
  • Keychain SecItemAdd return values are now checked and logged — previously, failed writes (e.g. errSecDuplicateItem, errSecInteractionNotAllowed) were silently discarded, risking password loss
  • Added kSecAttrService to all Keychain queries across ConnectionStorage, LicenseStorage, and AIKeyStorage — items now have a proper service identifier, preventing potential collisions with other apps
  • Ensured proper cleanup for @State reference type tokens — tracked untracked Task instances in ImportDialog (file selection), AIProviderEditorSheet (model fetching, connection test), and added onDisappear cancellation to prevent leaked work after view dismissal
  • Replaced .onAppear with .task for I/O operations in ConnectionTagEditor — uses SwiftUI-idiomatic lifecycle-tied loading instead of onAppear which can re-fire on navigation