Files
orca/tests
122b8c25d7 fix(terminal): let Linux IMEs keep the candidate key for a preedit they own (#22607)
* fix(terminal): let Linux IMEs keep the candidate key for a preedit they own

Sogou on fcitx draws its preedit and candidate list in its own window and
opens no Chromium composition session, so every composition-scoped candidate
guard is idle when the user presses Space or a candidate digit. The selector
reached the PTY as literal text and the commit arrived after it.

The marker the input framework does still deliver is the claimed letter
keydown - `keyCode 229` / `key 'Process'` over the physical key. A bounded
window armed from that claims the next Space or digit, and stands down for a
real composition session, for the commit, and on expiry.

Co-authored-by: nxiaobai <48705519+nxiaobai@users.noreply.github.com>
Co-authored-by: Big-ios <18501866+Big-ios@users.noreply.github.com>
Co-authored-by: cporoske <14369468+cporoske@users.noreply.github.com>

* fix(terminal): keep the claimed-preedit window open while the IME edits it

The window released on any keydown that was not a letter the IME had
claimed, but the IME claims the keys that edit and page a preedit too —
Backspace, the arrows, `-`/`=` for the next candidate page — and Chromium
keeps the original `code` on those 229/Process keydowns. Paging to a later
candidate therefore disarmed the window and the digit that picked from that
page reached the PTY.

A claimed keydown now refreshes the deadline instead of clearing it. Arming
still requires a claimed letter, so a bare navigation key with no preedit
behind it cannot open the window from cold.

* fix(terminal): release the claimed-preedit window when a composition ends

An engine that does run a composition session still emits the claimed
`keyCode 229` letter keydowns that arm this window, and both its commit and
its cancel travel as `insertCompositionText` — the one input type the commit
release deliberately ignores. Nothing else closed the window, so a cancelled
preedit left it open and the next literal Space was swallowed. Refreshing the
deadline on every claimed keydown had widened that tail.

`compositionend` now releases it, as `compositionstart` already did. The
engines this guard exists for emit neither event, so their path is unchanged.

The selector suite drives the state object directly and cannot see a missing
listener, so the release wiring gets its own DOM-level suite.

* test(terminal): pin a Space-committing composition session end to end

The regression the compositionend release fixes needs the whole session to
show up: compositionstart fires once, so every letter after the first re-arms
the window, the commit and the cancel both travel as insertCompositionText,
and the committing Space arrives claimed rather than bare. Only the end of the
session closes the window before the user's next literal Space.

Fails with the compositionend listener removed.

* fix(terminal): end the claimed-preedit window on the selector the IME took

A claimed selector means the engine picked the candidate itself, so the round
is over. It reads `key: 'Process'`, which the selector predicate cannot see,
so this asks the physical code instead.

Belt and braces for an engine that opens a composition session and never closes
it — a documented defect in at least one shipped input framework. Without a
`compositionend` the session-end release never runs, and the refresh added for
candidate paging would otherwise hold the window open across the commit and
swallow the user's next literal Space. Paging keys still refresh, so a long
candidate browse is unaffected.

* fix(terminal): let a claimed Enter or Escape end the pick too

Japanese and Zhuyin commit with Enter, not Space, and Escape cancels. Both
arrived claimed and fell into the refresh branch, so for an engine that opens a
composition session and never closes it they held the window across the commit
— the same defect the Space case was written to close, reached by a different
key. Arrows, `-`/`=`, Backspace and PageUp/PageDown still refresh, because those
page the candidate list rather than ending the round.

Physical codes rather than `key`: `key` reads `Process` on all of them, and
`Digit*` survives AZERTY, Dvorak and Colemak moving the digit row.

Also corrects the window comment, which still described the orphan-keyup
picking window. Since claimed keys refresh it, it is now a stuck-state valve
that only expires when the IME stops reporting.

---------

Co-authored-by: nxiaobai <48705519+nxiaobai@users.noreply.github.com>
Co-authored-by: Big-ios <18501866+Big-ios@users.noreply.github.com>
Co-authored-by: cporoske <14369468+cporoske@users.noreply.github.com>
2026-09-24 00:50:03 -07:00
..