Skip to content

Commit fb4ba2a

Browse files
committed
refactor(cli): consolidate keyboard handlers into single handler
- Combine 5 separate useKeyboard calls into one unified handler - Extract preventDefault helper function to reduce duplication - Improve maintainability and performance by reducing hook overhead - Add proper TypeScript typing for KeyEvent
1 parent 20bd0b3 commit fb4ba2a

File tree

1 file changed

+87
-145
lines changed

1 file changed

+87
-145
lines changed
Lines changed: 87 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useKeyboard } from '@opentui/react'
22
import { useCallback } from 'react'
33

4+
import type { KeyEvent } from '@opentui/core'
5+
46
type InputHandle = { focus: () => void }
57

68
interface KeyboardHandlersConfig {
@@ -21,6 +23,12 @@ interface KeyboardHandlersConfig {
2123
disabled?: boolean
2224
}
2325

26+
const preventDefault = (key: KeyEvent) => {
27+
if ('preventDefault' in key && typeof key.preventDefault === 'function') {
28+
key.preventDefault()
29+
}
30+
}
31+
2432
export const useKeyboardHandlers = ({
2533
isStreaming,
2634
isWaitingForResponse,
@@ -38,162 +46,96 @@ export const useKeyboardHandlers = ({
3846
historyNavDownEnabled,
3947
disabled = false,
4048
}: KeyboardHandlersConfig) => {
41-
useKeyboard(
42-
useCallback(
43-
(key) => {
44-
if (disabled) return
45-
46-
const isEscape = key.name === 'escape'
47-
const isCtrlC = key.ctrl && key.name === 'c'
48-
49-
if ((isEscape || isCtrlC) && (isStreaming || isWaitingForResponse)) {
50-
if (
51-
'preventDefault' in key &&
52-
typeof key.preventDefault === 'function'
53-
) {
54-
key.preventDefault()
55-
}
56-
57-
if (abortControllerRef.current) {
58-
abortControllerRef.current.abort()
59-
}
60-
onInterrupt()
61-
62-
return
63-
}
64-
65-
if (isCtrlC) {
66-
const shouldPrevent = onCtrlC()
67-
if (
68-
shouldPrevent &&
69-
'preventDefault' in key &&
70-
typeof key.preventDefault === 'function'
71-
) {
72-
key.preventDefault()
73-
}
74-
}
75-
},
76-
[
77-
isStreaming,
78-
isWaitingForResponse,
79-
abortControllerRef,
80-
onCtrlC,
81-
onInterrupt,
82-
disabled,
83-
],
84-
),
85-
)
86-
87-
useKeyboard(
88-
useCallback(
89-
(key) => {
90-
if (disabled) return
91-
if (!focusedAgentId) return
92-
93-
const isSpace =
94-
key.name === 'space' && !key.ctrl && !key.meta && !key.shift
95-
const isEnter =
96-
(key.name === 'return' || key.name === 'enter') &&
97-
!key.ctrl &&
98-
!key.meta &&
99-
!key.shift
100-
const isRightArrow =
101-
key.name === 'right' && !key.ctrl && !key.meta && !key.shift
102-
const isLeftArrow =
103-
key.name === 'left' && !key.ctrl && !key.meta && !key.shift
104-
105-
if (!isSpace && !isEnter && !isRightArrow && !isLeftArrow) return
106-
107-
if (
108-
'preventDefault' in key &&
109-
typeof key.preventDefault === 'function'
110-
) {
111-
key.preventDefault()
49+
const handleKeyboard = useCallback(
50+
(key: KeyEvent) => {
51+
if (disabled) return
52+
53+
const isEscape = key.name === 'escape'
54+
const isCtrlC = key.ctrl && key.name === 'c'
55+
const isUpArrow = key.name === 'up' && !key.ctrl && !key.meta && !key.shift
56+
const isDownArrow = key.name === 'down' && !key.ctrl && !key.meta && !key.shift
57+
const isShiftTab = key.shift && key.name === 'tab' && !key.ctrl && !key.meta
58+
const isSpace = key.name === 'space' && !key.ctrl && !key.meta && !key.shift
59+
const isEnter = (key.name === 'return' || key.name === 'enter') && !key.ctrl && !key.meta && !key.shift
60+
const isRightArrow = key.name === 'right' && !key.ctrl && !key.meta && !key.shift
61+
const isLeftArrow = key.name === 'left' && !key.ctrl && !key.meta && !key.shift
62+
63+
// Handle escape/ctrl+c during streaming
64+
if ((isEscape || isCtrlC) && (isStreaming || isWaitingForResponse)) {
65+
preventDefault(key)
66+
if (abortControllerRef.current) {
67+
abortControllerRef.current.abort()
11268
}
69+
onInterrupt()
11370
return
114-
},
115-
[focusedAgentId, disabled],
116-
),
117-
)
71+
}
11872

119-
useKeyboard(
120-
useCallback(
121-
(key) => {
122-
if (disabled) return
123-
if (key.name === 'escape' && focusedAgentId) {
124-
if (
125-
'preventDefault' in key &&
126-
typeof key.preventDefault === 'function'
127-
) {
128-
key.preventDefault()
129-
}
130-
setFocusedAgentId(null)
131-
setInputFocused(true)
132-
inputRef.current?.focus()
73+
// Handle ctrl+c for exit
74+
if (isCtrlC) {
75+
const shouldPrevent = onCtrlC()
76+
if (shouldPrevent) {
77+
preventDefault(key)
13378
}
134-
},
135-
[focusedAgentId, setFocusedAgentId, setInputFocused, inputRef, disabled],
136-
),
137-
)
138-
139-
// Handle chat history navigation
140-
useKeyboard(
141-
useCallback(
142-
(key) => {
143-
if (disabled) return
144-
145-
const isUpArrow =
146-
key.name === 'up' && !key.ctrl && !key.meta && !key.shift
147-
const isDownArrow =
148-
key.name === 'down' && !key.ctrl && !key.meta && !key.shift
149-
150-
if (!isUpArrow && !isDownArrow) return
79+
return
80+
}
81+
82+
// Handle escape to unfocus agent
83+
if (isEscape && focusedAgentId) {
84+
preventDefault(key)
85+
setFocusedAgentId(null)
86+
setInputFocused(true)
87+
inputRef.current?.focus()
88+
return
89+
}
15190

152-
if (
153-
'preventDefault' in key &&
154-
typeof key.preventDefault === 'function'
155-
) {
156-
key.preventDefault()
157-
}
91+
// Prevent default actions when agent is focused
92+
if (focusedAgentId && (isSpace || isEnter || isRightArrow || isLeftArrow)) {
93+
preventDefault(key)
94+
return
95+
}
15896

159-
if (isUpArrow) {
160-
if (!historyNavUpEnabled) return
97+
// Handle history navigation (up/down arrows)
98+
if (isUpArrow) {
99+
if (historyNavUpEnabled) {
100+
preventDefault(key)
161101
navigateUp()
162-
} else {
163-
if (!historyNavDownEnabled) return
164-
navigateDown()
165102
}
166-
},
167-
[
168-
historyNavUpEnabled,
169-
historyNavDownEnabled,
170-
navigateUp,
171-
navigateDown,
172-
disabled,
173-
],
174-
),
175-
)
176-
177-
useKeyboard(
178-
useCallback(
179-
(key) => {
180-
if (disabled) return
181-
182-
const isShiftTab =
183-
key.shift && key.name === 'tab' && !key.ctrl && !key.meta
184-
185-
if (!isShiftTab) return
103+
return
104+
}
186105

187-
if (
188-
'preventDefault' in key &&
189-
typeof key.preventDefault === 'function'
190-
) {
191-
key.preventDefault()
106+
if (isDownArrow) {
107+
if (historyNavDownEnabled) {
108+
preventDefault(key)
109+
navigateDown()
192110
}
111+
return
112+
}
193113

114+
// Handle shift+tab for mode toggle
115+
if (isShiftTab) {
116+
preventDefault(key)
194117
toggleAgentMode()
195-
},
196-
[toggleAgentMode, disabled],
197-
),
118+
return
119+
}
120+
},
121+
[
122+
disabled,
123+
isStreaming,
124+
isWaitingForResponse,
125+
abortControllerRef,
126+
onInterrupt,
127+
onCtrlC,
128+
focusedAgentId,
129+
setFocusedAgentId,
130+
setInputFocused,
131+
inputRef,
132+
historyNavUpEnabled,
133+
historyNavDownEnabled,
134+
navigateUp,
135+
navigateDown,
136+
toggleAgentMode,
137+
],
198138
)
139+
140+
useKeyboard(handleKeyboard)
199141
}

0 commit comments

Comments
 (0)