Repository navigation
[Bug]: IME shortcut sequencing still inverts for phrase-level preedits past the 200ms fallback #12890
Description
Operating system
macOS (applies to Windows and Linux equally, see below)
Orca version
Reproduced from source at eea0bb64d (1.4.169-rc.0)
Details
Short summary
Terminal shortcuts that write bytes to the shell are sequenced behind a pending IME composition by waiting for the composition to end, with a 200 ms fallback. That bound fits Korean, which commits per syllable, but not Japanese or Chinese, where a phrase-level preedit routinely stays open for seconds. Past 200 ms the byte goes out early and the original ordering bug returns, now with 200 ms of added latency.
What happened?
sendTerminalInputAfterComposition (src/renderer/src/components/terminal-pane/terminal-ime-deferred-newline.ts:8) waits for compositionend or the xterm session-end event, then arms TERMINAL_IME_DEFERRED_NEWLINE_FALLBACK_MS = 200 (:6) and calls finish() unconditionally when it expires. The fallback exists so a composition that never ends (blur, pane teardown) cannot strand the keystroke, which is correct. The problem is only the constant's fit.
Korean 2-Set closes a composition per syllable, so the wait resolves in tens of milliseconds and the fallback never fires. A Japanese or Chinese preedit is romaji-then-convert-then-confirm: the composition stays open across the whole phrase. A shortcut pressed at the start of that phrase fires its bytes 200 ms later with the preedit still open, which is exactly the unsequenced order the deferral is meant to prevent.
This is not new in #12732 — the Enter path has used the same constant since it was introduced. #12732 widens the same deferral to the kill and cursor-movement chords, so the ceiling now applies to more keys.
How can we reproduce it?
- On macOS, switch the input source to Japanese (Kotoeri) and focus a terminal pane running bash or zsh.
- Type a phrase in romaji long enough to stay in the preedit, e.g.
nihongowonyuuryoku, and do not confirm it. - Wait past 200 ms, then press
Cmd+Backspace. - The line-kill byte reaches the shell while the preedit is still open. Confirm the phrase afterwards and it lands after the kill, on the line the kill just emptied.
Ctrl+Backspace reproduces the same way on Windows and Linux.
Anything else that might help
Scope of what I verified: the 200 ms expiry and its unconditional finish() are read directly from the source, and the Korean case is covered by tests in #12732. I have not run the Japanese case on real hardware, so the repro above is derived from the code path rather than observed. The existing Japanese test in #12732 does not cover this: its harness commits the composition synchronously, so the timer never gets a chance to expire.
Two directions, neither small:
- Stop waiting; force the composition to finish. xterm already does this —
CompositionHelper.keydowncalls_finalizeComposition(false)when a non-composition key arrives, described in its own comment as being "mainly here for the case where enter is pressed and the handler needs to be triggered before the command is executed". Orca's window-level capture handler callsstopImmediatePropagation(keyboard-handlers.ts:587), soCoreBrowserTerminal._keyDownnever runs. Reaching the finalizer is blocked twice over:_finalizeCompositionis private with no public accessor, and the one call site inside_keyDownsits behind the_customKeyEventHandlerearly-return that Orca itself uses to claim these keys.@xterm/xtermis a published dependency here (6.1.0-beta.287), not a vendored fork, so this needs an upstream addition or a restructure of Orca's capture handler. - Keep the timer but stop treating 200 ms as universal. Cheaper, but every fixed number trades one failure for another: a longer wait means a shortcut that visibly does nothing for that long. Any variant would need the bound derived from the active input source rather than hardcoded.
Activity
nwparker commentedon Aug 8, 2026
Fixed on
mainin 17b3dff — the file you cite no longer exists.terminal-ime-deferred-newline.tsandTERMINAL_IME_DEFERRED_NEWLINE_FALLBACK_MSare both gone; terminal input is no longer sequenced behind a timer.Your diagnosis was exactly right, and we hit the same line from the other direction. On 1.4.171 we could get Orca to emit its own Shift+Enter payload to the PTY with no Enter pressed anywhere — deterministic, 10/10, landing 203-218 ms after the keydown, which is that 200 ms fallback firing straight through the pending-composition gate the event path respects. The event path called
finishAfterPendingComposition; the timer calledfinishdirectly. One word apart.Your point that the bound fits Korean but not phrase-level Japanese or Chinese is the reason a timer was the wrong shape for this at all, rather than a number that needed tuning.
Thanks for the precise write-up — file and line references made this quick to confirm.