Jelajahi Sumber

update specs

Yuki Takei 1 bulan lalu
induk
melakukan
8916986535
3 mengubah file dengan 100 tambahan dan 15 penghapusan
  1. 60 8
      .kiro/specs/hotkeys/design.md
  2. 4 4
      .kiro/specs/hotkeys/spec.json
  3. 36 3
      .kiro/specs/hotkeys/tasks.md

+ 60 - 8
.kiro/specs/hotkeys/design.md

@@ -41,16 +41,68 @@ BasicLayout / AdminLayout
 
 **Trade-off**: tinykeys has no React integration — key binding is done imperatively in a `useEffect` hook rather than declaratively via JSX props. This is acceptable given the simplicity of the binding map.
 
-### D2: Centralized Binding Map
+### D2: Subscriber-Owned Binding Definitions
 
-**Decision**: All key bindings are defined inline in `HotkeysManager.tsx` rather than distributed across subscriber components.
+**Decision**: Each subscriber component exports its own `hotkeyBindings` metadata alongside its React component. `HotkeysManager` imports these definitions and auto-builds the tinykeys binding map — it never hardcodes specific keys or subscriber references.
 
 **Rationale**:
-- Eliminates the need for `getHotkeyStrokes()` static methods on each subscriber
-- Removes the `HotkeysDetector` intermediary layer
-- All bindings are visible in one place, making the mapping easy to audit
+- True "1 module = 1 hotkey" encapsulation: each subscriber owns its key binding, handler category, and action logic
+- Adding a new hotkey requires creating only one file (the new subscriber); `HotkeysManager` needs no modification
+- Fully satisfies Req 7 AC 2 ("define hotkey without modifying core detection logic")
+- Self-documenting: looking at a subscriber file tells you everything about that hotkey
+
+**Type contract**:
+```typescript
+// Shared type definition in HotkeysManager.tsx or a shared types file
+type HotkeyCategory = 'single' | 'modifier';
+
+type HotkeyBindingDef = {
+  keys: string | string[];   // tinykeys key expression(s)
+  category: HotkeyCategory;  // determines handler wrapper (single = input guard, modifier = no guard)
+};
+
+type HotkeySubscriber = {
+  component: React.ComponentType<{ onDeleteRender: () => void }>;
+  bindings: HotkeyBindingDef;
+};
+```
+
+**Subscriber example**:
+```typescript
+// CreatePage.tsx
+export const hotkeyBindings: HotkeyBindingDef = {
+  keys: 'c',
+  category: 'single',
+};
+
+export const CreatePage = ({ onDeleteRender }: Props): null => { /* ... */ };
+```
+
+```typescript
+// ShowShortcutsModal.tsx
+export const hotkeyBindings: HotkeyBindingDef = {
+  keys: ['Control+/', 'Meta+/'],
+  category: 'modifier',
+};
+```
+
+**HotkeysManager usage**:
+```typescript
+// HotkeysManager.tsx
+import * as createPage from './Subscribers/CreatePage';
+import * as editPage from './Subscribers/EditPage';
+// ... other subscribers
+
+const subscribers: HotkeySubscriber[] = [
+  { component: createPage.CreatePage, bindings: createPage.hotkeyBindings },
+  { component: editPage.EditPage, bindings: editPage.hotkeyBindings },
+  // ...
+];
+
+// In useEffect: iterate subscribers to build tinykeys binding map
+```
 
