11"use client"
22
3- import { memo , useState , useRef , useCallback } from "react"
3+ import { memo , useState , useRef , useCallback , useMemo } from "react"
44import { useRouter } from "next/navigation"
55import { useChat } from "@/context/ChatContext"
66import { useTypewriter } from "@/hooks/useTypewriter"
@@ -55,6 +55,18 @@ const ChatInput = memo(function ChatInput() {
5555 }
5656 }
5757
58+ /* Detect @mention trigger in input text */
59+ const atMention = useMemo ( ( ) => {
60+ const match = inputValue . match ( / @ ( \w * ) $ / )
61+ return match ? { search : match [ 1 ] , fullMatch : match [ 0 ] } : null
62+ } , [ inputValue ] )
63+
64+ const handleTriggerConsumed = useCallback ( ( ) => {
65+ if ( atMention ) {
66+ setInputValue ( inputValue . slice ( 0 , inputValue . length - atMention . fullMatch . length ) . trimEnd ( ) )
67+ }
68+ } , [ atMention , inputValue , setInputValue ] )
69+
5870 const [ inputFocused , setInputFocused ] = useState ( false )
5971 const [ attachHover , setAttachHover ] = useState ( false )
6072 const [ globeHover , setGlobeHover ] = useState ( false )
@@ -364,7 +376,7 @@ const ChatInput = memo(function ChatInput() {
364376 flexShrink : 0 ,
365377 } }
366378 >
367- { /* Left: Attach + Web search */ }
379+ { /* Left: Attach + @ + Web search */ }
368380 < div style = { { display : "flex" , alignItems : "center" , gap : "14px" } } >
369381 { /* Attach */ }
370382 < button
@@ -378,6 +390,15 @@ const ChatInput = memo(function ChatInput() {
378390 < path d = "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" />
379391 </ svg >
380392 </ button >
393+ { /* @ Content type picker */ }
394+ < ContentTypePicker
395+ selected = { inputFeatures . requestedContentTypes ?? [ ] }
396+ onChange = { ( ids ) => { setInputFeatures ( { requestedContentTypes : ids } ) ; writeStoredContentTypes ( ids ) } }
397+ isDark = { isDark }
398+ COLORS = { COLORS }
399+ triggerSearch = { atMention ?. search }
400+ onTriggerConsumed = { handleTriggerConsumed }
401+ />
381402 { /* Web search toggle */ }
382403 < button
383404 onClick = { ( ) => setInputFeatures ( { webSearch : ! inputFeatures . webSearch } ) }
@@ -405,13 +426,6 @@ const ChatInput = memo(function ChatInput() {
405426 < path d = "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
406427 </ svg >
407428 </ button >
408- { /* @ Content type picker */ }
409- < ContentTypePicker
410- selected = { inputFeatures . requestedContentTypes ?? [ ] }
411- onChange = { ( ids ) => { setInputFeatures ( { requestedContentTypes : ids } ) ; writeStoredContentTypes ( ids ) } }
412- isDark = { isDark }
413- COLORS = { COLORS }
414- />
415429 </ div >
416430
417431 { /* Right: Model + Length + Mic + Send */ }
0 commit comments