Skip to content

feat(keyboard): leader-sequence runtime in the keymap dispatcher (RIG-2709) - #616

Open
rigel-mintaka wants to merge 1 commit into
compass-ux/rig-2708-leader-sequence-rowsfrom
compass-ux/rig-2709-leader-runtime
Open

feat(keyboard): leader-sequence runtime in the keymap dispatcher (RIG-2709)#616
rigel-mintaka wants to merge 1 commit into
compass-ux/rig-2708-leader-sequence-rowsfrom
compass-ux/rig-2709-leader-runtime

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 4 PRs:

  1. main
  2. feat(keyboard): sequence-grammar chord helpers for leader chords (RIG-2707) #612
  3. feat(keyboard): four G-leader sequence rows + display/aria hardening (RIG-2708) #613
  4. "feat(keyboard): leader-sequence runtime in the keymap dispatcher (RIG-2709)" (this PR)
  5. docs(keyboard): document the leader-sequence runtime in the dispatch header (RIG-2710) #619

Arm-and-complete "press G, then " sequences inside the one keydown
handler installKeymap already owns — no second listener, no module global.

  • Per-install pending leader state + a disarm() that clears the live
    timeout; leader set is leaderPrefixes(DEFAULT_KEYMAP, platform), computed
    once at install (table-derived, never hard-coded).
  • Per-keydown order (§A3): normalize → editable-guard FIRST (modifier-less
    keys only) → completion (pure-modifier keys ignored, Escape disarms+consumes,
    else disarm then resolve the "<leader> <chord>" sequence rows; a dead
    sequence falls through and re-enters arming) → arming (leader prefix,
    no command modifier, not event.repeat; preventDefault+stopPropagation) →
    single-chord path (unchanged).
  • Tier resolution factored into a resolve(matching, event) closure so a
    completed sequence and a single chord run byte-identical (active group →
    scoped → global, scope gate intact).
  • isEditableTarget extended to HTMLSelectElement and
    [role=combobox|listbox|menu] so a bare leader key never steals native
    <select> typeahead or an ARIA widget's keys.
  • Uninstaller clears any live disarm timer (no leak against a torn-down
    closure on test teardown / HMR).
  • LEADER_TIMEOUT_MS = 1000 (OQ1 ratified) exported for runtime + tests.

Also folds the two deferred RIG-2707 review lows into leaderPrefixes's unit
coverage (first runtime caller): per-platform resolution of a Mod leader
segment, and distinct-leader accumulation with same-leader dedup.

Tests: dispatch.test.ts + keyboard-e2e.test.tsx (51 pass) cover arm/complete,
timeout disarm, editable + <select> + ARIA-widget guards, dead-sequence
fall-through, re-arm, Escape, lone-Shift-does-not-disarm, Mod+B-mid-sequence
disarm-and-resolve, held-key no-arm, and uninstall-clears-timer; the two e2e
cases drive G S/G L through real App wiring. keymap.test.ts 20 pass.

Ledger-impact: none (decisions landed with the design PR #544; DL-248..252).

RIG-2709

@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

RIG-2709

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-ux-rig-2709-leader-r.compass-eng-docs.pages.dev

Deployed from compass-ux/rig-2709-leader-runtime at 761ad0f.

…-2709)

Arm-and-complete "press G, then <key>" sequences inside the one `keydown`
handler `installKeymap` already owns — no second listener, no module global.

- Per-install `pending` leader state + a `disarm()` that clears the live
  timeout; leader set is `leaderPrefixes(DEFAULT_KEYMAP, platform)`, computed
  once at install (table-derived, never hard-coded).
- Per-keydown order (§A3): normalize → editable-guard FIRST (modifier-less
  keys only) → completion (pure-modifier keys ignored, Escape disarms+consumes,
  else disarm then resolve the `"<leader> <chord>"` sequence rows; a dead
  sequence falls through and re-enters arming) → arming (leader prefix,
  no command modifier, not `event.repeat`; preventDefault+stopPropagation) →
  single-chord path (unchanged).
- Tier resolution factored into a `resolve(matching, event)` closure so a
  completed sequence and a single chord run byte-identical (active group →
  scoped → global, scope gate intact).
- `isEditableTarget` extended to `HTMLSelectElement` and
  `[role=combobox|listbox|menu]` so a bare leader key never steals native
  `<select>` typeahead or an ARIA widget's keys.
- Uninstaller clears any live disarm timer (no leak against a torn-down
  closure on test teardown / HMR).
- `LEADER_TIMEOUT_MS = 1000` (OQ1 ratified) exported for runtime + tests.

Also folds the two deferred RIG-2707 review lows into `leaderPrefixes`'s unit
coverage (first runtime caller): per-platform resolution of a `Mod` leader
segment, and distinct-leader accumulation with same-leader dedup.

Tests: dispatch.test.ts + keyboard-e2e.test.tsx (51 pass) cover arm/complete,
timeout disarm, editable + `<select>` + ARIA-widget guards, dead-sequence
fall-through, re-arm, Escape, lone-Shift-does-not-disarm, Mod+B-mid-sequence
disarm-and-resolve, held-key no-arm, and uninstall-clears-timer; the two e2e
cases drive `G S`/`G L` through real App wiring. keymap.test.ts 20 pass.

Ledger-impact: none (decisions landed with the design PR #544; DL-248..252).

RIG-2709
@rigel-mintaka
rigel-mintaka force-pushed the compass-ux/rig-2709-leader-runtime branch from c956910 to 761ad0f Compare August 25, 2026 18:25
@rigel-mintaka
rigel-mintaka marked this pull request as ready for review August 25, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant