@@ -103,6 +103,7 @@ const ChatInputBox = () => {
103103 const [ suggestions , setSuggestions ] = useState < string [ ] > ( [ ] ) ;
104104 const [ highlightedIndex , setHighlightedIndex ] = useState ( - 1 ) ;
105105 const [ suggestionsOpen , setSuggestionsOpen ] = useState ( false ) ;
106+ const [ dropdownPosition , setDropdownPosition ] = useState < 'below' | 'above' > ( 'below' ) ;
106107 const suppressNextFetchRef = useRef ( false ) ;
107108 const autocompleteAbortRef = useRef < AbortController | null > ( null ) ;
108109
@@ -125,14 +126,32 @@ const ChatInputBox = () => {
125126 autocompleteAbortRef . current = controller ;
126127 try {
127128 const res = await fetch (
128- `/api/agent/autocomplete?q=${ encodeURIComponent ( query ) } ` ,
129+ `/api/agent/autocomplete?q=${ encodeURIComponent ( query ) } &limit=8 ` ,
129130 { signal : controller . signal } ,
130131 ) ;
131132 if ( ! res . ok ) return ;
132133 const data = await res . json ( ) ;
133134 const list : string [ ] = Array . isArray ( data ?. suggestions ) ? data . suggestions : [ ] ;
134135 setSuggestions ( list ) ;
135- setSuggestionsOpen ( list . length > 0 ) ;
136+ if ( list . length > 0 ) {
137+ // Calculate dropdown position based on viewport space
138+ if ( wrapperRef . current ) {
139+ const rect = wrapperRef . current . getBoundingClientRect ( ) ;
140+ const spaceBelow = window . innerHeight - rect . bottom ;
141+ const spaceAbove = rect . top ;
142+ const dropdownHeight = Math . min ( 288 , list . length * 48 ) ; // max-h-72 = 288px, ~48px per item
143+
144+ // On mobile (or when not enough space below), show above if there's more space
145+ if ( spaceBelow < dropdownHeight && spaceAbove > spaceBelow ) {
146+ setDropdownPosition ( 'above' ) ;
147+ } else {
148+ setDropdownPosition ( 'below' ) ;
149+ }
150+ }
151+ setSuggestionsOpen ( true ) ;
152+ } else {
153+ setSuggestionsOpen ( false ) ;
154+ }
136155 setHighlightedIndex ( - 1 ) ;
137156 } catch ( err : any ) {
138157 if ( err ?. name !== "AbortError" ) console . error ( "Autocomplete fetch failed" , err ) ;
@@ -391,11 +410,15 @@ const ChatInputBox = () => {
391410 // no-op; rendered outside wrapper, handled by mousedown stopPropagation below
392411 }
393412 } }
394- initial = { { opacity : 0 , y : - 4 } }
413+ initial = { { opacity : 0 , y : dropdownPosition === 'above' ? 4 : - 4 } }
395414 animate = { { opacity : 1 , y : 0 } }
396- exit = { { opacity : 0 , y : - 4 } }
415+ exit = { { opacity : 0 , y : dropdownPosition === 'above' ? 4 : - 4 } }
397416 transition = { { duration : 0.12 } }
398- className = "absolute left-2 right-2 md:left-0 md:right-0 top-full mt-2 z-20 rounded-2xl bg-gray-50 dark:bg-[#30302E] border border-bg-300 dark:border-transparent shadow-xl overflow-hidden"
417+ className = { `absolute left-2 right-2 md:left-0 md:right-0 z-20 rounded-2xl bg-gray-50 dark:bg-[#30302E] border border-bg-300 dark:border-transparent shadow-xl overflow-hidden ${
418+ dropdownPosition === 'above'
419+ ? 'bottom-full mb-2'
420+ : 'top-full mt-2'
421+ } `}
399422 onMouseDown = { ( e ) => e . preventDefault ( ) }
400423 >
401424 < ul className = "max-h-72 overflow-y-auto custom-scrollbar py-1" >
@@ -405,10 +428,10 @@ const ChatInputBox = () => {
405428 type = "button"
406429 onMouseEnter = { ( ) => setHighlightedIndex ( i ) }
407430 onClick = { ( ) => selectSuggestion ( s ) }
408- className = { `w-full text-left px-4 py-2 text-[15px] flex items-center gap-2 transition-colors ${
431+ className = { `w-full text-left px-4 py-2.5 text-[15px] flex items-center gap-3 transition-colors ${
409432 i === highlightedIndex
410433 ? 'bg-bg-200 text-text-100'
411- : 'text-text-200 hover:bg-bg-200 '
434+ : 'text-text-200 hover:bg-bg-100 dark:hover:bg-[#3A3A38] '
412435 } `}
413436 >
414437 < Search className = "w-4 h-4 text-text-400 shrink-0" />
0 commit comments