Skip to content

Share one dual-tap detector between the Wall and the iframe shim - #1372

Merged
nedtwigg merged 2 commits into
mainfrom
fix/iframe-shim-leader-reset
Oct 27, 2026
Merged

nedtwigg merged 2 commits into
mainfrom
fix/iframe-shim-leader-reset

Conversation

@dormouse-bot

@dormouse-bot dormouse-bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Typing in a proxied iframe Surface could drop the pane out of passthrough. The injected shim posts leader on a left-then-right Shift (or Meta) tap within 500 ms, but its detector never cleared a pending tap when another key came in between. So typing "I Am" into a text field in the frame (left Shift for the I, right Shift for the A) read as the chord, and the Wall called exitTerminalMode() mid-sentence. The Wall's own detector already reset on any other key; the shim was a hand copy that had drifted.

The root fix is one detector. dualTap in lib/src/lib/dual-tap.ts owns the whole rule: left-then-right on the same modifier under 500 ms, and any other key cancels a pending tap.

  • The Wall calls it once per keydown at the top of dispatch in use-wall-keyboard.ts, before the terminal-context branch, so context keys still cancel. handleDualTap shrinks to exiting passthrough on a completed gesture and consuming bare Meta/Shift; the four refs and DualTapState in keyboard/types.ts are gone.
  • The shim embeds the function's source (var dualTap=${dualTap}), bound by position because the website's Vite build minifies it and can rename the function. dual-tap.ts documents the self-containment constraint this puts on it.

Behaviour change: the other modifier now cancels a pending tap too. Left Shift, left Meta, right Shift used to fire because the two modifiers had independent tracks; it no longer does. docs/specs/layout.md → "Mode switching" is updated to match.

Verification:

  • lib/src/lib/dual-tap.test.ts runs one table of key sequences (the gesture, the 500 ms boundary, right-then-left, mixed modifiers, "I Am", the other modifier between taps) twice: against the function, and against its source evaluated in an empty node:vm context, so a closure over module scope fails.
  • The shim test in iframe-proxy-rewrite.test.ts drives the shipped shim's keydown listener.
  • I also built iframe-proxy-rewrite.ts with esbuild --minify --keep-names and ran the resulting shim: the function came out renamed and still posted one leader for a clean ⇧⇧ and none for "⇧ I ⇧".
  • The lib suite for the touched areas (1252 tests), tsc -b, lint:specs, and lint:md pass locally.

🤖 Generated with Claude Code

The shim's dual-tap detector never reset between Shift or Meta taps, so
typing a left-Shift capital, a word, then a right-Shift capital inside a
proxied iframe posted 'leader' and dropped the pane out of passthrough.
The Wall's own detector already resets on any other key.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: bbd3626
Status: ✅  Deploy successful!
Preview URL: https://84388cc2.mouseterm.pages.dev
Branch Preview URL: https://fix-iframe-shim-leader-reset.mouseterm.pages.dev

View logs

The shim hand-copied the Wall's left-then-right detector and drifted: it
never cancelled a pending tap. `dualTap` in lib/src/lib/dual-tap.ts now
owns the whole rule, the Wall calls it on every keydown, and the shim
embeds its source. A test runs it from source in an empty context so it
stays self-contained.

The other modifier now cancels a pending tap too: left Shift, left Meta,
right Shift is no longer the gesture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg nedtwigg changed the title Cancel the iframe shim's pending leader tap when another key is pressed Share one dual-tap detector between the Wall and the iframe shim Oct 27, 2026
@nedtwigg
nedtwigg merged commit ce00640 into main Oct 27, 2026
17 checks passed
@nedtwigg
nedtwigg deleted the fix/iframe-shim-leader-reset branch October 27, 2026 18:27
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.

2 participants