Skip to content

[Bug]: IME shortcut sequencing still inverts for phrase-level preedits past the 200ms fallback #12890

Closed

Description

@kunsanglee

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?

  1. On macOS, switch the input source to Japanese (Kotoeri) and focus a terminal pane running bash or zsh.
  2. Type a phrase in romaji long enough to stay in the preedit, e.g. nihongowonyuuryoku, and do not confirm it.
  3. Wait past 200 ms, then press Cmd+Backspace.
  4. 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:

  1. Stop waiting; force the composition to finish. xterm already does this — CompositionHelper.keydown calls _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 calls stopImmediatePropagation (keyboard-handlers.ts:587), so CoreBrowserTerminal._keyDown never runs. Reaching the finalizer is blocked twice over: _finalizeComposition is private with no public accessor, and the one call site inside _keyDown sits behind the _customKeyEventHandler early-return that Orca itself uses to claim these keys. @xterm/xterm is 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.
  2. 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

  1. nwparker commented on Aug 8, 2026

    @nwparker
    Contributor

    Fixed on main in 17b3dff — the file you cite no longer exists. terminal-ime-deferred-newline.ts and TERMINAL_IME_DEFERRED_NEWLINE_FALLBACK_MS are 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 called finish directly. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions