perf: clean mobile live input focus timer (#4050)
This commit is contained in:
parent
b032289056
commit
fcd267a309
|
|
@ -70,8 +70,10 @@ import {
|
|||
loadTerminalAccessoryLayout
|
||||
} from '../../../../src/terminal/terminal-accessory-layout'
|
||||
import {
|
||||
clearTerminalLiveInputFocusTimer,
|
||||
getTerminalLiveSpecialKeyBytes,
|
||||
isTerminalLiveInputWithinByteLimit
|
||||
isTerminalLiveInputWithinByteLimit,
|
||||
scheduleTerminalLiveInputFocus
|
||||
} from '../../../../src/terminal/terminal-live-input'
|
||||
import { countTerminalGestureInputSequences } from '../../../../src/terminal/terminal-gesture-input'
|
||||
import { MobileBrowserPane, type MobileBrowserTab } from '../../../../src/browser/MobileBrowserPane'
|
||||
|
|
@ -1048,6 +1050,7 @@ export default function SessionScreen() {
|
|||
const viewportMeasuredRef = useRef(false)
|
||||
const terminalRefs = useRef<Map<string, TerminalWebViewHandle>>(new Map())
|
||||
const liveInputRef = useRef<TextInput>(null)
|
||||
const liveInputFocusTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const terminalUnsubsRef = useRef<Map<string, () => void>>(new Map())
|
||||
const subscribingHandlesRef = useRef<Set<string>>(new Set())
|
||||
const initializedHandlesRef = useRef<Set<string>>(new Set())
|
||||
|
|
@ -1132,6 +1135,7 @@ export default function SessionScreen() {
|
|||
// so pending animation callbacks cannot clear a newer/unmounted surface.
|
||||
toastSeqRef.current += 1
|
||||
clearToastHideTimer()
|
||||
clearTerminalLiveInputFocusTimer(liveInputFocusTimerRef)
|
||||
}
|
||||
}, [clearToastHideTimer])
|
||||
|
||||
|
|
@ -2714,8 +2718,9 @@ export default function SessionScreen() {
|
|||
})
|
||||
setLiveInputCapture('')
|
||||
if (nextEnabled) {
|
||||
setTimeout(() => liveInputRef.current?.focus(), 50)
|
||||
scheduleTerminalLiveInputFocus(liveInputFocusTimerRef, () => liveInputRef.current?.focus())
|
||||
} else {
|
||||
clearTerminalLiveInputFocusTimer(liveInputFocusTimerRef)
|
||||
liveInputRef.current?.blur()
|
||||
}
|
||||
}, [activeHandle, liveInputTerminalHandles])
|
||||
|
|
|
|||
|
|
@ -1,11 +1,22 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
TERMINAL_LIVE_INPUT_MAX_BYTES,
|
||||
clearTerminalLiveInputFocusTimer,
|
||||
getTerminalLiveSpecialKeyBytes,
|
||||
isTerminalLiveInputWithinByteLimit
|
||||
isTerminalLiveInputWithinByteLimit,
|
||||
scheduleTerminalLiveInputFocus,
|
||||
type TerminalLiveInputFocusTimerRef
|
||||
} from './terminal-live-input'
|
||||
|
||||
function createTimerRef(): TerminalLiveInputFocusTimerRef {
|
||||
return { current: null }
|
||||
}
|
||||
|
||||
describe('terminal live input', () => {
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('maps phone keyboard special keys to PTY bytes', () => {
|
||||
expect(getTerminalLiveSpecialKeyBytes('Backspace')).toBe('\x7f')
|
||||
expect(getTerminalLiveSpecialKeyBytes('Enter')).toBeNull()
|
||||
|
|
@ -22,4 +33,32 @@ describe('terminal live input', () => {
|
|||
isTerminalLiveInputWithinByteLimit('é'.repeat(TERMINAL_LIVE_INPUT_MAX_BYTES / 2 + 1))
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('replaces pending deferred focus work', () => {
|
||||
vi.useFakeTimers()
|
||||
const timerRef = createTimerRef()
|
||||
const staleFocus = vi.fn()
|
||||
const nextFocus = vi.fn()
|
||||
|
||||
scheduleTerminalLiveInputFocus(timerRef, staleFocus)
|
||||
scheduleTerminalLiveInputFocus(timerRef, nextFocus)
|
||||
vi.runOnlyPendingTimers()
|
||||
|
||||
expect(staleFocus).not.toHaveBeenCalled()
|
||||
expect(nextFocus).toHaveBeenCalledTimes(1)
|
||||
expect(timerRef.current).toBeNull()
|
||||
})
|
||||
|
||||
it('clears pending deferred focus work', () => {
|
||||
vi.useFakeTimers()
|
||||
const timerRef = createTimerRef()
|
||||
const focus = vi.fn()
|
||||
|
||||
scheduleTerminalLiveInputFocus(timerRef, focus)
|
||||
clearTerminalLiveInputFocusTimer(timerRef)
|
||||
vi.runOnlyPendingTimers()
|
||||
|
||||
expect(focus).not.toHaveBeenCalled()
|
||||
expect(timerRef.current).toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ const TERMINAL_LIVE_INPUT_MAX_BYTES = 256 * 1024
|
|||
|
||||
const encoder = new TextEncoder()
|
||||
|
||||
export type TerminalLiveInputFocusTimerRef = {
|
||||
current: ReturnType<typeof setTimeout> | null
|
||||
}
|
||||
|
||||
export function getTerminalLiveSpecialKeyBytes(key: string): string | null {
|
||||
if (key === 'Backspace') {
|
||||
return '\x7f'
|
||||
|
|
@ -16,4 +20,26 @@ export function isTerminalLiveInputWithinByteLimit(
|
|||
return encoder.encode(text).byteLength <= maxBytes
|
||||
}
|
||||
|
||||
export function clearTerminalLiveInputFocusTimer(timerRef: TerminalLiveInputFocusTimerRef): void {
|
||||
if (timerRef.current === null) {
|
||||
return
|
||||
}
|
||||
clearTimeout(timerRef.current)
|
||||
timerRef.current = null
|
||||
}
|
||||
|
||||
export function scheduleTerminalLiveInputFocus(
|
||||
timerRef: TerminalLiveInputFocusTimerRef,
|
||||
focus: () => void,
|
||||
delayMs = 50
|
||||
): void {
|
||||
// Why: live input can be toggled during route changes; replacing the pending
|
||||
// focus timer prevents stale native TextInput focus after unmount/disable.
|
||||
clearTerminalLiveInputFocusTimer(timerRef)
|
||||
timerRef.current = setTimeout(() => {
|
||||
timerRef.current = null
|
||||
focus()
|
||||
}, delayMs)
|
||||
}
|
||||
|
||||
export { TERMINAL_LIVE_INPUT_MAX_BYTES }
|
||||
|
|
|
|||
Loading…
Reference in New Issue