Skip to content

fix(@wterm/dom): don't leak first IME composition key to PTY#79

Open
vmxmy wants to merge 1 commit into
vercel-labs:mainfrom
vmxmy:fix-ime-first-key
Open

fix(@wterm/dom): don't leak first IME composition key to PTY#79
vmxmy wants to merge 1 commit into
vercel-labs:mainfrom
vmxmy:fix-ime-first-key

Conversation

@vmxmy
Copy link
Copy Markdown

@vmxmy vmxmy commented May 23, 2026

Problem

When using an IME (e.g. Chinese pinyin) inside a wterm-backed terminal, the
first key that triggers an IME composition is forwarded to the PTY as
a normal character — before compositionstart fires and flips
InputHandler.composing to true.

Concrete example with the Vite + @wterm/ghostty example:
typing pinyin for 如何, the PTY receives r你何 or r如何 (a stray r
ahead of the committed text) instead of just 如何.

This is presumably a contributor to #70 ("IME tentative input doesn't appear
all"), though that issue is also about IME window positioning.

Cause

InputHandler.handleKeyDown already returns early when this.composing is
true, but the opening keydown of a composition dispatches before
compositionstart. So this.composing is still false when the first
keydown is handled, and the key is forwarded via onData(seq).

Fix

Skip a keydown whenever any of the three standard IME signals is set:

  • e.isComposing — WHATWG flag, true while an IME composition is in
    progress and (in browsers that follow the spec) on the keydown that
    opens one
  • e.keyCode === 229 — long-standing IME signal supported by every
    major browser
  • e.key === "Process" — Chromium's .key value for the composition-
    starting keydown

This matches the approach used by xterm.js, the Monaco editor,
CodeMirror 6, and Chromium's own native text inputs.

Testing

  1. Build the example app (pnpm turbo run build --filter=vite...) and
    serve it pointing at any backend PTY.
  2. Switch the OS to a Chinese / Japanese / Korean IME.
  3. Type a phrase that requires composition (nihao → select 你好).

Before this patch, the PTY receives n你好 (stray n from the keydown
that opens the composition). With the patch, the PTY receives only
你好.

I'm happy to add a Playwright test that synthesises composition events
under e2e/ if you'd like — just say the word.

When IME composition starts, compositionstart fires AFTER the keydown that
triggered it. Without checking isComposing / keyCode 229 / key === "Process",
the first key (e.g. 'r' of pinyin) leaks into the PTY before compositionend
delivers the committed text.

Guard handleKeyDown with the three standard IME signals.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 23, 2026

@vmxmy is attempting to deploy a commit to the Vercel Labs 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