Skip to content

fix(terminal): stop losing fast IME-mode English input - #1276

Open
jt-wang wants to merge 2 commits into
TraderAlice:devfrom
jt-wang:contrib/terminal-ime-keyboard
Open

fix(terminal): stop losing fast IME-mode English input#1276
jt-wang wants to merge 2 commits into
TraderAlice:devfrom
jt-wang:contrib/terminal-ime-keyboard

Conversation

@jt-wang

@jt-wang jt-wang commented Aug 31, 2026

Copy link
Copy Markdown

Context — With a CJK IME enabled on macOS (even its ASCII/English mode), fast English typing in the workspace terminal silently drops characters: what's the next step's the next step (Chromium); it doesn't workitdoen't wor (Safari, reproduced byte-for-byte from a captured user trace). Slow typing is unaffected, and IME-committed CJK strings bypass the path entirely — which is why only fast English typing appears broken.

Why — Two root causes in two layers, one commit each.

  1. keyCode-229 keydowns were claimed by the native-text forwarder, which cleared xterm's CompositionHelper textarea and destroyed the pending diff baseline. Fast typing lost whole letter runs, and a clear landing on a non-empty baseline emitted a spurious DEL that deleted the just-forwarded character. Those keydowns now stay with xterm's IME machinery; the claim remains for non-IME keydowns, where xterm would otherwise miss the IME's replacement text.
  2. Safari dispatches textInput/beforeinput/input before the corresponding keydown under an IME, and xterm's _inputEvent guarded on (!ev.composed || !this._keyDownSeen) — a Chromium-order heuristic — so every input event landing between a keydown and the previous keyup was dropped. The bundled xterm patch replaces the ordering heuristic with precise guards: accept non-composed insertText (composition text still flows through CompositionHelper), keep the existing _keyPressHandled dedupe, and cancel the pending 229 textarea-diff when the input event delivers the text directly.

Verified: bare-xterm A/B on the captured trace (drops before, complete after, on both WebKit and Chromium); full-stack replay through WS/PTY/pi; clean-typing and IME-229 regression matrices; new 7-test spec for the forwarder; monorepo suite green.

Reviewer action — Target dev per CONTRIBUTING.md. The two commits are independent and can land in either order. Quickest repro: macOS → enable a Chinese IME → switch to its English mode → type fast in the terminal.

jt-wang and others added 2 commits August 31, 2026 23:27
With a Chinese IME active on macOS, Chromium reports every keydown with
keyCode 229 (keeping the real character in `key`) — including the IME's
ASCII/English mode. xterm routes 229 keydowns through CompositionHelper's
textarea-diff path, which batches pending input behind a single 0ms timer
over the shared helper textarea.

The forwarder still claimed ASCII/CJK punctuation among those keydowns and
cleared that same textarea after forwarding, destroying the pending diff
baseline. Fast typing then lost whole letter runs ("what's the next step"
-> "'s the next step"), and a clear landing on a non-empty baseline made
the diff emit a spurious DEL that deleted the just-forwarded character —
the "cannot type ?" symptom. Slow typing let the timer drain first, and
IME-committed CJK strings bypass the path entirely, which is why only
fast English typing appeared broken.

keyCode 229 keydowns now stay with xterm's IME machinery, whose diff
forwards the inserted text (including full-width punctuation) verbatim.
The claim remains for non-IME keydowns, where xterm would otherwise
synthesize the raw ASCII byte and miss the IME's replacement text.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Safari dispatches textInput/beforeinput/input BEFORE the corresponding
keydown while a macOS IME is active (including IME English modes), and
overlapping keystrokes keep the previous key held when the next key's
input event arrives. xterm's _inputEvent guarded on
(!ev.composed || !this._keyDownSeen), a Chromium-order heuristic, so
every input event landing between a keydown and the previous keyup was
silently dropped — reproduced byte-for-byte from a user-captured Safari
trace ("it doesn't work" -> "itdoen't wor", losing exactly the rollover
chars) in a bare patched xterm on both WebKit and Chromium.

The patch replaces the ordering heuristic with precise guards: accept
non-composed insertText (composition text still flows through
CompositionHelper), keep the existing _keyPressHandled dedupe, and
cancel CompositionHelper's pending 229 textarea-diff when the input
event delivers the text directly — Chromium IME streams now emit
per-character sends instead of batched diffs, with no double-send.

Verified: bare-xterm A/B on the captured trace (drops before, complete
after, both engines); full-stack replay through WS/PTY/pi; clean-typing
and IME-229 regression matrices; monorepo suite green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

@jt-wang is attempting to deploy a commit to the luokerenx4's Team Team on Vercel.

A member of the Team first needs to authorize it.

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