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
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
173 lines
7.6 KiB
Markdown
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.
|