@@ -14,6 +14,7 @@ import { BORDER_CHARS } from '../utils/ui-constants'
1414import type { useTheme } from '../hooks/use-theme'
1515import type { InputValue } from '../state/chat-store'
1616import type { AgentMode } from '../utils/constants'
17+ import { useEvent } from '../hooks/use-event'
1718
1819type Theme = ReturnType < typeof useTheme >
1920
@@ -51,6 +52,7 @@ interface ChatInputBarProps {
5152 separatorWidth : number
5253 shouldCenterInputVertically : boolean
5354 inputBoxTitle : string | undefined
55+ isCompactHeight : boolean
5456
5557 // Feedback mode
5658 feedbackMode : boolean
@@ -83,6 +85,7 @@ export const ChatInputBar = ({
8385 separatorWidth,
8486 shouldCenterInputVertically,
8587 inputBoxTitle,
88+ isCompactHeight,
8689 feedbackMode,
8790 handleExitFeedback,
8891 handleSubmit,
@@ -195,6 +198,45 @@ export const ChatInputBar = ({
195198 inputMode === 'default' ? inputPlaceholder : modeConfig . placeholder
196199 const borderColor = theme [ modeConfig . color ]
197200
201+ // Shared key intercept handler for suggestion menu navigation
202+ const handleKeyIntercept = useEvent (
203+ ( key : {
204+ name ?: string
205+ shift ?: boolean
206+ ctrl ?: boolean
207+ meta ?: boolean
208+ option ?: boolean
209+ } ) => {
210+ // Intercept navigation keys when suggestion menu is active
211+ // The useChatKeyboard hook will handle menu selection/navigation
212+ const hasSuggestions = hasSlashSuggestions || hasMentionSuggestions
213+ if ( ! hasSuggestions ) return false
214+
215+ const isPlainEnter =
216+ ( key . name === 'return' || key . name === 'enter' ) &&
217+ ! key . shift &&
218+ ! key . ctrl &&
219+ ! key . meta &&
220+ ! key . option
221+ const isTab = key . name === 'tab' && ! key . ctrl && ! key . meta && ! key . option
222+ const isUpDown =
223+ ( key . name === 'up' || key . name === 'down' ) &&
224+ ! key . ctrl &&
225+ ! key . meta &&
226+ ! key . option
227+
228+ // Don't intercept Up/Down when user is navigating history
229+ if ( isUpDown && lastEditDueToNav ) {
230+ return false
231+ }
232+
233+ if ( isPlainEnter || isTab || isUpDown ) {
234+ return true
235+ }
236+ return false
237+ } ,
238+ )
239+
198240 if ( askUserState ) {
199241 return (
200242 < box
@@ -233,6 +275,67 @@ export const ChatInputBar = ({
233275 )
234276 }
235277
278+ // Compact mode: no border, minimal chrome, supports menus and multiline
279+ if ( isCompactHeight ) {
280+ const compactMaxHeight = Math . floor ( terminalHeight / 2 )
281+ return (
282+ < >
283+ { hasSlashSuggestions ? (
284+ < SuggestionMenu
285+ items = { slashSuggestionItems }
286+ selectedIndex = { slashSelectedIndex }
287+ maxVisible = { 5 }
288+ prefix = "/"
289+ />
290+ ) : null }
291+ { hasMentionSuggestions ? (
292+ < SuggestionMenu
293+ items = { [ ...agentSuggestionItems , ...fileSuggestionItems ] }
294+ selectedIndex = { agentSelectedIndex }
295+ maxVisible = { 5 }
296+ prefix = "@"
297+ />
298+ ) : null }
299+ < box
300+ style = { {
301+ flexDirection : 'row' ,
302+ alignItems : 'flex-start' ,
303+ width : '100%' ,
304+ paddingLeft : 1 ,
305+ paddingRight : 1 ,
306+ backgroundColor : theme . surface ,
307+ } }
308+ >
309+ { modeConfig . icon && (
310+ < box
311+ style = { {
312+ flexShrink : 0 ,
313+ paddingRight : 1 ,
314+ } }
315+ >
316+ < text style = { { fg : theme [ modeConfig . color ] } } >
317+ { modeConfig . icon }
318+ </ text >
319+ </ box >
320+ ) }
321+ < MultilineInput
322+ value = { inputValue }
323+ onChange = { handleInputChange }
324+ onSubmit = { handleSubmit }
325+ onKeyIntercept = { handleKeyIntercept }
326+ placeholder = { effectivePlaceholder }
327+ focused = { inputFocused && ! feedbackMode }
328+ maxHeight = { compactMaxHeight }
329+ width = { adjustedInputWidth }
330+ ref = { inputRef }
331+ cursorPosition = { cursorPosition }
332+ />
333+ </ box >
334+ < InputModeBanner />
335+ </ >
336+ )
337+ }
338+
236339 return (
237340 < >
238341 < box
@@ -301,43 +404,11 @@ export const ChatInputBar = ({
301404 value = { inputValue }
302405 onChange = { handleInputChange }
303406 onSubmit = { handleSubmit }
304- onKeyIntercept = { ( key ) => {
305- // Intercept navigation keys when suggestion menu is active
306- // The useChatKeyboard hook will handle menu selection/navigation
307- const hasSuggestions =
308- hasSlashSuggestions || hasMentionSuggestions
309- if ( ! hasSuggestions ) return false
310-
311- const isPlainEnter =
312- ( key . name === 'return' || key . name === 'enter' ) &&
313- ! key . shift &&
314- ! key . ctrl &&
315- ! key . meta &&
316- ! key . option
317- const isTab =
318- key . name === 'tab' && ! key . ctrl && ! key . meta && ! key . option
319- const isUpDown =
320- ( key . name === 'up' || key . name === 'down' ) &&
321- ! key . ctrl &&
322- ! key . meta &&
323- ! key . option
324-
325- // Don't intercept Up/Down when user is navigating history
326- // (lastEditDueToNav is true), let them continue paging through
327- if ( isUpDown && lastEditDueToNav ) {
328- return false
329- }
330-
331- if ( isPlainEnter || isTab || isUpDown ) {
332- return true // Prevent default, let useChatKeyboard handle it
333- }
334- return false
335- } }
407+ onKeyIntercept = { handleKeyIntercept }
336408 placeholder = { effectivePlaceholder }
337409 focused = { inputFocused && ! feedbackMode }
338410 maxHeight = { Math . floor ( terminalHeight / 2 ) }
339411 width = { adjustedInputWidth }
340-
341412 ref = { inputRef }
342413 cursorPosition = { cursorPosition }
343414 />
0 commit comments