mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(terminal): tell a Windows IME shifted jamo apart from the committing Enter
Windows reports every IME-consumed keydown as key 'Process' with keyCode 229, so isTerminalImeProcessEnter also matched a shifted jamo (ㅃ, ㄲ, ㅆ) typed mid-composition. keyboard-handlers then rewrote that keydown to key: 'Enter', the Shift+Enter rule matched, and \x1b\r reached the PTY right behind the committed syllable. Only `code` separates the two shapes, and the predicate never read it. The repo's own fixtures already record both: the committing press as Process/Enter/229, and an observed IBus jamo keydown as Process/KeyG/229.
This commit is contained in:
@@ -295,6 +295,34 @@ describe('Windows IME keyboard ownership', () => {
|
||||
hook.unmount()
|
||||
harness.dispose()
|
||||
})
|
||||
|
||||
// Windows gives a shifted jamo the same Process/229/shiftKey shape as the committing
|
||||
// Enter, so treating it as Enter injects a Shift+Enter newline into ordinary Hangul.
|
||||
it.each([
|
||||
{ code: 'KeyQ', jamo: 'ㅃ' },
|
||||
{ code: 'KeyE', jamo: 'ㄸ' }
|
||||
])('does not read a shifted $jamo as the committing Enter', ({ code, jamo }) => {
|
||||
const harness = createHarness()
|
||||
const hook = renderHook(() => useTerminalKeyboardShortcuts(harness.deps))
|
||||
harness.startComposition()
|
||||
const shiftedJamo = keyboardEvent('keydown', {
|
||||
key: 'Process',
|
||||
code,
|
||||
keyCode: 229,
|
||||
timeStamp: 10,
|
||||
isComposing: true,
|
||||
shiftKey: true
|
||||
})
|
||||
|
||||
harness.terminalInput.dispatchEvent(shiftedJamo)
|
||||
harness.endComposition(jamo)
|
||||
vi.advanceTimersByTime(250)
|
||||
|
||||
expect(harness.ptyWrites).toEqual([jamo])
|
||||
expect(shiftedJamo.defaultPrevented).toBe(false)
|
||||
hook.unmount()
|
||||
harness.dispose()
|
||||
})
|
||||
})
|
||||
|
||||
// The committing press repeats and the direct shortcut write races xterm's flush for
|
||||
|
||||
@@ -325,6 +325,7 @@ describe('isTerminalImeProcessEnter', () => {
|
||||
const event = (overrides: Partial<KeyboardEvent> = {}) =>
|
||||
({
|
||||
key: 'Process',
|
||||
code: 'Enter',
|
||||
keyCode: 229,
|
||||
metaKey: false,
|
||||
ctrlKey: false,
|
||||
@@ -333,7 +334,7 @@ describe('isTerminalImeProcessEnter', () => {
|
||||
...overrides
|
||||
}) as KeyboardEvent
|
||||
|
||||
it.each([{ shiftKey: true }, { shiftKey: false, ctrlKey: true }])(
|
||||
it.each([{ shiftKey: true }, { shiftKey: false, ctrlKey: true }, { code: 'NumpadEnter' }])(
|
||||
'recognizes a Windows IME modifier Enter reported as Process',
|
||||
(modifiers) => {
|
||||
expect(isTerminalImeProcessEnter(event(modifiers))).toBe(true)
|
||||
@@ -345,7 +346,12 @@ describe('isTerminalImeProcessEnter', () => {
|
||||
{ keyCode: 13 },
|
||||
{ shiftKey: false },
|
||||
{ ctrlKey: true },
|
||||
{ altKey: true }
|
||||
{ altKey: true },
|
||||
// Windows hands every IME-consumed keydown the same Process/229 shape, so a shifted
|
||||
// jamo (ㅃ on KeyQ) differs from the committing Enter only by `code`.
|
||||
{ code: 'KeyQ' },
|
||||
{ code: 'Digit3' },
|
||||
{ code: '' }
|
||||
])('rejects a non-IME or ambiguous Process key', (override) => {
|
||||
expect(isTerminalImeProcessEnter(event(override))).toBe(false)
|
||||
})
|
||||
|
||||
@@ -108,11 +108,17 @@ export function getTerminalImeModifiedEnterKind(
|
||||
}
|
||||
|
||||
export function isTerminalImeProcessEnter(
|
||||
event: Pick<KeyboardEvent, 'key' | 'keyCode' | 'metaKey' | 'ctrlKey' | 'altKey' | 'shiftKey'>
|
||||
event: Pick<
|
||||
KeyboardEvent,
|
||||
'key' | 'code' | 'keyCode' | 'metaKey' | 'ctrlKey' | 'altKey' | 'shiftKey'
|
||||
>
|
||||
): boolean {
|
||||
// Why: Windows reports every IME-consumed keydown as Process/229, so a shifted jamo
|
||||
// (ㅃ, ㄲ) matches the modifier check too; only `code` tells it apart from Enter.
|
||||
return (
|
||||
event.key === 'Process' &&
|
||||
event.keyCode === 229 &&
|
||||
(event.code === 'Enter' || event.code === 'NumpadEnter') &&
|
||||
getTerminalImeModifiedEnterKind(event) !== null
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user