mirror of
https://github.com/stablyai/orca.git
synced 2026-10-28 00:02:54 +00:00
* Revert "test(ime): restore coverage the composition-ownership change removed (#13168)" This reverts commit25a8c517e1. * Revert "refactor(terminal): return IME composition ownership to xterm (#13128)" This reverts commit17b3dff3c4. * test(ime): keep the architecture-neutral Korean trace coverage The recorded IBus/fcitx5 and Windows MS-Korean traces from #13168 assert PTY byte order, not composition ownership, so they still hold once the terminal composition layer is restored. The mobile accessory-order test pinned the new handleLiveInputChange signature and does not. Co-authored-by: Orca <help@stably.ai> * fix(terminal): keep the macOS Backslash bypass through the revert The restored native-text forwarder only claims keys for input sources in its hardcoded CJK allowlist, so third-party IMEs off that list (Qingg, #10896) still get a raw backslash. #13128 added this bypass as a partial replacement; keep it rather than trade the open issue back. Scoped to the bare backslash key. The rest of shouldBypassXtermForMacNativeText bypassed all unmodified non-ASCII text, which would race the restored forwarder. Co-authored-by: Orca <help@stably.ai> * fix(mobile): move the mirror-step ref write out of render The restored hook assigned runMirrorStepRef during render, which is not replay-safe — React can discard render work, so the mutation can leak from UI that never commits. Its only read is inside the held-commit timer, which fires long after commit, and the ref has a safe default, so an effect is soon enough. Surfaced by the changed-lines React Doctor gate: the rule postdates this code, so restoring the file re-introduced it as a new violation. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
114 lines
4.5 KiB
TypeScript
114 lines
4.5 KiB
TypeScript
import { useCallback, type RefObject } from 'react'
|
|
import type { TextInput } from 'react-native'
|
|
import {
|
|
getTerminalLiveAccessoryBytesDecision,
|
|
getTerminalLiveAccessoryLocalEditText
|
|
} from './terminal-live-text-commit'
|
|
import type { TerminalLiveAccessoryInput } from './terminal-live-accessory-input'
|
|
import { sendTerminalLiveControlAfterPendingFlush } from './terminal-live-control-send-order'
|
|
import type { TerminalLiveInputSender } from './terminal-live-input-sender'
|
|
|
|
export type TerminalLiveAccessoryInputCommitResult =
|
|
| { readonly kind: 'allow-raw' }
|
|
| { readonly kind: 'handled' }
|
|
| { readonly kind: 'suppress-raw' }
|
|
|
|
export async function getTerminalLiveAccessoryInactiveInputCommitResult(
|
|
waitForPendingLiveInputFlush: () => Promise<boolean>
|
|
): Promise<TerminalLiveAccessoryInputCommitResult> {
|
|
return (await waitForPendingLiveInputFlush()) ? { kind: 'allow-raw' } : { kind: 'suppress-raw' }
|
|
}
|
|
|
|
type TerminalLiveAccessoryInputCommitOptions = {
|
|
readonly activeHandle: string | null
|
|
readonly applyLiveInputMirror: (handle: string, fieldText: string) => void
|
|
readonly clearPendingLiveInputCommit: () => void
|
|
readonly flushPendingLiveInputText: (expectedHandle: string | null) => Promise<boolean>
|
|
readonly heldLiveInputTextRef: RefObject<string>
|
|
readonly liveInputRef: RefObject<TextInput | null>
|
|
readonly liveInputTerminalHandles: ReadonlySet<string>
|
|
readonly pendingLiveInputHandleRef: RefObject<string | null>
|
|
readonly sentLiveInputTextRef: RefObject<string>
|
|
readonly sendLiveTerminalInputRef: RefObject<TerminalLiveInputSender>
|
|
readonly setLiveInputCapture: (text: string) => void
|
|
readonly waitForPendingLiveInputFlush: () => Promise<boolean>
|
|
}
|
|
|
|
export function useTerminalLiveAccessoryInputCommit({
|
|
activeHandle,
|
|
applyLiveInputMirror,
|
|
clearPendingLiveInputCommit,
|
|
flushPendingLiveInputText,
|
|
heldLiveInputTextRef,
|
|
liveInputRef,
|
|
liveInputTerminalHandles,
|
|
pendingLiveInputHandleRef,
|
|
sentLiveInputTextRef,
|
|
sendLiveTerminalInputRef,
|
|
setLiveInputCapture,
|
|
waitForPendingLiveInputFlush
|
|
}: TerminalLiveAccessoryInputCommitOptions): (
|
|
input: TerminalLiveAccessoryInput
|
|
) => Promise<TerminalLiveAccessoryInputCommitResult> {
|
|
return useCallback(
|
|
async (input: TerminalLiveAccessoryInput): Promise<TerminalLiveAccessoryInputCommitResult> => {
|
|
if (!activeHandle) {
|
|
return { kind: 'allow-raw' }
|
|
}
|
|
if (!liveInputTerminalHandles.has(activeHandle)) {
|
|
return getTerminalLiveAccessoryInactiveInputCommitResult(waitForPendingLiveInputFlush)
|
|
}
|
|
const ownsPendingState = pendingLiveInputHandleRef.current === activeHandle
|
|
if (pendingLiveInputHandleRef.current && !ownsPendingState) {
|
|
clearPendingLiveInputCommit()
|
|
}
|
|
const heldText = ownsPendingState ? heldLiveInputTextRef.current : ''
|
|
const sentText = ownsPendingState ? sentLiveInputTextRef.current : ''
|
|
const decision = getTerminalLiveAccessoryBytesDecision({ ...input, heldText, sentText })
|
|
switch (decision.kind) {
|
|
case 'send-now':
|
|
// Why: raw accessory bytes must wait behind any in-flight mirror send
|
|
// so composed Hangul reaches the PTY before follow-up controls.
|
|
return (await waitForPendingLiveInputFlush())
|
|
? { kind: 'allow-raw' }
|
|
: { kind: 'suppress-raw' }
|
|
case 'local-edit': {
|
|
const editedText = getTerminalLiveAccessoryLocalEditText({
|
|
localEdit: decision.localEdit,
|
|
fieldText: sentText + heldText
|
|
})
|
|
// Why: accessory buttons do not emit native TextInput edits, so the
|
|
// field is edited here and the mirror diff syncs the PTY echo.
|
|
setLiveInputCapture(editedText)
|
|
liveInputRef.current?.setNativeProps({ text: editedText })
|
|
applyLiveInputMirror(activeHandle, editedText)
|
|
return { kind: 'handled' }
|
|
}
|
|
case 'commit-held-then-send':
|
|
await sendTerminalLiveControlAfterPendingFlush(
|
|
() => flushPendingLiveInputText(activeHandle),
|
|
() => sendLiveTerminalInputRef.current(activeHandle, decision.bytes)
|
|
)
|
|
return { kind: 'handled' }
|
|
default:
|
|
decision satisfies never
|
|
return { kind: 'handled' }
|
|
}
|
|
},
|
|
[
|
|
activeHandle,
|
|
applyLiveInputMirror,
|
|
clearPendingLiveInputCommit,
|
|
flushPendingLiveInputText,
|
|
heldLiveInputTextRef,
|
|
liveInputRef,
|
|
liveInputTerminalHandles,
|
|
pendingLiveInputHandleRef,
|
|
sentLiveInputTextRef,
|
|
sendLiveTerminalInputRef,
|
|
setLiveInputCapture,
|
|
waitForPendingLiveInputFlush
|
|
]
|
|
)
|
|
}
|