107 lines
4.6 KiB
Markdown
107 lines
4.6 KiB
Markdown
# 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<scope, HotkeyRegistryEntry[]>`.
|
|
- [ ] `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 `<input>`, 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.
|