Skip to content

feat: repeat navigation keys when the desktop supports it - #141

Merged
enaboapps merged 6 commits into
mainfrom
codex/key-repeat-140
Sep 8, 2026
Merged

feat: repeat navigation keys when the desktop supports it#141
enaboapps merged 6 commits into
mainfrom
codex/key-repeat-140

Conversation

@enaboapps

@enaboapps enaboapps commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Navigation keys repeat through the existing repeat envelope when the desktop advertises keyRepeat support. Older desktops and unsupported keys retain single-key behavior. Repeat status and stop controls distinguish key repeats from pointer movement.

Modifier changes and commands on an existing typing stream stop the active repeat and clear Remote state and Android switch capture before sending input. This prevents the next navigation-key activation from being swallowed. Stream text, stream keys, and stream close use the repeat queue so pending repeat starts finish before cleanup.

Validation on Node.js 24.13.0:

  • npm run validate passed: version metadata, ESLint, TypeScript, 64 suites / 550 tests, and Expo Doctor 21/21.
  • Regression coverage includes modifier press/release, resuming an open stream with text or a key, closing that stream, and pending repeat-start acknowledgements. Tests use fake command and bridge adapters.
  • Independent review passed on head 990b1ac with no actionable findings; the reviewer also passed 95 focused tests. GitHub CI passed: validate, Android assembleDebug, and the iOS simulator build.

Closes #140

Lets the remote repeat the arrow keys, Tab, Backspace, Delete, Page Up and
Page Down, matching how mouse move and scroll repeat already work.

Key repeat rides the existing mouse.repeat.start nested envelope rather
than a new command name, so the repeat queue, bridge arming, physical
switch stop and cleanup all work unchanged and mouse.repeat.stop remains
the single stop for every repeat kind.

Gating is capability driven. A desktop that does not advertise keyRepeat
parses as unsupported and the remote sends a single keyboard.key press, so
an older PC build needs no special case. The same fallback covers key
repeat disabled on the desktop and keys outside the advertised allowlist.

Keys in live typing keep using the text stream so chunk sequencing is
preserved, and are never repeated.

Closes #140
…ht stop control

Addresses the independent review of this branch.

Mirroring the pointer path made any key press while a repeat was active
stop the repeat and return without sending the key. For a repeatable key
that is the intended toggle, but for Escape, Enter, or any key on a
desktop that cannot repeat keys at all it was silent input loss, costing
the user a second activation. A key that cannot repeat is now delivered
after the repeat stops.

The blocked-editing explanation on all three surfaces still told the user
to "Stop movement" during a key repeat, when the only control on screen is
"Stop repeating". Both now come from one shared repeatStopLabel helper so
they cannot drift apart.

The Android physical-switch warning now also requires the repeat commands,
not just the capability flags, so it cannot appear when no repeat could
start.

Test fixtures advertised key repeat unconditionally, which quietly turned
the action-routing test into a repeat-toggling test and left one fixture
claiming key repeat while mouse repeat was unsupported. Key repeat is now
opt-in per fixture, with new coverage for repeatable-key routing, live
typing still using the stream, and the delivery fix.
… allowlist locally

Addresses the second independent review of this branch.

The previous fix keyed off "is this key repeatable" rather than "is the
active repeat this key's own repeat", so a repeatable key pressed during a
pointer repeat, or while a different key repeated, was still dropped -
input that main delivered. The session now tracks which key is repeating
and swallows the press only when it is that key's own toggle; every other
case stops the repeat and still delivers the key.

The advertised repeatableKeys list is now intersected with a local set
rather than trusted outright. Received data is untrusted, and a desktop
advertising Enter would otherwise make the Enter control re-submit on
every tick, contradicting the documented guarantee.

Adds the coverage the review found missing: the blocked-editing
explanation naming the on-screen stop control, and the physical-switch
warning being withheld when no repeat could start. Both were verified to
fail when their fix is reverted.

Also updates the physical smoke test and the two accessibility lines that
still named Stop movement as the only repeat stop control.
The previous commit added a local REPEATABLE_KEYS intersection but left
protocol-compatibility.md claiming a desktop can widen the advertised list
without a Remote release. A PC release adding Home or End would silently
send single presses with nothing in the doc explaining why. The paragraph
now describes the client cap, what a desktop can still change unilaterally,
and adds the fourth fallback cause to the skew list.

Also records that Delete, PageUp and PageDown are repeatable but have no
Remote action yet, so nobody hunts the picker for a control that does not
exist, and narrows the accessibility claim about the blocked-editing text,
which keeps its Stop movement wording when dragging or held modifiers
block editing without a repeat.
@enaboapps

Copy link
Copy Markdown
Contributor Author

@-

@enaboapps
enaboapps marked this pull request as ready for review September 7, 2026 23:02
@enaboapps
enaboapps merged commit 334308f into main Sep 8, 2026
3 checks passed
@enaboapps enaboapps mentioned this pull request Sep 8, 2026
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.

Repeat navigation keys when the desktop advertises keyRepeat

2 participants