@@ -20,7 +20,7 @@ import {
2020 SearchResult ,
2121} from "@src/utils/context-mentions"
2222import { cn } from "@src/lib/utils"
23- import { convertToMentionPath } from "@src/utils/path-mentions"
23+ import { convertToMentionPath , escapeSpaces } from "@src/utils/path-mentions"
2424import { StandardTooltip } from "@src/components/ui"
2525
2626import Thumbnails from "../common/Thumbnails"
@@ -293,7 +293,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
293293 } , [ showContextMenu , setShowContextMenu ] )
294294
295295 const handleMentionSelect = useCallback (
296- ( type : ContextMenuOptionType , value ?: string ) => {
296+ ( type : ContextMenuOptionType , value ?: string , trigger ?: "Enter" | "Tab" | "Click" ) => {
297297 if ( type === ContextMenuOptionType . NoResults ) {
298298 return
299299 }
@@ -341,6 +341,69 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
341341 }
342342 }
343343
344+ // Special handling for folder drill-in via Tab only: keep picker open and do not add trailing space
345+ if ( type === ContextMenuOptionType . Folder && value && textAreaRef . current && trigger === "Tab" ) {
346+ // Ensure trailing slash and escape spaces like insertMention does
347+ let insertValue = value . endsWith ( "/" ) ? value : value + "/"
348+ if ( insertValue . startsWith ( "/" ) && insertValue . includes ( " " ) && ! insertValue . includes ( "\\ " ) ) {
349+ insertValue = escapeSpaces ( insertValue )
350+ }
351+
352+ const text = textAreaRef . current . value
353+ const position = cursorPosition
354+ const beforeCursor = text . slice ( 0 , position )
355+ const afterCursor = text . slice ( position )
356+ const lastAtIndex = beforeCursor . lastIndexOf ( "@" )
357+
358+ // Mirror insertMention behavior for replacing after '@' without adding a trailing space
359+ const afterCursorContent = / ^ [ a - z A - Z 0 - 9 \s ] * $ / . test ( afterCursor )
360+ ? afterCursor . replace ( / ^ [ ^ \s ] * / , "" )
361+ : afterCursor
362+
363+ let newValue : string
364+ let mentionIndex : number
365+
366+ if ( lastAtIndex !== - 1 ) {
367+ const beforeMention = text . slice ( 0 , lastAtIndex )
368+ newValue = beforeMention + "@" + insertValue + afterCursorContent
369+ mentionIndex = lastAtIndex
370+ } else {
371+ newValue = beforeCursor + "@" + insertValue + afterCursor
372+ mentionIndex = position
373+ }
374+
375+ setInputValue ( newValue )
376+
377+ // Place caret right after the inserted folder path (after trailing slash)
378+ const newCursorPos = mentionIndex + 1 + insertValue . length
379+ setCursorPosition ( newCursorPos )
380+ setIntendedCursorPosition ( newCursorPos )
381+
382+ // Keep the context menu open and immediately search within the folder
383+ setShowContextMenu ( true )
384+ setSelectedType ( null )
385+ const folderQuery = insertValue . replace ( / ^ \/ + / , "" )
386+ setSearchQuery ( folderQuery )
387+ setSelectedMenuIndex ( 0 )
388+
389+ // Trigger immediate search (no debounce) to repopulate with folder children
390+ const reqId = Math . random ( ) . toString ( 36 ) . substring ( 2 , 9 )
391+ setSearchRequestId ( reqId )
392+ setSearchLoading ( true )
393+ vscode . postMessage ( {
394+ type : "searchFiles" ,
395+ query : unescapeSpaces ( folderQuery ) ,
396+ requestId : reqId ,
397+ } )
398+
399+ // Position caret without blurring to avoid closing the menu
400+ if ( textAreaRef . current ) {
401+ textAreaRef . current . setSelectionRange ( newCursorPos , newCursorPos )
402+ }
403+
404+ return
405+ }
406+
344407 setShowContextMenu ( false )
345408 setSelectedType ( null )
346409
@@ -454,7 +517,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
454517 selectedOption . type !== ContextMenuOptionType . NoResults &&
455518 selectedOption . type !== ContextMenuOptionType . SectionHeader
456519 ) {
457- handleMentionSelect ( selectedOption . type , selectedOption . value )
520+ handleMentionSelect ( selectedOption . type , selectedOption . value , event . key as "Enter" | "Tab" )
458521 }
459522 return
460523 }
@@ -575,7 +638,8 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
575638 } else {
576639 // Existing @ mention handling.
577640 const lastAtIndex = newValue . lastIndexOf ( "@" , newCursorPosition - 1 )
578- const query = newValue . slice ( lastAtIndex + 1 , newCursorPosition )
641+ const rawQuery = newValue . slice ( lastAtIndex + 1 , newCursorPosition )
642+ const query = rawQuery . startsWith ( "/" ) ? rawQuery . slice ( 1 ) : rawQuery
579643 setSearchQuery ( query )
580644
581645 // Send file search request if query is not empty.
0 commit comments