Map mobile special keys to terminal PTY bytes (#6515)
Expand live input mapping to handle escape, tab, delete, insert, arrows, home, end, page navigation, and F1–F12 keys. - Safe Map lookup avoids collisions with object prototype methods. - Enter is excluded to prevent double-sent carriage returns. - Standardizes byte translation via buildTerminalShortcutKey.
This commit is contained in:
parent
c0f4bdd020
commit
ef292cb069
|
|
@ -19,12 +19,48 @@ describe('terminal live input', () => {
|
|||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('maps phone keyboard special keys to PTY bytes', () => {
|
||||
expect(getTerminalLiveSpecialKeyBytes('Backspace')).toBe('\x7f')
|
||||
it.each([
|
||||
['Escape', '\x1b'],
|
||||
['Esc', '\x1b'],
|
||||
['Tab', '\t'],
|
||||
['Backspace', '\x7f'],
|
||||
['Delete', '\x1b[3~'],
|
||||
['Insert', '\x1b[2~'],
|
||||
['ArrowUp', '\x1b[A'],
|
||||
['ArrowDown', '\x1b[B'],
|
||||
['ArrowLeft', '\x1b[D'],
|
||||
['ArrowRight', '\x1b[C'],
|
||||
['Home', '\x1b[H'],
|
||||
['End', '\x1b[F'],
|
||||
['PageUp', '\x1b[5~'],
|
||||
['PageDown', '\x1b[6~'],
|
||||
['F1', '\x1bOP'],
|
||||
['F2', '\x1bOQ'],
|
||||
['F3', '\x1bOR'],
|
||||
['F4', '\x1bOS'],
|
||||
['F5', '\x1b[15~'],
|
||||
['F6', '\x1b[17~'],
|
||||
['F7', '\x1b[18~'],
|
||||
['F8', '\x1b[19~'],
|
||||
['F9', '\x1b[20~'],
|
||||
['F10', '\x1b[21~'],
|
||||
['F11', '\x1b[23~'],
|
||||
['F12', '\x1b[24~']
|
||||
])('maps %s to terminal PTY bytes', (key, bytes) => {
|
||||
expect(getTerminalLiveSpecialKeyBytes(key)).toBe(bytes)
|
||||
})
|
||||
|
||||
it('leaves submitted or printable keys on their existing input paths', () => {
|
||||
expect(getTerminalLiveSpecialKeyBytes('Enter')).toBeNull()
|
||||
expect(getTerminalLiveSpecialKeyBytes('a')).toBeNull()
|
||||
})
|
||||
|
||||
it('ignores object prototype names from native key events', () => {
|
||||
expect(getTerminalLiveSpecialKeyBytes('constructor')).toBeNull()
|
||||
expect(getTerminalLiveSpecialKeyBytes('toString')).toBeNull()
|
||||
expect(getTerminalLiveSpecialKeyBytes('hasOwnProperty')).toBeNull()
|
||||
})
|
||||
|
||||
it('enforces the paste-sized byte budget', () => {
|
||||
expect(isTerminalLiveInputWithinByteLimit('hello')).toBe(true)
|
||||
expect(isTerminalLiveInputWithinByteLimit('x'.repeat(TERMINAL_LIVE_INPUT_MAX_BYTES))).toBe(true)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,67 @@
|
|||
import { buildTerminalShortcutKey } from './terminal-accessory-keys'
|
||||
|
||||
const TERMINAL_LIVE_INPUT_MAX_BYTES = 256 * 1024
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
|
||||
type TerminalLiveSpecialKeyId =
|
||||
| 'arrowDown'
|
||||
| 'arrowLeft'
|
||||
| 'arrowRight'
|
||||
| 'arrowUp'
|
||||
| 'backspace'
|
||||
| 'delete'
|
||||
| 'end'
|
||||
| 'escape'
|
||||
| 'f1'
|
||||
| 'f2'
|
||||
| 'f3'
|
||||
| 'f4'
|
||||
| 'f5'
|
||||
| 'f6'
|
||||
| 'f7'
|
||||
| 'f8'
|
||||
| 'f9'
|
||||
| 'f10'
|
||||
| 'f11'
|
||||
| 'f12'
|
||||
| 'home'
|
||||
| 'insert'
|
||||
| 'pageDown'
|
||||
| 'pageUp'
|
||||
| 'tab'
|
||||
|
||||
// Why: Enter stays on onSubmitEditing; mapping it here can double-send carriage
|
||||
// returns when native TextInput emits both submit and key events.
|
||||
const TERMINAL_LIVE_SPECIAL_KEY_IDS = new Map<string, TerminalLiveSpecialKeyId>([
|
||||
['Escape', 'escape'],
|
||||
['Esc', 'escape'],
|
||||
['Tab', 'tab'],
|
||||
['Backspace', 'backspace'],
|
||||
['Delete', 'delete'],
|
||||
['Insert', 'insert'],
|
||||
['ArrowUp', 'arrowUp'],
|
||||
['ArrowDown', 'arrowDown'],
|
||||
['ArrowLeft', 'arrowLeft'],
|
||||
['ArrowRight', 'arrowRight'],
|
||||
['Home', 'home'],
|
||||
['End', 'end'],
|
||||
['PageUp', 'pageUp'],
|
||||
['PageDown', 'pageDown'],
|
||||
['F1', 'f1'],
|
||||
['F2', 'f2'],
|
||||
['F3', 'f3'],
|
||||
['F4', 'f4'],
|
||||
['F5', 'f5'],
|
||||
['F6', 'f6'],
|
||||
['F7', 'f7'],
|
||||
['F8', 'f8'],
|
||||
['F9', 'f9'],
|
||||
['F10', 'f10'],
|
||||
['F11', 'f11'],
|
||||
['F12', 'f12']
|
||||
])
|
||||
|
||||
export type TerminalLiveInputFocusTimerRef = {
|
||||
current: ReturnType<typeof setTimeout> | null
|
||||
}
|
||||
|
|
@ -15,10 +75,11 @@ export type TerminalLiveInputDefaultResult = {
|
|||
export type TerminalLiveInputPruneResult = TerminalLiveInputDefaultResult
|
||||
|
||||
export function getTerminalLiveSpecialKeyBytes(key: string): string | null {
|
||||
if (key === 'Backspace') {
|
||||
return '\x7f'
|
||||
const shortcutKey = TERMINAL_LIVE_SPECIAL_KEY_IDS.get(key)
|
||||
if (!shortcutKey) {
|
||||
return null
|
||||
}
|
||||
return null
|
||||
return buildTerminalShortcutKey({ key: shortcutKey, modifiers: [] })?.bytes ?? null
|
||||
}
|
||||
|
||||
export function isTerminalLiveInputWithinByteLimit(
|
||||
|
|
|
|||
Loading…
Reference in New Issue