-**Trade-off**: Adding a new hotkey requires editing `HotkeysManager.tsx` (violates Req 7 AC 2's ideal of zero-touch core). This is an acceptable trade-off — the binding map is a simple object literal and changes are trivial.
+**Trade-off**: Slightly more structure than a plain object literal, but the pattern is minimal and each subscriber file is fully self-contained.
 
 ### D3: Subscriber Render-on-Fire Pattern
 
@@ -83,10 +135,10 @@ BasicLayout / AdminLayout
 
 | Requirement | Deviation | Justification |
 |-------------|-----------|---------------|
-| Req 7 AC 2: "define hotkey without modifying core detection logic" | Adding a new hotkey requires editing HotkeysManager.tsx's binding map | Binding map is a trivial object literal; the simplification from removing HotkeysDetector + getHotkeyStrokes outweighs the minor editing cost |
-| Req 8 AC 2: "export typed interfaces for hotkey definitions" | `SubscriberComponent` type is internal only, not exported | No external consumers need the type; exporting it would be unnecessary API surface |
+| Req 8 AC 2: "export typed interfaces for hotkey definitions" | `HotkeyBindingDef` and `HotkeySubscriber` types are exported for subscriber use but not published as a package API | These types are internal to the Hotkeys module; no external consumers need them |
 
 > **Note (task 5)**: Req 8 AC 1 is now fully satisfied — all 6 subscriber components converted from `.jsx` to `.tsx` with TypeScript `Props` types and named exports.
+> **Note (D2 revision)**: Req 7 AC 2 is now fully satisfied — subscriber-owned binding definitions mean adding a hotkey requires only creating a new subscriber file.
 
 ## Key Binding Format (tinykeys)
 

+ 4 - 4
.kiro/specs/hotkeys/spec.json

@@ -1,9 +1,9 @@
 {
   "feature_name": "hotkeys",
   "created_at": "2026-02-20T00:00:00.000Z",
-  "updated_at": "2026-02-24T05:35:00.000Z",
+  "updated_at": "2026-02-24T06:00:00.000Z",
   "language": "en",
-  "phase": "implementation-complete",
+  "phase": "tasks-generated",
   "approvals": {
     "requirements": {
       "generated": true,
@@ -15,9 +15,9 @@
     },
     "tasks": {
       "generated": true,
-      "approved": true
+      "approved": false
     }
   },
   "ready_for_implementation": true,
-  "cleanup_completed": true
+  "cleanup_completed": false
 }

+ 36 - 3
.kiro/specs/hotkeys/tasks.md

@@ -1,8 +1,6 @@
 # Implementation Tasks
 
-All tasks completed as part of `reduce-modules-loaded` spec (iteration 8.7).
-
-## Tasks
+## Completed Tasks (tinykeys migration — iteration 8.7)
 
 - [x] 1. Write HotkeysManager tests
   - Created `HotkeysManager.spec.tsx` with 6 tests covering single-key triggers, modifier-key triggers, and input element suppression
@@ -41,3 +39,38 @@ All tasks completed as part of `reduce-modules-loaded` spec (iteration 8.7).
   - Converted all 6 subscribers from default exports to named exports; updated HotkeysManager imports
   - Tests: 6/6 pass, lint:typecheck: pass, lint:biome: pass
   - _Requirements: 7, 8_
+
+## New Tasks (D2 revision — subscriber-owned binding definitions)
+
+- [ ] 6. Refactor hotkey bindings to subscriber-owned definitions
+- [ ] 6.1 Define shared hotkey binding types and add binding exports to all subscribers
+  - Define types for hotkey category (single vs modifier) and binding metadata (keys + category)
+  - Each of the six subscriber components exports its own binding definition alongside its component
+  - Single-key subscribers (c, e, /) declare category 'single'; modifier and sequence subscribers (Ctrl+/, Konami codes) declare category 'modifier'
+  - Binding definitions use tinykeys key format; subscribers with multiple key expressions (e.g. Control+/ and Meta+/) use an array
+  - _Requirements: 7, 8_
+
+- [ ] 6.2 Refactor HotkeysManager to build binding map from subscriber exports
+  - Replace inline key-to-component mapping with dynamic iteration over imported subscriber binding definitions
+  - Apply handler wrapper (input guard for 'single' category, pass-through for 'modifier') based on each subscriber's declared category
+  - HotkeysManager becomes a generic orchestrator with no hardcoded key knowledge — adding a new hotkey requires only creating a new subscriber file
+  - Preserve cleanup via tinykeys unsubscribe in useEffect return
+  - _Requirements: 6, 7_
+
+- [ ] 7. Verify refactoring preserves all existing behavior
+  - Confirm all existing tests pass without modification (behavior is unchanged, only internal structure changed)
+  - Run lint:typecheck, lint:biome, and test suites
+  - _Requirements: 1, 2, 3, 4, 5_
+
+## Requirements Coverage
+
+| Requirement | Tasks |
+|-------------|-------|
+| 1. Replace react-hotkeys with tinykeys | 2, 3, 4, 7 |
+| 2. Preserve single-key shortcuts | 1, 2, 7 |
+| 3. Preserve modifier-key shortcuts | 1, 2, 7 |
+| 4. Preserve multi-key sequences | 2, 7 |
+| 5. Input element focus guard | 1, 2, 7 |
+| 6. Lifecycle management and cleanup | 2, 6.2 |
+| 7. Subscriber component architecture | 3, 5, 6.1, 6.2 |
+| 8. TypeScript migration | 2, 5, 6.1 |