Files
orca/docs/reference/ime-regression-checklist.md
T
Neil 85a3ba6d42 fix(terminal): align CJK IME preedit spacing (#19367)
* fix(terminal): align IME preedit to terminal cell grid

* fix(terminal): preserve native shaping and reuse IME preedit on repaint

* fix(terminal): preserve native shaping with bounded IME spacing runs

* test(terminal): account for inline preedit subpixel rounding

* test(terminal): keep the IME grid fixture wide at every DPI

* chore: regenerate xterm patch after rebase

* test(terminal): remove IME assertion lint findings

* test(terminal): avoid reflective IME fixture access

* test(e2e): run IME renderer matrix with WebGL available

* fix(ci): restore editor line budget
2026-09-19 16:44:38 -07:00

12 KiB

IME Regression Checklist

Follow-up issue ledger

These reports define durable acceptance contracts, not only the symptoms from one machine.

Issue Root cause Ownership invariant Required evidence
#16911 Native Chat preedit overwritten by streaming or attachment settlement React reconciliation or an asynchronously resolved attachment writes an application draft while the browser owns the composing textarea; duplicate settlement can then re-adopt stale DOM. From compositionstart until the first compositionend or blur, the browser owns the textarea. Resolved paths queue at the shared semantic sink; settlement adopts the browser DOM before flushing once. Repeated stale streaming rerenders preserve the element and preedit; idle external drafts still synchronize; concurrent SSH completions preserve completion order and duplicates; both settlement orders run once; disable discards queued work; blur does not steal focus; native composition commits once.
#16949 terminal preedit has no visible cursor The opaque composition overlay covers the renderer cursor; at the final cell an over-wide inline preedit can also place its caret beyond the clipped screen. The existing xterm CompositionHelper owns a visible caret after the preedit and before any row remainder; final-cell composition end-aligns within the screen while mid-line composition stays left-anchored. Start, update, arbitrary-width final-cell containment, mid-line remainder placement, cleanup, and update-without-start are covered; preview and normal terminals inherit the live cursor theme.
#16950 typing diagnostic records no CJK samples The probe observes echoing keydowns but not reconciled composition commits, then guesses which queued input owns opaque TUI output. A reconciled composition is observed even when compositionend.data is empty; only an isolated input enters exact percentiles, while overlap or a dropped-input gap produces one aggregate ambiguous burst. Recorded Linux IBus empty-data commit, isolated direct and IME samples, mixed-source ambiguity, timeout/cap gaps, UTF-8 output bytes, and stop/drain cleanup are covered.
#17104 Korean preedit repeats the Codex placeholder Generic xterm row-tail reproduction exposed an application-semantic Codex or Claude composer placeholder that presentation style cannot identify safely. Xterm always preserves generic covered row text. Orca's existing structural composer classifier masks only a verified placeholder during the exact active composition session; repaint reclassification runs only while composing, and end, blur, or disposal clears ownership, class, and listeners. Arbitrary dim output and shell lookalikes remain visible. Codex prompt/footer and Claude prompt/frame classification, arbitrary all-dim and shell-lookalike negatives, repaint entry and exit, end/blur/disposal cleanup, and rendered Electron proof at cursor column 2 preserving generic row text are covered.

Preedit cell advances (#19315)

Single-codepoint CJK graphemes use the active Unicode provider's cell width and measured font advance. Ordinary inline spans preserve browser bidi and baseline layout; equal corrections share a run. Keep glyphs unscaled and the underline, caret, and candidate textarea aligned with the rendered preedit. Appending ASCII, emoji, or another script must not change an existing CJK prefix's correction. Combining sequences, emoji, other scripts, and whitespace retain native shaping. Font loading, typography changes, and renderer metric changes must update an open composition; row-tail repaints preserve its unchanged nodes.

Cold font measurements and styled runs share a fixed work budget. Repeated CJK can remain one corrected run; after the budget is exhausted, the remaining text keeps its native advance. This deliberately leaves the original spacing mismatch in the tail of unusually varied long compositions, without switching the prefix back to native spacing or rebuilding thousands of spans.

terminal-ime-xterm-preedit-cell-grid.test.ts covers text preservation, native clusters, lifecycle, and bounded work. terminal-ime-preedit-cell-grid.spec.ts checks rendered glyph origins, caret/textarea geometry, underlines, font changes, and native shaping at DPR 1, 1.25, and 2 with WebGL on/off. terminal-ime-preedit-continuity.spec.ts covers mixed suffixes and budget crossings. These checks use Chromium composition through CDP; they do not replace native OS IME evidence.

Bounded-state and ownership contracts

Every transient collection and ownership tracker must have an explicit lifetime and bound:

  • Native Chat uses NATIVE_FILE_DROP_MAX_PATHS (256). If a resolved completion would cross the cap, the whole batch is rejected atomically and the overflow notice remains visible through settlement; accepted paths keep order and duplicates. The queue is cleared before re-entry and on disable or pane-owner remount.
  • The terminal placeholder mask tracks one scalar activeSessionId because xterm renders one composition view. A newer start supersedes an older one, a stale end cannot clear the latest owner, and blur or disposal clears it. The composition route keeps its per-ID reference-counted map intentionally for transport ownership; it is not replaced by the scalar.
  • Typing diagnostics cap pending and ignored echo candidates at MAX_PENDING_ECHO_CANDIDATES (64), cap pending user-input signals, drain timed-out candidates, and clear all series on pane detach. Overflow becomes an explicitly ambiguous burst rather than an arbitrary attribution.

Native Chat asynchronous attachment settlement

Attachment resolution is an external semantic write, including local file selection, pasted-image temp saves, and SSH uploads. While composition is active, it must not replace the browser-owned textarea value.

  • Start two concurrent SSH uploads, resolve the second first, and return one path twice. Preserve completion order and both duplicates.
  • On the first settlement event, adopt the browser DOM before flushing queued paths. Exercise compositionend then blur and blur then compositionend in one React batch; both orders must adopt and flush exactly once.
  • If the composer becomes disabled before an upload resolves or before the queue flushes, discard that result.
  • If compositionend is omitted, blur performs the same one-time settlement without focusing the textarea or stealing focus back.

Cross-platform verification

Synthetic DOM events prove Orca's event and rendering contracts, but they do not exercise the operating system's input method. Changes must also cover:

Environment Native evidence
macOS A native Korean 2-set composition in Native Chat and a terminal; preedit survives external renders, the caret remains visible, and commit occurs once.
Windows Microsoft Korean IME over an untouched Codex placeholder; the preedit is the only visible text, the placeholder returns after cancel, and ordinary mid-line content remains visible.
Linux / SSH IBus Hangul with an SSH-hosted PTY; an empty-data compositionend still produces one diagnostic sample and one committed syllable.

For remote evidence, live means the owning host reported the current verification session or process identity. exited requires positive host-owned evidence that the same identity terminated or is absent. Any transport failure, stale identity, timeout, or inability to ask the owning host makes the result unverifiable; it is never evidence that the composition or PTY process exited.

Code elegance gate

Each fix must pass all of these checks:

  • Reuse the component that already owns the state or overlay; do not install a second composition state machine.
  • Make browser, renderer, and PTY ownership boundaries explicit. Provisional text must not leak into committed state or PTY input.
  • Route local selection, pasted-image temp saves, and SSH upload results through one resolved-attachment sink; queue semantic paths, never whole draft snapshots.
  • Keep correctness changes separate from unrelated micro-optimizations.
  • Use bounded per-composition state and work. Dispose every listener, timer, observer, and DOM node with its owner.
  • Preserve ordinary Latin input, mixed styled terminal content, local and SSH PTYs, preview terminals, and folder workspaces with paired negative tests.
  • Keep platform quirks behind event contracts or runtime platform checks; do not branch on an IME vendor, language, or terminal agent name.
  • Treat the canonical xterm source patch as the only hand-edited source, then regenerate its bundle patch and lockfile together.
  • Prefer deterministic replay or state-transition tests. Native evidence is a second layer, never a substitute for regression coverage.