Skip to content

fix(terminal): forward macOS WebKit IME replacement input for Korean/CJK#718

Open
kmsdoit wants to merge 1 commit into
crynta:mainfrom
kmsdoit:fix/macos-webkit-korean-cjk-ime
Open

fix(terminal): forward macOS WebKit IME replacement input for Korean/CJK#718
kmsdoit wants to merge 1 commit into
crynta:mainfrom
kmsdoit:fix/macos-webkit-korean-cjk-ime

Conversation

@kmsdoit

@kmsdoit kmsdoit commented Jun 4, 2026

Copy link
Copy Markdown

What

On macOS (WKWebView) the Korean/CJK IME drives composition through input events — insertText when a new glyph starts and insertReplacementText as the in-progress syllable is refined (ㅇ → 아 → 안) — and never fires the standard composition* events. xterm forwards insertText to the PTY but silently drops insertReplacementText, so only the first jamo of each syllable ever reached the shell.

Typing 안녕 arrived at the PTY as ㅇㄴ. Single jamo worked, but as soon as two combined the syllable broke.

Root cause

Captured the real event stream on macOS (Apple Silicon, WKWebView). isComposing is always false and no compositionstart/update/end ever fire — the IME uses only input events:

input insertText            "ㅇ"   → xterm → PTY "ㅇ"   ✅
input insertReplacementText "아"   → dropped by xterm   ❌
input insertReplacementText "안"   → dropped by xterm   ❌
input insertText            "ㄴ"   → xterm → PTY "ㄴ"   ✅  (new syllable)
input insertReplacementText "녀"   → dropped            ❌
input insertReplacementText "녕"   → dropped            ❌
Enter → "\r"

xterm's _inputEvent only handles inputType === "insertText", so every refinement is lost.

This is the remaining half of #147 — the keydown guard added in #196 stopped the duplicate raw keydowns, but nothing restored the dropped replacements.

Fix

On the terminal textarea, listen for input and bridge insertReplacementText: track the last uncommitted glyph (pendingGlyph), and when a replacement arrives, erase the previously sent glyph with DEL (\x7f) and write the refined syllable. insertText is still handled by xterm; we only record it so the next replacement knows how many code points to erase. The shell now mirrors exactly what the IME shows, and each committed syllable reaches the PTY once.

Testing

  • Built and ran pnpm tauri dev on macOS (Apple Silicon).
  • Captured the raw event sequence to confirm the root cause (instrumentation removed before commit).
  • After the fix, typing 안녕 + Enter reaches the shell as 안녕 and runs as expected. Single jamo, multi-syllable words, and Latin input all behave correctly.
  • pnpm exec tsc --noEmit — zero errors.

Notes

The replacement is rewritten in place via DEL, which suits line-editing shells (zsh/bash/readline) — the common case for terminal command entry. The same input-event path likely underlies #699 (Cyrillic duplication on Linux/WebKitGTK); I can follow up on that separately.

On macOS (WKWebView) the Korean/CJK IME drives composition through `input`
events — `insertText` for a new glyph and `insertReplacementText` as the
in-progress syllable is refined (ㅇ → 아 → 안) — and never fires the standard
composition* events. xterm forwards `insertText` to the PTY but silently
drops `insertReplacementText`, so only the first jamo of each syllable reached
the shell: typing "안녕" arrived as "ㅇㄴ".

Bridge the gap on the terminal textarea: track the last uncommitted glyph and,
on `insertReplacementText`, erase it with DEL and write the refined syllable so
the shell mirrors what the IME shows. `insertText` is still handled by xterm;
we only record it so the next replacement knows how many code points to erase.

This is the remaining half of crynta#147 (the keydown guard from crynta#196 stopped the
duplicate raw keys but never restored the dropped replacements).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kmsdoit kmsdoit requested a review from crynta as a code owner June 4, 2026 05:40
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