fix(prompt-input): keep the editor inert during IME composition - #44826
fix(prompt-input): keep the editor inert during IME composition#44826powoct wants to merge 4 commits into
Conversation
Safari fires compositionend before the keydown that confirms an IME candidate, so that keydown reports isComposing=false and keyCode=13. Treat any key event within 100ms of compositionend (tracked with performance.now) as part of the IME confirmation so it does not submit.
Apply the same post-compositionend window to the v2 editor: an Enter arriving within 100ms of compositionend confirms an IME candidate and must not submit the message.
The v2 editor dispatched on every keydown and read/wrote the selection on keyup/pointerup while composing. Safari reacts by aborting the composition after each letter, so `nihao` becomes five one-letter compositions and the confirming Enter arrives as a plain keyCode=13 long after any compositionend, leaving the guard nothing to match. Make the editor fully inert while a composition is active and during the 100ms window after it: no keydown dispatch, no cursor sync, no input handling, no editor DOM rewrites. State is reconciled once from the DOM on compositionend. Safari then keeps the composition as a single segment and the confirming Enter reports keyCode=229.
|
Tested this branch — unfortunately it does not fix the issue in my environment. Environment
ResultNo special trigger is needed — plain continuous Chinese input fails right away. While typing, every letter except the last one is immediately committed as plain English text; only the final keystroke remains in the IME composition. This is the same behavior I reported in #38674 (comment). Chrome on the same machine works fine. Screen recording attached below. Screen.Recording.2026-08-26.at.2.34.12.mov |
|
@anyingiit Thanks for testing, and for the detailed report and recording — much appreciated. I tried to reproduce this after updating to macOS 26.6.2 / Safari 26.6.2, typing continuously with the macOS built-in Simplified Chinese Pinyin IME into a production deployment carrying this fix (based on v1.18.22), and composition stays intact there: i.e.
If your trace shows a different order — e.g. the first |
|
@powoct Good news — with a correct local setup, the fix works in my environment, in both normal and private windows, including the cold-start first input. Details and the traces you asked for below. One important correction about my earlier testing first. A correction about my first reproductionMy original report (and my earlier comment) was against a setup I believed was "this branch, dev mode". It turns out my local Correct test against this branch (production build)I then built the branch locally and served the real patched bundle via
The per-letter split I saw before is gone with the patched build — including the first-input cold-start case, and in both window types. Answers to your five questions
The key difference vs. my broken trace: with the patched build the composition stays open across all five letters (no Net: the branch fixes the issue for me. Happy to run any further traces if useful. Screen.Recording.2026-08-28.at.8.06.43.movNice Work! |
|
@anyingiit Thank you for the thorough follow-up — tracking down the serve fallback and re-testing with the real build is exactly what was needed, and the traces are perfect. Much appreciated. Summary for reviewers:
|

Issue for this PR
Closes #38674
Supersedes #38728, which the automated cleanup closed before anyone reviewed it. I can't reopen that one — GitHub rejects the reopen — so this is the same fix rebased onto current
dev, squashed into three commits, with an unrelated placeholder change split out into a follow-up branch.Type of change
What does this PR do?
Typing CJK in the web prompt input is broken in two ways in Safari. They have different causes, so both are fixed here.
1. The Enter that confirms an IME candidate submits the message.
Safari fires
compositionendbefore the confirming keydown, so that keydown reportsisComposing=falseandkeyCode=13:The existing guard is
event.isComposing || composing() || event.keyCode === 229. In Safari none of the three match, the Enter falls through to submit, and the message is sent with the raw pinyin still in it. The fix recordsperformance.now()atcompositionendand treats any key event within 100ms of it as part of the IME confirmation. Same guard in both editors — v1 (packages/app) and v2 (packages/session-ui).2. The v2 editor aborts the composition after every keystroke.
That guard alone was not enough against a real deployment. The v2 editor runs
dispatch/setStateon every keydown and reads and writes the selection on keyup/pointerup while composing. Safari responds by aborting the composition after each letter, sonihaobecomes five one-letter compositions and the confirming Enter arrives as a plainkeyCode=13long after anycompositionend— nothing is left for the guard to recognize.The v2 editor is now fully inert while a composition is active, and during the 100ms window after it: no keydown dispatch, no cursor sync, no input handling, no editor DOM rewrites. State is reconciled once from the DOM on
compositionend. Safari then keeps the composition as a single segment and the confirming Enter reportskeyCode=229, which the guard already handled.That second part is not Safari-specific, and I believe it addresses #39632 as well — the first keystroke escaping the composition in the v2 editor is the same DOM-rewrite-during-composition cause, which is presumably why the duplicate bot linked the two issues. I reproduced with macOS Pinyin and Japanese rather than Shuangpin, so I'd rather not close that issue automatically; confirmation from its reporters would settle it.
Note on expected behavior: Enter committing the raw romaji instead of 你好 is macOS Pinyin's own design — Space picks the candidate. The bug was that the app submitted the message on that Enter.
How did you verify your code works?
nihao+ Enter no longer submits and the text stays in the box,nihao+ Space commits 你好, Japanese live conversion with multi-segment auto-commit is not interrupted, and plain-English Enter / Shift+Enter behavior is unchanged. Chrome regression-tested with the same IMEs.bun test:packages/session-ui85 pass / 0 fail (including 31 lines of new coverage inmachine.test.ts),packages/app721 pass / 1 fail. The one failure issrc/i18n/desktop-native.test.ts, which fails identically on unpatcheddev— unrelated to this change.bun run typecheckclean in both packages.devat 18b4cb6; no conflicts.Screenshots / recordings
No visual change — the fix only affects which key events reach the submit path and when the editor touches the DOM.
Checklist