Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
/**
|
|
* Pure helpers for controlled text inputs that must not fight CJK IME composition.
|
|
*
|
|
* Deferred parent updates (e.g. startTransition) against `value={external}` reset
|
|
* the DOM mid-composition and break Windows IMEs (candidate dismiss / pinyin echo).
|
|
*/
|
|
|
|
export function shouldCommitImeControlledChange(input: {
|
|
isComposingSession: boolean;
|
|
nativeEventIsComposing?: boolean;
|
|
/**
|
|
* When true, the open (or just-ended) composition was discarded by an external
|
|
* value change (e.g. navigation cleared the filter). Suppress commits so a
|
|
* post-compositionend `onChange` cannot reassert the stale composed draft.
|
|
*/
|
|
compositionExternallySuperseded?: boolean;
|
|
}): boolean {
|
|
if (input.compositionExternallySuperseded) return false;
|
|
return !input.isComposingSession && input.nativeEventIsComposing !== true;
|
|
}
|
|
|
|
export function shouldAdoptExternalImeControlledValue(input: {
|
|
isComposingSession: boolean;
|
|
draftValue: string;
|
|
externalValue: string;
|
|
/**
|
|
* Committed external value captured at compositionstart. When provided during
|
|
* an open composition, an external change relative to this baseline (e.g.
|
|
* different-directory navigation setting filter to "") supersedes the draft
|
|
* so compositionend cannot re-commit stale text over the navigation clear.
|
|
*/
|
|
valueAtComposeStart?: string;
|
|
}): boolean {
|
|
if (input.draftValue === input.externalValue) return false;
|
|
if (!input.isComposingSession) return true;
|
|
// Mid-composition: only adopt when parent moved away from the compose-start
|
|
// baseline. Without a baseline, never fight the IME for self-driven drafts.
|
|
if (input.valueAtComposeStart !== undefined) {
|
|
return input.externalValue !== input.valueAtComposeStart;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Resolve an input event when a composition may have been externally superseded
|
|
* (navigation clear mid-IME). Browsers often re-fire the composed text via
|
|
* `onChange` with `isComposing=false` immediately after `compositionend`; that
|
|
* follow-up must not reassert the stale draft.
|
|
*
|
|
* - While still composing after supersede: keep ignoring event values (draft stays
|
|
* on the external value) and keep the latch armed.
|
|
* - Once composition has fully ended: ignore that one post-composition change and
|
|
* clear the latch so subsequent ordinary typing commits normally.
|
|
*/
|
|
export function resolveSupersededImeInputEvent(input: {
|
|
compositionExternallySuperseded: boolean;
|
|
isComposingSession: boolean;
|
|
nativeEventIsComposing?: boolean;
|
|
}): {
|
|
ignoreEventValue: boolean;
|
|
clearSupersedeLatch: boolean;
|
|
} {
|
|
if (!input.compositionExternallySuperseded) {
|
|
return { ignoreEventValue: false, clearSupersedeLatch: false };
|
|
}
|
|
const compositionFullyEnded =
|
|
!input.isComposingSession && input.nativeEventIsComposing !== true;
|
|
return {
|
|
ignoreEventValue: true,
|
|
clearSupersedeLatch: compositionFullyEnded,
|
|
};
|
|
}
|