Skip to content

Don't arm the _keyDownSeen gate on modifier-only keydowns - #6200

Open
52mzd wants to merge 1 commit into
xtermjs:masterfrom
52mzd:fix/modifier-keydown-keydownseen-gate
Open

52mzd wants to merge 1 commit into
xtermjs:masterfrom
52mzd:fix/modifier-keydown-keydownseen-gate

Conversation

@52mzd

@52mzd 52mzd commented Oct 2, 2026

Copy link
Copy Markdown

Problem

On macOS, typing an IME's shifted punctuation with a direct commit — e.g. Shift+/ for the full-width ? with Chinese Pinyin — silently drops the first press; the user has to type the key twice. Reproduced in Safari and in WKWebView (Tauri), reported as the held-modifier variant in #5887 (Zhuyin, Tauri 2 + WKWebView), #6144 (Pinyin, order beforeinput → input → keydown) and #5374 (JP layout, Shift+3 / Shift+2).

Real-device trace captured on macOS 26 (WKWebView, Tauri, Apple Pinyin), from capture-phase listeners on the helper textarea — _keyDownSeen read before xterm's own handling:

keydown   key=Shift  kc=16     ← arms _keyDownSeen = true
beforeinput data=":"          ← IME commits before the character keydown
input     data=":"  seen=1    ← dropped by (!ev.composed || !this._keyDownSeen)
keydown   key=:    kc=229     ← composition diff fallback sees the commit already
                                applied to the textarea → empty diff, not recovered
keyup     key=:    kc=186     ← resets _keyDownSeen
input     data=":"  seen=0    ← second press now passes the gate

So only the first press is lost: the held modifier's keydown arms the gate, the character's keyup disarms it.

Fix

_keyDownSeen's only consumer is the _inputEvent dedupe guard. A modifier-only keydown never produces text, so arming the gate for it can only produce false positives — an insertText arriving while a modifier is held can only be an IME direct commit that no keydown delivered yet. The fix reuses wasModifierKeyOnlyEvent, the same helper _keyUp already uses:

this._keyDownSeen = !wasModifierKeyOnlyEvent(event);

The regular path is unchanged: a character keydown still arms the gate so its own insertText stays deduped (covered by a test).

Scope / not claimed to fix

🤖 Generated with Claude Code

On macOS, an IME's direct-commit of a shifted character (e.g. the
full-width ? from Shift+/ with Chinese Pinyin) can deliver
beforeinput/input before the character's own keydown, in Safari and
WKWebView alike. Because the held modifier's keydown had armed the
_keyDownSeen gate, _inputEvent wrongly dedupes and drops the first
insertText; the character's keyup then resets the flag, so only the
first press is lost and the user has to type the key twice.

A modifier-only keydown never produces text, so it must not arm the
gate. Reuse wasModifierKeyOnlyEvent (the same helper _keyUp uses) so
the flag stays disarmed while Shift/Ctrl/Alt/Meta is held.

Co-Authored-By: Claude Code <noreply@anthropic.com>
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