11import { useKeyboard } from '@opentui/react'
22import { useCallback } from 'react'
33
4+ import type { KeyEvent } from '@opentui/core'
5+
46type InputHandle = { focus : ( ) => void }
57
68interface 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+
2432export 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