PSA/docs/features/keyboard-shortcuts.md
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

173 lines
7.6 KiB
Markdown

# Keyboard Shortcuts
Alga PSA ships a system-wide keyboard shortcut engine active on all MSP pages
(`/msp`). Shortcuts are grouped by scope: **global** shortcuts fire anywhere;
**page** shortcuts fire on the current page; **panel/dialog** shortcuts fire
when a drawer or dialog is open and take priority over page shortcuts.
## Opening the Command Palette
Press **`Mod+K`** (⌘K on macOS, Ctrl+K on Windows/Linux) to open the command
palette. The palette merges navigation destinations, runnable shortcut actions,
and full record search into one keyboard-driven overlay. From here you can:
- **Navigate** to any page by typing its name (e.g. `tickets`, `schedule`).
- **Search records** across clients, tickets, contacts, projects, assets, and
more.
- **Run an action** by prefixing `>` (e.g. `>create ticket`).
- **Open a record by ID** with `#` (e.g. `#1042`).
- **Narrow by type** using field scopes: `ticket:`, `client:`, `project:`,
`asset:`, `contact:`, `user:` / `@name`, `nav:` / `/path`.
- **Magic keywords**: `$mine`, `$recent`, `$open` (abbreviate to first
syllable: `$m`, `$rec`, `$op`).
- **Operators**: double-quoted phrases (`status:"in progress"`), `-` / `NOT`
exclusion, `*` and `?` wildcards, fuzzy `term~`.
## Discovering Shortcuts
Press **`?`** on any MSP page (when not focused in a text field) to open the
**shortcut help dialog**. It lists every shortcut active in the current
context, grouped by scope, resolved for the user's operating system, and with
personalized bindings flagged.
Shortcuts are also surfaced as **visible `kbd` hints** rendered next to
instrumented controls (search field, primary action buttons, relevant menu
items). These hints use OS-native glyph notation (⌘/⌥/⇧/⌃ on macOS;
Ctrl/Alt/Shift on Windows/Linux). The `aria-keyshortcuts` attribute is set on
instrumented controls for screen readers.
## Default Shortcut Reference
### Global (fire anywhere in `/msp`)
| Action | macOS | Windows / Linux |
|--------|-------|-----------------|
| Open command palette | ⌘K | Ctrl+K |
| Open shortcut help dialog | ? | ? |
| Quick-create dialog (multi-type) | N | N |
### Navigation sequences
Press the first key, then the second key within ≈1 second. Sequences are
suppressed while any input, textarea, rich-text editor, or combobox is
focused, and reset on route change.
| Sequence | Destination |
|----------|-------------|
| G → T | Tickets |
| G → C | Clients |
| G → P | Projects |
| G → A | Assets |
| G → S | Schedule |
| G → H | Dashboard (Home) |
### Page-level (fire when the matching page is active)
| Action | Key |
|--------|-----|
| Open current page's create dialog | C |
| Save current page | ⌘S / Ctrl+S |
| Navigate to previous record | `[` |
| Navigate to next record | `]` |
`C` and `N` are suppressed while any editable target is focused and while a
dialog or drawer owns scope.
### Panel / dialog scope
When a drawer or modal dialog is open it captures `Escape` (close/cancel) and
`[`/`]` (previous/next record within the panel). Global and page shortcuts do
not fire while a panel-scope owner is active.
## Customizing Shortcuts
Navigate to **Profile → Keyboard Shortcuts** to manage personal bindings.
| Control | Effect |
|---------|--------|
| Click the binding chip for any action | Capture a new key combination |
| Toggle **Enabled** switch | Disable the shortcut without losing the binding |
| Reset icon on a row | Restore that action's default binding |
| **Reset all** button (confirmation required) | Clear all personal overrides |
Conflict detection runs before commit: if the captured key is already claimed
by another action, the UI prompts for confirmation; the previous owner is
unbound and the new binding takes effect.
Custom bindings are stored per user account and sync across devices. A binding
authored on macOS (e.g. `mod+J`) resolves automatically to Ctrl+J on
Windows/Linux; non-portable combos surface a non-blocking advisory on sign-in
from the other OS.
## Shortcut Profiles
Three preset profiles are available from the Keyboard Shortcuts panel:
| Profile | Description |
|---------|-------------|
| **Default** | Standard bindings optimized for MSP workflows |
| **Vim** | `j`/`k` list navigation and Vim-style action keys |
| **Emacs** | Emacs-inspired modifier-based bindings |
Selecting a profile applies its preset as the binding baseline. Per-action
overrides stack on top of any profile. Per-action reset returns to the active
profile's baseline (not the factory default).
## Scope and Priority Rules
The dispatcher picks the highest-priority registered action that matches the
event's active scope set:
1. **Dialog / panel** — highest priority; owns `Escape` and record-nav keys.
2. **Editor**`editor`-scoped shortcuts (invoice designer, workflow designer,
rich-text) override page shortcuts.
3. **Page** — page-scoped shortcuts fire when the registered page is active.
4. **Global / shell** — lowest priority; fire regardless of current page.
When the same key maps to two actions in the same scope at the same priority,
the conflict is reported in the settings panel and help dialog (it does not
silently pick one).
Single-letter shortcuts (`C`, `N`, `j`, `k`) are always suppressed inside
`<input>`, `<textarea>`, `<select>`, `contenteditable`, and ARIA text/combobox
roots unless the action explicitly sets `allowInEditable`.
## Architecture Overview
The engine lives in `packages/ui/src/keyboard-shortcuts/` and has no imports
of `@alga-psa/user-composition` or any feature package — dependency direction
is enforced by a CI boundary guard (`scripts/guard-keyboard-shortcuts-*.mjs`
and `eslint-plugin-custom-rules`).
| Path | Role |
|------|------|
| `packages/ui/src/keyboard-shortcuts/provider.tsx` | Single delegated capture-phase `keydown` listener; dispatch loop |
| `packages/ui/src/keyboard-shortcuts/catalog.ts` | Single source of truth for action metadata (ids, default bindings, scope, priority) |
| `packages/ui/src/keyboard-shortcuts/parser.ts` | `parseBinding` / `parseSequence` — dual `event.code` + `event.key` matching, `mod` resolution |
| `packages/ui/src/keyboard-shortcuts/preferences.ts` | `ShortcutStorage` adapter interface + preference resolution (`user override → profile delta → platform default`) |
| `packages/ui/src/keyboard-shortcuts/ShortcutHintHud.tsx` | `<Kbd>` / `ShortcutHint` component rendering OS-native glyphs |
| `packages/ui/src/keyboard-shortcuts/command-palette-query.ts` | Command palette query parser (TeamCity-style field scopes + operators) |
| `server/src/components/settings/general/KeyboardShortcutsPanel.tsx` | Profile → Keyboard Shortcuts settings UI |
The provider is mounted in `MspLayoutClient.tsx` (wraps `DefaultLayout` and
`AlgaDeskMspShell`). It does not mount on auth or client-portal pages.
Persistence uses the existing `user_preferences` table via `useUserPreference`
(key `keyboard_shortcuts_v1`); no new tables or API endpoints. The stored blob
is a versioned delta (`{ version, bindings, disabled, profile }`), using
platform-neutral syntax (`mod+k`), with a `v1 → v2` migration that adds the
`profile` field defaulting to `'default'`.
## Coverage and Non-Goals (v1)
The shortcut engine covers all routes under `/msp`. The following are
deliberately out of scope for the initial release:
- Auth screens and the client portal.
- Tenant-level shortcut defaults or admin lock-down.
- Export / import of personal override sets.
- Browser-owned combos (`mod+R`, `mod+F`, `mod+P`, `mod+W`, `mod+T`, `mod+N`)
are not overridden.
- Component-local widget key handling (DatePicker, SearchableSelect, TagInput,
Radix internals) retains its local handlers unchanged.