Skip to content

feat: command palette for actions and database objects - #545

Draft
verbaux wants to merge 22 commits into
TabularisDB:mainfrom
verbaux:feature/command-palette-foundation
Draft

feat: command palette for actions and database objects#545
verbaux wants to merge 22 commits into
TabularisDB:mainfrom
verbaux:feature/command-palette-foundation

Conversation

@verbaux

@verbaux verbaux commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces QuickNavigatorModal with a Spotlight-style palette that has two (for now) modes: object search (tables, views, routines, triggers) and an action palette on Cmd/Ctrl+Shift+A.

image

The part worth reviewing is not the UI. Both palettes and the explorer sidebar each used to build their own "open this in the editor" logic — assembling SQL, guessing the tab type, and reading the active connection regardless of which pane the user clicked in. This PR gives them one contract to share.

What changed

Navigation contract

  • src/utils/editorNavigation.ts — router state now carries a typed EditorNavigationRequest; Editor.tsx parses it and opens the tab.
  • src/utils/databaseObjectActions.ts + src/hooks/useDatabaseObjectNavigation.ts — one definition of what "open / count / show definition" means per object type. The sidebar's 16 call sites and both palettes go through it.

Palette

  • src/utils/commandScopeStore.ts + src/hooks/useCommandPaletteScope.ts — each pane registers a command scope; the palette resolves against the active one. This is what makes split view target the right connection.
  • src/components/ui/SpotlightPalette.tsx — shared shell (focus trap, arrow navigation, ARIA combobox/listbox), used by both modes.
  • src/utils/paletteItems.ts + objectPaletteItems.ts — single item pipeline for search, grouping and ranking. Uses Fuse.js, already a dependency.

Modals no longer read the active connection

  • SchemaModal and GenerateSQLModal take an explicit TableTarget { connectionId, tableName, schema }. Before this, opening either from a non-active split pane inspected the wrong connection.

Shortcut

  • command_palette_actionsCmd+Shift+A / Ctrl+Shift+A, overridable in settings.

Test plan

  • pnpm typecheck clean
  • pnpm lint clean
  • pnpm test — 3448/3448 pass, 201 files
  • 17 new test files: command scoping across split panes (active scope, root fallback, missing scope), palette keyboard handling and focus trap, sidebar → navigation wiring for double-click and every context-menu entry, definition loading including the failure path, item search/grouping/ranking, route-state parser
  • Manual: split view, two connections — action palette from each pane targets that pane's table
  • Manual: object palette against Postgres (schemas) and SQLite/MySQL (no schemas)
  • Manual: routine and trigger definitions open read-only where expected

Known limitations

The object palette builds its multi-database list from the saved connection.params.database (src/hooks/useCommandPaletteObjectItems.ts:66), while #524/#530 reconcile dropped databases into selectedDatabases only. A database dropped on the server can still show up in the palette until the connection list reloads.

Not introduced here — QuickNavigatorModal read the same source, and quickNavigator.ts iterated configuredDatabases on main too. Left as-is because the fix is a one-line source swap but the multi-database path has no test fixture yet.

Open questions

Draft because I'd rather hear about the approach before polishing:

  • Scope registration. Panes registering into a store is a new pattern here. If you'd rather the scope came from context, or straight from editor state, say so and I'll rework it.
  • Route state as the contract. It keeps callers honest, but navigation intent now lives in location.state.
  • Nothing shows which palette you're in. The placeholder is the only cue and it disappears as soon as you type. Cheapest fix is a visible mode label in the header. The bigger question is whether these should stay two surfaces on two shortcuts (⌘P objects, ⌘⇧A actions) or become one palette with switchable modes, DataGrip-style.
  • Only two commands so far, deliberately. The action palette ships Open settings and Open current table in SQL console — enough to exercise the registry, the scoping and the navigation contract without padding the diff. Adding a third is one PaletteItem in createBuiltInCommandItems (src/utils/builtInCommands.ts) plus an i18n key. Anything that needs more than connectionId / driver / table extendsCommandScope first — switching connections is the obvious next one and the first case that would need that. Happy to take a list of what you'd want in the initial set.

@verbaux verbaux changed the title Feature/command palette foundation feat: command palette for actions and database objects Jul 28, 2026
verbaux added 22 commits August 2, 2026 17:49
Overlay clicks are ignored unless a caller opts in with closeOnBackdrop,
so existing modals keep their current behaviour.
Routes that own the whole pane (root, connections, settings) never show a
split view, so the decision lives next to the layout model instead of the
components that render it.
Both palettes and the explorer sidebar used to build their own editor
navigation, each with a slightly different idea of what "open this object"
means. Route state now carries a typed EditorNavigationRequest, and the
sidebar, the palettes and the schema/SQL modals all go through it.

- Extract database object actions and editor navigation into utilities,
  exposed to components through useDatabaseObjectNavigation.
- Drop the separate actions/objects palette providers in favour of item
  hooks over a single palette context.
- Move the schema and generate-SQL modals onto an explicit TableTarget, so
  they no longer read the active connection behind the caller's back.
The locale landed upstream while this branch was in flight, so it was the
only one missing the palette keys.
@verbaux
verbaux force-pushed the feature/command-palette-foundation branch from a8ef2b7 to a8c3ac6 Compare August 2, 2026 15:54
@debba

debba commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Hi @verbaux , first off, sorry for taking so long to get back to you. This PR deserved a quicker reply, especially since you left such clear questions. And thank you for the work here, the navigation contract cleanup alone is something we've needed for a while.

Going through your points:

Scope registration. Keep the store. The palette is a single modal at the root while the scopes live in the panes, so a context-based approach would force the palette inside the active pane's provider, which doesn't really work with one modal and N panes. The store with useSyncExternalStore decouples who registers from who reads, and the ownership guard on cleanup already handles re-registration races, so I'm comfortable with the pattern. One thing I'd like to double check though: in split view the scope id is the connection id. If the same connection can end up open in two panes at once, they'd overwrite each other's registration. If that's a reachable state, the scope id should probably be the pane id rather than the connection id.

Route state as the contract. Fine by me. You clear the state right after consuming it and dedupe via the navigation key, so refresh and back don't re-run anything. And since split panes execute the intent directly without going through the router, route state stays what it should be: the cross-route contract to the main editor. No rework needed.

Mode indication. Let's do the cheap fix in this PR, a visible mode label in the header. The placeholder disappearing as soon as you type isn't enough of a cue. On the bigger question, I wouldn't block this PR on it. The shared SpotlightPalette shell makes merging the two surfaces into one palette cheap later, so keep the two shortcuts for now and we can revisit once there are more commands to justify a mode switcher.

Initial command set. A few that should fit the current CommandScope without extending anything, since they already exist as object actions: new SQL console at connection level (not just "open current table in console"), inspect / show schema for the current table, generate SQL, count rows. Plus "open connection manager" as pure navigation, same shape as open settings. I agree switching connections is the right first candidate for extending the scope, but I'd leave that for a follow-up rather than grow this diff.

On the known limitation with configuredDatabases: agreed it's pre-existing and fine to leave, but could you open a follow-up issue linking #524/#530 so it doesn't get lost?

Happy to take this out of draft whenever you feel ready, I'll do a proper pass with real data on the split view scenarios then. Thanks again for the thorough writeup, it made this much easier to review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants