Skip to content

feat(sidebar): select several databases or schemas and act on them at once - #2094

Merged
datlechin merged 3 commits into
mainfrom
feat/multi-select-databases
Aug 12, 2026
Merged

feat(sidebar): select several databases or schemas and act on them at once#2094
datlechin merged 3 commits into
mainfrom
feat/multi-select-databases

Conversation

@datlechin

@datlechin datlechin commented Aug 12, 2026

Copy link
Copy Markdown
Member

A user asked whether they could select several databases and act on them together, for example to delete them. They could not: database and schema rows were not selectable at all, and Drop Database was single-name from one menu.

What changed

Sidebar tree. Database and schema rows are selectable. outlineView(_:shouldSelectItem:) gated on tableRef != nil, so containers could never be selected, let alone multi-selected. Shift-click and Cmd-click extend the selection. The context menu acts on the selection and names its count: Refresh, Copy N Names, Export..., and a destructive Drop N Databases.... Use as Active stays single-target and disables while more than one row is selected.

Database switcher. Selection moves from String? to Set<String>. Its contextMenu(forSelectionType:) already receives the correctly resolved set from SwiftUI, so the fix was to stop calling .first. Same four actions.

Targeting. SidebarMenuTarget is one pure resolver implementing AppKit's clickedRow convention: clicked row inside the selection means the whole selection, outside means the clicked row alone. Mixed selections filter to the clicked row's kind.

Schema drop. dropSchema and supportsDropSchema added to PluginKit with defaults, bridged through PluginDriverAdapter. Implemented for PostgreSQL (DROP SCHEMA ... CASCADE), SQL Server (T-SQL has no cascade, so a non-empty schema is refused by the server) and SurrealDB (REMOVE DATABASE, since its schema level is a Surreal database). A schema in a non-active database drops through a scoped metadata driver, so the statement runs against the right database instead of the connected one.

Export. ExportPreselection replaces the bare Set<String> and matches by qualified container instead of bare table name. ExportDialog.loadDatabaseItems() already built a multi-database tree, so this is preselection plumbing rather than a new export path. Export is hidden for a schema outside the connected database, because the dialog only enumerates that database's schemas and would preselect nothing.

Confirmation. One dialog for the whole set: the title counts the targets, the message lists every name (capped at 10 plus an overflow count), and the destructive button names the count. Drops run sequentially, a failure never stops the rest, and the failures are reported together at the end.

Bugs fixed on the way

  • Right-clicking a table that was not part of the selection acted on the selected tables instead, Delete included.
  • Database icons and the current-database checkmark disappeared on a selected switcher row: they were hardcoded to Color.accentColor, so they rendered accent-on-accent against the selection fill. They now go through \.backgroundProminence, the documented signal for content on a prominent selection background, which is what the sidebar rows already used. The helper moved out of Views/Sidebar since it is no longer sidebar-only. Multi-select made this obvious, but it affected single selection too.
  • A dropped database stayed in the sidebar until reconnect; nothing invalidated the cached list.
  • refreshDatabases and refreshSchemas cleared the cache before refetching, which is the "a refresh never clears the cache it is refreshing" invariant (Multiple UI Flicker #1916): the tree rendered .loading with no content as a spinner. Both now fetch first and commit over the old value, and a failed refresh keeps what is on screen.

PluginKit ABI

Additive, so no currentPluginKitVersion bump. scripts/check-pluginkit-abi.sh main reports a diff with zero removed symbols; the only additions are the two new requirements, each with its default implementation. That is the case CLAUDE.md documents as binary-compatible. Labelled abi-additive per the gate's instructions.

Testing

  • 33 tests across 6 suites, 4 of them new: menu targeting, drop eligibility, drop request strings, export preselection. Two new regression tests cover the refresh fix (a refresh commits the new list; a failed refresh keeps the old one).
  • swiftlint lint --strict clean.
  • TablePro scheme builds; the AllPlugins aggregate compiles all 30, which is what covers MSSQL and SurrealDB since neither is bundled.

No UI automation was added. TableProUITests drives the sample database, which is SQLite, and supportsDatabaseTree requires a network connection mode, so neither the tree nor the switcher is reachable without a live server. The behaviour that could be made deterministic went into pure types instead.

Follow-up: plugin release (after merge)

MSSQL and SurrealDB are registry-only, so their schema drop reaches users only through a plugin release. PostgreSQL ships with the app and needs nothing.

There is nothing to change in the repo for it: the plugin version comes from the tag name, and TableProPluginKitVersion stays at 19 in both Info.plists because the ABI change is additive. One dispatch on main after this merges:

gh workflow run build-plugin.yml \
  -f tags=plugin-mssql-v1.0.35,plugin-surrealdb-v1.0.3 \
  --ref main

Versions follow the latest existing tags (plugin-mssql-v1.0.34, plugin-surrealdb-v1.0.2). PluginKit defaults to 19 from PluginManager.swift, which is correct here. Dispatch on main rather than this branch so the published binaries match what was reviewed; the workflow refuses to overwrite a release carrying a different PluginKit label, so a build from the wrong ref costs a version number.

A new plugin on an older app is inert rather than harmful: the old app has no drop-schema UI to read the capability with.

@mintlify

mintlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Aug 12, 2026, 4:11 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin added the abi-additive PluginKit ABI diff reviewed as additive; no version bump needed label Aug 12, 2026
@datlechin
datlechin merged commit ae4f423 into main Aug 12, 2026
4 checks passed
@datlechin
datlechin deleted the feat/multi-select-databases branch August 12, 2026 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

abi-additive PluginKit ABI diff reviewed as additive; no version bump needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant