Skip to content

feat(agor-ui): global keyboard shortcuts + command palette#1794

Open
preset-ce wants to merge 1 commit into
mainfrom
keyboard-shortcuts
Open

feat(agor-ui): global keyboard shortcuts + command palette#1794
preset-ce wants to merge 1 commit into
mainfrom
keyboard-shortcuts

Conversation

@preset-ce

Copy link
Copy Markdown

Summary

Adds a small, extensible keyboard-shortcut system so common actions can be driven from the keyboard, per the request in #agor (Juliann / Evan / Daniel).

Everything is driven by a single registry (apps/agor-ui/src/keyboard/shortcuts.ts) that powers both key matching and the discoverable help overlay, so they can never drift out of sync.

Shortcuts

Key Action
c Start a new session (opens the create dialog)
b Board switcher — command palette with filter + ↑/↓ + Enter
a Authenticate MCP servers (bulk token refresh)
? or ⌘/Ctrl + / Show the keyboard-shortcuts help overlay
⌘/Ctrl + K Search everything (documented in the overlay; still owned by GlobalSearch)

Key-choice rationale: single-key shortcuts follow the GitHub / Linear convention. They're memorable, and because the system suppresses shortcuts while the user is typing in an input / textarea / contenteditable / textbox-role element, single keys never collide with browser or OS chords (which are all modifier-based). Matching is exact on modifiers, so bare c does not fire on ⌘/Ctrl+C. is used on macOS and Ctrl elsewhere (mod in the combo DSL).

Design

  • src/keyboard/
    • shortcuts.ts / types.ts — the registry (single source of truth).
    • matchShortcut.ts — pure combo parser + matcher, isEditableTarget() guard, platform-aware formatCombo(). Unit-tested.
    • KeyboardShortcutsProvider.tsx — installs one global keydown listener and dispatches by id. Components attach handlers with useKeyboardShortcut(id, handler), so bindings are mount-scoped for free. Owns the help-overlay state.
    • KeyboardShortcutsHelp.tsx — the ? overlay, generated entirely from the registry and grouped (General / Navigation / Actions).
  • components/BoardSwitcherPalette/ — keyboard-first board switcher modal (complements the existing click-driven header BoardSwitcher dropdown rather than replacing it).
  • Wiring: the provider is mounted once in src/App.tsx (around AppContent); a small GlobalShortcuts component inside App binds the app-level handlers and owns the palette. The new-session handler reuses the same create-dialog flow as the existing "New session" button.

MCP authentication — what's implemented vs. deferred

Implemented (a): re-authenticates every OAuth MCP server whose token is missing or expired by calling the existing mcp-servers/oauth-refresh endpoint for each, in parallel, with a summary toast. No popups, no new backend.

Deferred / noted: servers that need an interactive sign-in (no refresh token / revoked grant) can't be batched — browsers block bulk OAuth popups, and each flow needs a user gesture. Rather than half-wire that (and get popups blocked), those servers are reported by name in a warning toast so the user can sign in per-server from Settings → MCP servers (the existing per-pill flow). A true one-click "authenticate all" would need either a bulk backend endpoint or a dedicated multi-step consent surface.

Tests

matchShortcut.test.ts (10 cases): mod→Cmd/Ctrl mapping, exact-modifier matching (bare c vs ⌘C), case-insensitivity, ? implicit-shift handling, editable-target detection, and platform key formatting. pnpm typecheck and biome ci pass.

Screenshots

Not captured — this branch's worktree has no running dev environment. The help overlay is a standard AntD Modal rendering styled <kbd> caps; the palette mirrors the existing global-search dropdown patterns.

🤖 Generated with Claude Code

Add an extensible keyboard-shortcut system driven by a single registry
(src/keyboard) that powers both key matching and a discoverable help
overlay, so the two can never drift.

Shortcuts (single-key, GitHub/Linear style — suppressed while typing in
inputs/textareas/contenteditable, and exact-modifier matching so they
never collide with browser chords like Cmd/Ctrl+C):
- c → start a new session (opens the create dialog)
- b → board switcher command palette (filter + ↑/↓ + Enter)
- a → authenticate MCP servers (bulk silent token refresh)
- ? or Cmd/Ctrl+/ → show the keyboard-shortcuts help overlay
- Cmd/Ctrl+K → documented in the overlay; still owned by GlobalSearch

The provider installs one global keydown listener and dispatches by id;
components attach handlers via useKeyboardShortcut() so bindings are
mount-scoped for free.

MCP auth re-authenticates every OAuth server whose token is missing or
expired via the refresh endpoint (no popups). Servers needing an
interactive sign-in can't be batched (browsers block bulk OAuth popups)
and are reported so the user can sign in per-server from Settings.

Tests cover the matcher (mod mapping, exact-modifier rules, `?` implicit
shift, editable-target detection, platform key formatting).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant