fix(tui,dashboard): consolidated IME input fixes — Korean/Telex recomposition, return-burst commits, PTY composition forwarding - #86761
Merged
Merged
Conversation
Preserve printable IME commit text when xterm delivers it in the same input burst as Return, so Dashboard/TUI submits the visible draft instead of dropping the final segment. Also fixes the TUI type-check stdio tuple typing and adds focused regression coverage.
Third-party Vietnamese IMEs (OpenKey/Unikey/EVKey in Telex mode) recompose a syllable by emitting an erase burst followed by the finished characters. Two layers of the TUI input pipeline mishandled this, dropping letters and leaving a stray space mid-syllable (e.g. "hạnh" rendered as "hạ ", and "vương sỹ hạnh" as "vương sỹ hạ "). Root causes, both confirmed from real captured byte streams: 1. parse-keypress: an IME often fuses a control byte (\x7f/\b, or even the U+202F marker OpenKey injects) with the recomposed text in a single stdin read. parseKeypress only recognizes a control key when the whole string is exactly that byte, so a mixed chunk fell through every branch, returned name:"" with a non-printable sequence, and the composer's printable gate discarded the entire chunk — taking the surrounding letters with it. Split text tokens on every control byte so the printable runs survive. CR/LF are deliberately not split, preserving paste/return semantics. 2. textInput: multi-character (IME/paste) inserts were committed through the 16ms deferred key-burst path, which raced an interleaved re-render and snapped the buffer back to a stale value, dropping the recomposed tail. Commit them synchronously. Additionally, the fast-echo "\b \b" backspace shortcut desynced the screen when it ran right after an Ink repaint (forced by the U+202F marker), stranding the marker glyph; suppress fast-echo for the recompose burst that follows an Ink repaint and resume it on the next real keystroke. Tested with real OpenKey and EVKey captures of "vương sỹ hạnh" across read timings, plus parser unit coverage and an EVKey no-regression guard.
- Remove unconditional 60ms wait that let deferred path pass sync-commit test - Use fake timers (setTimeout/setInterval/Date only, NOT setImmediate) - Assert immediately after final read — no trailing wait/advance - Add deterministic coverage for 60ms fast-echo suppression reset: * suppresses backspace after Ink repaint (IME recompose) * does NOT suppress on normal ASCII typing - Verified: revert sync commit -> deferred path makes 4/6 tests fail
…E composition React 18's root-level event delegation intercepts keydown events with keyCode 229 (the "composition in progress" signal sent by the browser during non-Latin IME input) and synthesises an onCompositionStart event. That synthetic path sets internal composing state that interferes with xterm.js's own IME handling on its hidden textarea, causing the first keystroke of each composition chunk to be silently dropped. The fix adds a capture-phase keydown listener on the terminal host div that stops propagation of keyCode-229 events before they reach React's delegation layer. xterm.js relies on native compositionstart/ compositionend on its internal textarea — not on keydown — so blocking the propagation is safe. Fixes #52111
Contributor
૮ >ﻌ< ა ci reviewran on 176fe01 — style: eslint --fix import ordering in salvaged IME tests
|
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidates the TUI/Ink and dashboard-PTY IME cluster (7 PRs) into one branch: Korean/Chinese/Vietnamese IME input no longer drops or duplicates characters in the TUI, and the web dashboard terminal no longer loses the first keystroke of a composition or committed dead-key text.
Changes
TUI (ui-tui)
''clear lands."会丢失内容\r"now has its printable prefix inserted before the Return submits, via a new purevalueForReturnSubmithelper.parse-keypresssplits control bytes fused with text ("\x7fô","ab\bç") so recomposed characters survive; multi-char IME inserts commit synchronously instead of through the 16 ms key-burst race; fast-echo backspace is suppressed for one keystroke after an Ink repaint (the OpenKeyU+202F"hạ␣␣" bug).Dashboard PTY (web)
Processevents from reaching React 18's root delegation, which was synthesizing composition state that fought xterm.js's own IME handling (worst on Cyrillic/Firefox).createPtyCompositionForwarderwaits 16 ms for xterm's authoritativeonData; when xterm stays silent thecompositionendtext is forwarded instead — with prefix-matching across chunked onData, mouse-report filtering, rapid-commit preservation, and disposal.Excluded
Regression tests
textInputSubmitClear.test.tsx(new): Korean commit + Enter, parent clear must land.textInputReturnBurst.test.ts(new): fused IME-commit+Return keeps the committed text.imeVietnameseTelex.test.tsx(new): real OpenKey/EVKey byte captures for "vương sỹ hạnh", sync-commit and fast-echo-suppression coverage.parse-keypress{,-drop-probe,-noregress}.test.ts: control-byte splitting, no-printable-loss probe, EVKey no-regress.pty-composition.test.ts(new): forwarder lifecycle — fallback fires, xterm-authoritative suppression, chunked/unrelated data, disposal.Validation
npx vitest run— 6 TUI/ink test filesnpx vitest run src/lib/pty-composition.test.ts(web)npx tsc --noEmit(ui-tui, web)scripts/audit_pr_attribution.py --fixCredits
Commits cherry-picked with authorship preserved: @sweetcornna (#38245), @InphinitiZ (#39695), @hanhvs (#55415), @liuhao1024 (#52117), @dplush (#76257). Related earlier reports by @energypantry (#30775) and @wuyang9311 (#39246) — see Excluded.
Issues
Fixes #75960 (TUI side). Fixes #77559. Covers the TUI/dashboard side of #69555 and #71813, and improves the IME latency reported in #84349 by removing the deferred key-burst commit for IME inserts.
Infographic