Skip to content

fix: handle insertReplacementText for Korean IME on WKWebView/Safari#5704

Open
minemos wants to merge 4 commits intoxtermjs:masterfrom
minemos:fix/wkwebview-korean-ime
Open

fix: handle insertReplacementText for Korean IME on WKWebView/Safari#5704
minemos wants to merge 4 commits intoxtermjs:masterfrom
minemos:fix/wkwebview-korean-ime

Conversation

@minemos
Copy link
Copy Markdown

@minemos minemos commented Feb 16, 2026

Summary

WKWebView (used by Tauri, Capacitor, and Safari-based apps on macOS/iOS) does not fire compositionstart/compositionupdate/compositionend events for Korean IME input. Instead, it fires:

  • insertText with inputType === 'insertText' for the initial jamo (e.g. )
  • insertReplacementText for composition updates (e.g. )

Since _inputEvent() only handles inputType === 'insertText', the composed Korean syllables from insertReplacementText were silently dropped, causing only raw jamo to reach the terminal.

Changes

CoreBrowserTerminal.ts (main fix):

  • Buffer insertReplacementText data instead of dropping it, and show composition preview
  • Intercept Hangul insertText (before the composed/keyDownSeen guard) to buffer instead of sending immediately — WKWebView may set composed=true on Hangul syllables
  • Flush the buffered composed syllable on next non-IME keydown or new character
  • Show composition preview on first jamo; defer preview when flushing to avoid stale cursor position

CompositionHelper.ts (minimal):

  • Add wkImeComposing flag to suppress _handleAnyTextareaChanges() during synthetic WK composition (keyCode 229 would otherwise send individual jamo via setTimeout)

Types.ts / TestUtils.test.ts:

  • Add wkImeComposing to ICompositionHelper interface and mock

How it works

User types WKWebView event Before this fix After this fix
insertText "ㅎ" sent to PTY Buffered, preview shown
insertReplacementText "하" Dropped Buffer updated to , preview updated
insertReplacementText "한" Dropped Buffer updated to , preview updated
(next char) insertText "..." flushed to PTY, new buffer started

Testing

Tested with Korean (Hangul) IME in:

  • Tauri v2 on macOS (WKWebView)

This fix is scoped to insertReplacementText handling and Hangul detection, so it should not affect existing composition flows on Chrome/Firefox where standard composition events are fired.

Fixes input for Korean IME users on Safari/WKWebView-based terminal applications.

WKWebView (used by Tauri, Capacitor, and Safari-based apps) does not fire
compositionstart/compositionupdate/compositionend events for Korean IME input.
Instead, it fires:
  - insertText for initial jamo (e.g. 'ㅎ')
  - insertReplacementText for composition updates (e.g. 'ㅎ' → '하' → '한')

Since _inputEvent only handles insertText, composed Korean syllables were
silently dropped, causing only raw jamo to reach the terminal.

This patch:
  1. Buffers insertReplacementText data and shows composition preview
  2. Intercepts Hangul insertText to buffer instead of sending immediately
  3. Flushes the composed syllable on next non-IME keydown or new character
  4. Adds wkImeComposing flag to CompositionHelper to suppress
     _handleAnyTextareaChanges during synthetic WK composition

Tested with Korean (Hangul) IME in Tauri v2 (macOS WKWebView).
@jangster77
Copy link
Copy Markdown

Thanks. This patch works fine for tauri 2.0. macos 26.3(25D125), safari, rust and tauri env. hope to merge this on later version xterm.js 6.x.x.

Comment on lines +123 to +128
/**
* WKWebView IME workaround: holds the latest composed text from
* insertReplacementText events, flushed on next insertText or non-IME keydown.
*/
private _wkImeComposing: boolean = false;
private _wkImePending: string = '';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at how the events work Safari and it's unfortunate. How about instead of hardcoding this stuff into CoreBrowserTerminal, have a WkHangulCompositionEventEmulator that maps insertText/insertReplacementText events into compositionstart/update/end events? This class could then also be created only when it looks like webkit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice when finishing a character and moving onto the next, the next doesn't show until the second part is typed:

Image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pressing ctrl+c while composing seems to duplicate the composing character:

Image

bang9 added a commit to bang9/ai-tools that referenced this pull request Mar 17, 2026
Apply changes from xtermjs/xterm.js#5704 as a pnpm patch. Adds
WKWebView IME workaround that handles insertReplacementText events
for Korean (Hangul) composition input, which WKWebView/Safari fires
instead of standard composition events.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@thedalbee
Copy link
Copy Markdown

thedalbee commented Mar 29, 2026

For anyone looking for an external workaround while this PR is being reworked: I've published a standalone module that implements the same logic as an external interceptor (per Tyriar's recommendation to use a separate emulator class):

https://github.com/thedalbee/wk-hangul-ime

Confirmed working with xterm.js 6.0.0 + Tauri v2 (WKWebView) on macOS.

Three-layer approach:

  1. attachCustomKeyEventHandler blocks keyCode 229 — prevents CompositionHelper jamo leak
  2. stopImmediatePropagation on input events (capture phase) — blocks _inputEvent
  3. shouldSkip() in onData — filters any remaining leaked jamo

No xterm.js fork needed — works as a drop-in module.
진짜 *나 힘들었다... 감사합니다

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.

4 participants