# Tasks: enterprise-react-hotkeys **Change ID**: enterprise-react-hotkeys **Package**: `@suaveplan/react-hotkeys` **Target path**: `react/ui/react-hotkeys` **Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/react-translate` --- ## 0. Pre-flight (non-negotiable) - [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each) - [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest. ## 1. Scaffold - [ ] Create directory tree per proposal layout. - [ ] Write `package.json` at `0.1.0` with `"sideEffects": false`, exports map. - [ ] Dependencies: `@suaveplan/error workspace:^`, `@suaveplan/react-translate workspace:^`, `zod catalog:forms`, `tslib catalog:build`. - [ ] Write `tsconfig.json`, `vitest.config.ts`, scripts. --- ## 2. Error codes and types - [ ] Define `HotkeyError` extending `SuaveplanError`; codes: `HOTKEY_CONFLICT`, `HOTKEY_INVALID_COMBO`. - [ ] Define all types: `HotkeyCombo` (single token + modifier flags), `HotkeyBinding` (combo string + handler + description), `HotkeyOptions`, `HotkeyRegistryEntry`, `RegistryEntry`. - [ ] Write `HotkeyBindingSchema` Zod schema. --- ## 3. Combo parser (`src/utils/combo-parser/`) - [ ] Implement `parseCombo(raw: string): HotkeyCombo | HotkeyCombo[]` — supports `ctrl+shift+k`, `cmd+k`, `mod+k` (platform-aware), `g g` (sequence as array), `option+/`. - [ ] Implement `matchesCombo(event: KeyboardEvent, combo: HotkeyCombo): boolean`. - [ ] Implement `formatCombo(combo: HotkeyCombo): string` — `⌘K` on mac, `Ctrl+K` on other. - [ ] Implement `isModifierKey(event): boolean`, `isMac(): boolean`. - [ ] Write `combo-parser.test.ts` (≤500 lines): all modifier combos, sequence combos, platform-aware mod, edge cases (`/`, ` ` space). `{ timeout: 5000 }`. - [ ] Write `combo-parser.md`. --- ## 4. Store (`src/store/hotkey-store/`) - [ ] Implement `createHotkeyStore()` — `useSyncExternalStore`-compatible external store holding `Map`. - [ ] `register(scope, entry): () => void` — adds entry, returns unsubscribe; throws `HotkeyError(HOTKEY_CONFLICT)` if same scope+combo already registered. - [ ] `getEntries(scope): HotkeyRegistryEntry[]`, `subscribe(listener): () => void`. - [ ] Sequence buffer: tracks last N keydowns; matches against sequence combos. - [ ] Write `hotkey-store.test.ts`, `hotkey-store.md`. --- ## 5. Hooks ### `useHotkey` - [ ] Registers single combo; installs `keydown` listener on `document` (SSR-safe via `useEffect`). - [ ] `prevent-in-input` guard. - [ ] Returns `{ isPressed: boolean }`. - [ ] Write test (combo fires, in-input blocked, SSR-safe), stories (playground toggle), md. ### `useHotkeys` - [ ] Bulk-registers `HotkeyBinding[]`; detects conflicts via store and emits `HotkeyError(HOTKEY_CONFLICT)` (non-throwing — logs via `console.warn` in dev, captures OTel event). - [ ] Write test, md. ### `useHotkeyRegistry` - [ ] Subscribes to store via `useSyncExternalStore`; returns current entries for active scope. - [ ] Write test (mount/unmount triggers re-render), md. --- ## 6. Components ### HotkeyScope - [ ] Provides scope name via context; routes events only when scope is active. - [ ] Write test, stories (scoped-routing — two scopes, only active one fires), md. ### HotkeyCheatSheet - [ ] Renders grouped registry entries (scope → table of combo + description). - [ ] Uses `useTranslation` for chrome strings ("Keyboard Shortcuts", "Description", "Shortcut"). - [ ] Re-renders live when entries change (via `useHotkeyRegistry`). - [ ] Write test, stories (default, live-update on mount, playground), md. --- ## 7. E2E - [ ] `e2e/hotkeys.spec.ts` — Playwright: - Press `ctrl+k` (or `meta+k` on mac), verify handler fires. - Press `g g` sequence, verify handler fires. - Focus ``, press hotkey, verify handler does NOT fire. - Switch `HotkeyScope`, verify only active scope responds. --- ## 8. Verify and publish - [ ] `bunx turbo lint typecheck test --filter=@suaveplan/react-hotkeys` — green. - [ ] `bunx turbo test:coverage --filter=@suaveplan/react-hotkeys` — 100%. - [ ] `bun scripts/publish.ts --package @suaveplan/react-hotkeys`. - [ ] Archive change folder.