Skip to content

feat(shortcuts): add a keyboard shortcuts dialog (#746) - #821

Open
Asgabani wants to merge 1 commit into
libredb:mainfrom
Asgabani:feat/shortcuts-dialog
Open

Asgabani wants to merge 1 commit into
libredb:mainfrom
Asgabani:feat/shortcuts-dialog

Conversation

@Asgabani

Copy link
Copy Markdown
Contributor

Closes #746.

What changed

  • src/lib/shortcuts.ts: a single registry (SHORTCUT_GROUPS) listing every app-wide shortcut — the command palette, Monaco's Run/Format bindings, tab navigation (including the new-tab shortcut, imported from StudioTabBar rather than retyped so it can't drift), and the data profiler's Escape-to-close.
  • ShortcutsDialog (src/components/ShortcutsDialog.tsx): a self-contained dialog, following CommandPalette's own Cmd/Ctrl+K effect — it owns its open state and its ? listener (guarded against firing while typing in an input, textarea, or contentEditable element). Exposes an imperative open() via ref, the same seam QueryEditorRef already uses.
  • Wired into Studio.tsx directly, plus a new "Keyboard Shortcuts" CommandPalette entry that reaches it through the ref.
  • Wired into DataProfiler.tsx directly rather than through props: DataProfiler is rendered by both the standalone shell (Studio.tsx) and the embedded workspace (StudioWorkspace.tsx, which has no CommandPalette), so mounting ShortcutsDialog inside DataProfiler once — scoped to while the profiler is open, matching its own Escape effect's scoping — covers both hosts without threading state through either.

Testing

Ran locally (bun run format && bun run lint && bun run typecheck && bun run knip && bun run chart:check && bun run channels:showcase:check && bun run readme:check && bun run security:check && bun run test && bun run build, plus bun run build:lib && bun run attw since DataProfiler/Sidebar are reachable from the embeddable workspace export surface):

  • format / lint / typecheck / knip: clean. Lint warning count unchanged from a clean checkout (191, compared via git stash).
  • All four drift guards (chart, channels showcase, readme, security): pass.
  • bun run test:components: 44/44 groups pass. (Added the new ShortcutsDialog.test.tsx to tests/run-components.sh's group list — tests/unit/component-runner-coverage.test.ts catches a file missing from it.)
  • bun run test: same 13 pre-existing failures as a clean checkout (Helm binary not installed, missing built standalone zip). No live Postgres/MySQL in this sandbox.
  • bun run test:coverage && bun run coverage:check: 100.00% line coverage on the merged lcov.
  • bun run build, bun run build:lib, bun run attw: all succeed.

If CI surfaces something this sandbox couldn't (Helm chart tests, live DB integration tests), happy to fix it up.

Nothing in the app answered "what shortcuts exist" in one place -
Cmd/Ctrl+K was the only one documented anywhere in the UI, and
Monaco's own bindings were discoverable only through its right-click
menu. ShortcutsDialog lists every app-wide shortcut, Monaco's
included, from a single registry (src/lib/shortcuts.ts). It opens on
"?" (ignored while typing in an input, textarea or contentEditable
element) and, in the standalone shell, from a new CommandPalette
entry via an imperative ref.

Mounted directly in both Studio.tsx and DataProfiler.tsx rather than
threaded through props: DataProfiler is rendered by both the
standalone shell and the embedded workspace, so mounting it there
once covers both hosts, the same way its own Escape-to-close effect
already does.
@cevheri cevheri added the enhancement New feature or request label Sep 13, 2026
@codecov

codecov Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cevheri cevheri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Asgabani, and apologies for the first item: the ground moved under you after you opened this.

main now has src/lib/keyboard-shortcuts.ts, a shared registry with SHORTCUTS, matchesShortcut, shortcutLabel and monacoKeybinding, already used by CommandPalette, QueryEditor and StudioTabBar, and the shortcut line in docs/FEATURES.md is generated from it by bun run shortcuts:sync. That is also your StudioTabBar conflict: main dropped NEW_TAB_SHORTCUT_LABEL for shortcutLabel(SHORTCUTS.newTab). Please rebase and render the dialog from that registry rather than a second one. Adding ? there is not purely mechanical, since every entry feeds the Monaco binding, so use your judgement on the display-only rows. You also get "Cmd/Ctrl+Enter", which your hand-written "Ctrl+Enter" gets wrong on macOS.

Two things I measured that survive the rebase.

? cannot be typed in the query editor. I typed SELECT, pressed ?, and the dialog opened while the character never reached the buffer. Monaco 0.56 focuses a div.native-edit-context, so the input/textarea/contentEditable check misses it, and ? is the positional parameter placeholder in SQLite and MySQL. Your guard tests mount a real textarea, which the editor is no longer.

Studio and DataProfiler each mount a dialog with its own listener, so with the profiler open ? gives you two, and one Escape closes one of them plus the profiler underneath.

A question rather than a change: the tab bar's arrows are listed, the object browser's whole tree pattern is not. Worth settling which side widget keys sit on.

Importing NEW_TAB_SHORTCUT_LABEL was the right instinct, by the way: retyping it and changing the binding does fail your test.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No panel lists the app's keyboard shortcuts

2 participants