fix(terminal): correct macOS Hangul/CJK IME input - #1053
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughmacOS terminal input now reconstructs IME updates into PTY writes, tracks uncommitted composition state per slot, and filters duplicate printable deliveries. Tests cover Unicode deletion, replacement suppression, composition text, unsupported inputs, and Korean composition traces. ChangesmacOS IME input
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
d3ae4ff to
bf768e6
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/modules/terminal/lib/rendererPool.ts`:
- Around line 220-225: Track each payload written by the IME handler around ta’s
input listener, then have the xterm onData path consume only the matching
deferred release, including the deleteContentBackward DEL case, instead of
filtering all printable data via isPrintableOnly; preserve unbracketed paste and
other non-IME printable delivery. Update both affected rendererPool.ts sites
(220-225 and 339-344) and extend ime.test.ts (61-83) to cover correlated
consumption and path-dependent non-IME input.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a20b827f-02a5-4b83-ac59-4fda945f6805
📒 Files selected for processing (3)
src/modules/terminal/lib/ime.test.tssrc/modules/terminal/lib/ime.tssrc/modules/terminal/lib/rendererPool.ts
macOS WKWebView surfaces IME input in two different ways depending on the OS / webview build: 1. It fires real composition events (compositionstart/update/end). Here xterm's own CompositionHelper assembles the syllable and emits the committed text through onData — it just works, and anything extra we do corrupts it. 2. It fires NO composition events and delivers the composed stream only as `input` events (insertText/insertReplacementText/insertCompositionText) with isComposing false. xterm can't assemble a syllable from that, so Hangul reached the PTY broken (leading jamo split off, e.g. 안녕 -> ㅇ아). Handle both. On the first `compositionstart` we mark the slot native and get out of the way entirely (xterm owns IME). Until/unless that happens, we reconstruct the committed stream from the input events (ime.ts) and drop xterm's spurious re-delivered printable onData so it can't double the jamo. Everything is gated on IS_MAC and on the native flag, so non-macOS and composition-capable webviews run exactly xterm's normal path. Scoped to macOS. Unit tests cover the reconstruction core for both the replacement and composition event traces.
bf768e6 to
ce67e85
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Tested this branch on macOS 26 / Apple Silicon (M1 Pro), rebased onto current One thing I think is off in the delete branch of if (inputType.startsWith("delete")) {
return { send: DEL.repeat(Math.max(1, cpLen(unit))), unit: "" };
}Backspace pressed during an active preedit looks like it reaches the PTY as two DELs, not one:
For a one-code-point preedit unit that is 2 DELs for 1 keypress, so the character before the preedit gets eaten as well. Since // xterm's Backspace keydown already wrote one DEL before this input event
// fired, so only erase the remainder of the unit.
if (inputType.startsWith("delete")) {
return { send: DEL.repeat(Math.max(0, cpLen(unit) - 1)), unit: "" };
}
This changes two of the existing tests, since Branch with the change on top of yours, if it is useful: https://github.com/sskys18/terax-ai/tree/fix/macos-hangul-ime To be straight about confidence: this is derived from reading the event ordering and the Happy to leave this with you either way. Getting #813 fixed matters more than whose PR does it. |
|
Separate from the code: this PR is CI never ran on The useful part: that gate no longer applies to you. #1079 merged on 2026-08-01, so a fresh push to this branch should have workflows run automatically. A rebase onto current Worth doing soon. #623 and #718 are both |
|
Thanks for the careful work and real macOS testing. We are consolidating this fix in #1112, whose author has been asked to incorporate this PRs important native-composition detection while keeping a narrower PTY path. I am closing this one as superseded so we have one canonical implementation. We really appreciate the investigation. |
Summary
Fixes Hangul/CJK IME input in the terminal on macOS. Composed text now reaches the PTY correctly, and Latin / symbols / IME switching keep working.
Closes #813.
Problem
macOS WKWebView surfaces IME input in two different ways depending on the OS / webview build, and the terminal only worked in neither:
compositionstart/update/end). Here xterm's ownCompositionHelperassembles the syllable and emits the committed text throughonData— it works, and anything extra we do corrupts it.inputevents (insertText/insertReplacementText/insertCompositionText) withisComposingfalse. xterm can't assemble a syllable from that, so Hangul reached the PTY broken.Reproduction of case 2 (Korean 2-beolsik):
안녕ㅇ아...(leading jamo split off)안녕사과ㅅ사과사과Fix
Detect the mode at runtime and never fight xterm:
compositionstart, mark the slot native and get out of the way entirely — xterm owns IME, Latin, and symbols.inputevents (ime.ts), and drop xterm's spurious re-delivered printableonDataso it can't double the leading jamo. Control sequences and bracketed paste carry control bytes and pass through.Everything is gated on
IS_MACand the native flag, so non-macOS and composition-capable webviews run exactly xterm's normal path — zero behavior change there.Testing
pnpm check-types,pnpm lint— clean.pnpm test— full suite green;ime.test.tscovers the reconstruction core for both the replacement-event trace (안녕) and the composition-event trace (아).!@#$%^&*()_+-=~|?/), bracketed paste, IME language switching, and Enter / arrows / Backspace all correct.Relationship to prior PRs
Builds on the input-reconstruction idea in #623 / #718. Those reconstruct unconditionally, which breaks on webview builds that DO fire composition events (double / scrambled output) and don't dedup xterm's re-delivery. This detects the mode and defers to xterm when it can handle IME itself, reconstructing only when it can't.
Summary by CodeRabbit
New Features
Bug Fixes