97 lines
2.7 KiB
Markdown
97 lines
2.7 KiB
Markdown
|
|
# Keyboard Integration as Shared Input, Not as Gesture
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
Keyboard is not a `GestureType`.
|
||
|
|
|
||
|
|
v2 treats pointer gestures and keyboard events as parallel input streams that share:
|
||
|
|
|
||
|
|
- one context stack
|
||
|
|
- one resolver
|
||
|
|
- one action dispatcher
|
||
|
|
- one provider-owned keyboard listener per active canvas
|
||
|
|
|
||
|
|
## Runtime Shape
|
||
|
|
|
||
|
|
- Pointer stays gesture-native:
|
||
|
|
- `tap`
|
||
|
|
- `double-tap`
|
||
|
|
- `triple-tap`
|
||
|
|
- `long-press`
|
||
|
|
- `drag`
|
||
|
|
- `pinch`
|
||
|
|
- `scroll`
|
||
|
|
- Keyboard uses a separate event shape:
|
||
|
|
- `kind: 'key'`
|
||
|
|
- `phase: 'down' | 'up'`
|
||
|
|
- `key`
|
||
|
|
- `code`
|
||
|
|
- `repeat`
|
||
|
|
- Shared runtime event union:
|
||
|
|
- `PointerGestureEvent`
|
||
|
|
- `KeyInputEvent`
|
||
|
|
- `InputEvent`
|
||
|
|
- Held keyboard state is explicit:
|
||
|
|
- `heldKeys.byKey`
|
||
|
|
- `heldKeys.byCode`
|
||
|
|
- Pointer events carry a held-key snapshot so bindings like `Space+drag` are modeled as pointer gestures with keyboard context.
|
||
|
|
|
||
|
|
## Resolver Rules
|
||
|
|
|
||
|
|
- Pointer patterns still match pointer gesture types.
|
||
|
|
- Keyboard patterns match `phase`, `key`, and/or `code`.
|
||
|
|
- `key` score: `+64`
|
||
|
|
- `code` score: `+64`
|
||
|
|
- held-key positive score: `+16`
|
||
|
|
- held-key negative score: `+8`
|
||
|
|
- If both `key` and `code` are present, both must match.
|
||
|
|
|
||
|
|
## Ownership
|
||
|
|
|
||
|
|
- `InputProvider` is the single keyboard owner for the active canvas.
|
||
|
|
- The last canvas root to receive pointer interaction or focus becomes keyboard-active.
|
||
|
|
- Keyboard routing is suppressed when:
|
||
|
|
- target is `input`
|
||
|
|
- target is `textarea`
|
||
|
|
- target is `contentEditable`
|
||
|
|
- IME composition is active
|
||
|
|
- Held keys clear on `blur` and `visibilitychange`.
|
||
|
|
|
||
|
|
## Default Behavior
|
||
|
|
|
||
|
|
- `/` opens command line
|
||
|
|
- `Cmd/Ctrl+F` opens search
|
||
|
|
- `Cmd/Ctrl+C/X/V/D` route through shared input actions
|
||
|
|
- `Cmd/Ctrl+Z`, `Cmd/Ctrl+Shift+Z`, `Cmd/Ctrl+Y` route through shared input actions
|
||
|
|
- `Delete` / `Backspace` delete current selection
|
||
|
|
- `Escape` precedence:
|
||
|
|
1. cancel active interaction
|
||
|
|
2. exit keyboard manipulate mode
|
||
|
|
3. clear search / selection / close palette
|
||
|
|
- `keyboardInteractionMode` is explicit:
|
||
|
|
- `'navigate'`
|
||
|
|
- `'manipulate'`
|
||
|
|
- Navigate mode:
|
||
|
|
- arrows move focus
|
||
|
|
- `Enter` selects and enters manipulate mode
|
||
|
|
- `Space` activates focused node
|
||
|
|
- `Tab` cycles focus
|
||
|
|
- Manipulate mode:
|
||
|
|
- arrows nudge selection by `10`
|
||
|
|
- `Shift+Arrow` nudges by `50`
|
||
|
|
- `Escape` returns to navigate mode
|
||
|
|
|
||
|
|
## Cutoff
|
||
|
|
|
||
|
|
- Remove keyboard ownership from:
|
||
|
|
- `packages/canvas/src/commands/keyboard.ts`
|
||
|
|
- `packages/canvas/src/hooks/useCanvasHistory.ts`
|
||
|
|
- `packages/canvas/src/hooks/useArrowKeyNavigation.ts`
|
||
|
|
- Keep those exports as compatibility shims only if needed for callers during the repo migration.
|
||
|
|
|
||
|
|
## Verification
|
||
|
|
|
||
|
|
- resolver tests for key/code/held-key matching
|
||
|
|
- runtime tests for `Space+drag`, `Escape`, copy/paste, undo/redo, and active-canvas ownership
|
||
|
|
- behavior tests for navigate vs manipulate mode
|