11'use client' ;
22
3- import { Button } from "@/components/ui/button" ;
43import { Separator } from "@/components/ui/separator" ;
54import { ChatBox } from "@/features/chat/components/chatBox" ;
65import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar" ;
76import { LanguageModelInfo } from "@/features/chat/types" ;
87import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread" ;
9- import { resetEditor } from "@/features/chat/utils" ;
10- import { useDomain } from "@/hooks/useDomain" ;
118import { RepositoryQuery , SearchContextQuery } from "@/lib/types" ;
12- import { getDisplayTime } from "@/lib/utils" ;
13- import { BrainIcon , FileIcon , LucideIcon , SearchIcon } from "lucide-react" ;
14- import Link from "next/link" ;
15- import { ReactNode , useCallback , useEffect , useRef , useState } from "react" ;
16- import { ReactEditor , useSlate } from "slate-react" ;
9+ import { useState } from "react" ;
1710import { SearchModeSelector , SearchModeSelectorProps } from "./toolbar" ;
1811import { useLocalStorage } from "usehooks-ts" ;
1912import { ContextItem } from "@/features/chat/components/chatBox/contextSelector" ;
20-
21- // @todo : we should probably rename this to a different type since it sort-of clashes
22- // with the Suggestion system we have built into the chat box.
23- type SuggestionType = "understand" | "find" | "summarize" ;
24-
25- const suggestionTypes : Record < SuggestionType , {
26- icon : LucideIcon ;
27- title : string ;
28- description : string ;
29- } > = {
30- understand : {
31- icon : BrainIcon ,
32- title : "Understand" ,
33- description : "Understand the codebase" ,
34- } ,
35- find : {
36- icon : SearchIcon ,
37- title : "Find" ,
38- description : "Find the codebase" ,
39- } ,
40- summarize : {
41- icon : FileIcon ,
42- title : "Summarize" ,
43- description : "Summarize the codebase" ,
44- } ,
45- }
46-
47-
48- const Highlight = ( { children } : { children : React . ReactNode } ) => {
49- return (
50- < span className = "text-highlight" >
51- { children }
52- </ span >
53- )
54- }
55-
56- const suggestions : Record < SuggestionType , {
57- queryText : string ;
58- queryNode ?: ReactNode ;
59- openRepoSelector ?: boolean ;
60- } [ ] > = {
61- understand : [
62- {
63- queryText : "How does authentication work in this codebase?" ,
64- openRepoSelector : true ,
65- } ,
66- {
67- queryText : "How are API endpoints structured and organized?" ,
68- openRepoSelector : true ,
69- } ,
70- {
71- queryText : "How does the build and deployment process work?" ,
72- openRepoSelector : true ,
73- } ,
74- {
75- queryText : "How is error handling implemented across the application?" ,
76- openRepoSelector : true ,
77- } ,
78- ] ,
79- find : [
80- {
81- queryText : "Find examples of different logging libraries used throughout the codebase." ,
82- } ,
83- {
84- queryText : "Find examples of potential security vulnerabilities or authentication issues." ,
85- } ,
86- {
87- queryText : "Find examples of API endpoints and route handlers." ,
88- }
89- ] ,
90- summarize : [
91- {
92- queryText : "Summarize the purpose of this file @file:" ,
93- queryNode : < span > Summarize the purpose of this file < Highlight > @file:</ Highlight > </ span >
94- } ,
95- {
96- queryText : "Summarize the project structure and architecture." ,
97- openRepoSelector : true ,
98- } ,
99- {
100- queryText : "Provide a quick start guide for ramping up on this codebase." ,
101- openRepoSelector : true ,
102- }
103- ] ,
104- }
105-
106- const MAX_RECENT_CHAT_HISTORY_COUNT = 10 ;
107-
13+ import { DemoExamples } from "@/types" ;
14+ import { AskSourcebotDemoCards } from "./askSourcebotDemoCards" ;
10815
10916interface AgenticSearchProps {
11017 searchModeSelectorProps : SearchModeSelectorProps ;
@@ -116,49 +23,23 @@ interface AgenticSearchProps {
11623 createdAt : Date ;
11724 name : string | null ;
11825 } [ ] ;
26+ demoExamples : DemoExamples | undefined ;
11927}
12028
12129export const AgenticSearch = ( {
12230 searchModeSelectorProps,
12331 languageModels,
12432 repos,
12533 searchContexts,
126- chatHistory ,
34+ demoExamples ,
12735} : AgenticSearchProps ) => {
128- const [ selectedSuggestionType , _setSelectedSuggestionType ] = useState < SuggestionType | undefined > ( undefined ) ;
12936 const { createNewChatThread, isLoading } = useCreateNewChatThread ( ) ;
130- const dropdownRef = useRef < HTMLDivElement > ( null ) ;
131- const editor = useSlate ( ) ;
13237 const [ selectedItems , setSelectedItems ] = useLocalStorage < ContextItem [ ] > ( "selectedContextItems" , [ ] , { initializeWithValue : false } ) ;
133- const domain = useDomain ( ) ;
13438 const [ isContextSelectorOpen , setIsContextSelectorOpen ] = useState ( false ) ;
13539
136- const setSelectedSuggestionType = useCallback ( ( type : SuggestionType | undefined ) => {
137- _setSelectedSuggestionType ( type ) ;
138- if ( type ) {
139- ReactEditor . focus ( editor ) ;
140- }
141- } , [ editor , _setSelectedSuggestionType ] ) ;
142-
143- // Close dropdown when clicking outside
144- useEffect ( ( ) => {
145- function handleClickOutside ( event : MouseEvent ) {
146- if (
147- ! dropdownRef . current ?. contains ( event . target as Node )
148- ) {
149- setSelectedSuggestionType ( undefined ) ;
150- }
151- }
152-
153- document . addEventListener ( "mousedown" , handleClickOutside )
154- return ( ) => document . removeEventListener ( "mousedown" , handleClickOutside )
155- } , [ setSelectedSuggestionType ] ) ;
156-
15740 return (
158- < div className = "flex flex-col items-center w-full max-w-[800px]" >
159- < div
160- className = "mt-4 w-full border rounded-md shadow-sm"
161- >
41+ < div className = "flex flex-col items-center w-full" >
42+ < div className = "mt-4 w-full border rounded-md shadow-sm max-w-[800px]" >
16243 < ChatBox
16344 onSubmit = { ( children ) => {
16445 createNewChatThread ( children , selectedItems ) ;
@@ -187,111 +68,18 @@ export const AgenticSearch = ({
18768 className = "ml-auto"
18869 />
18970 </ div >
190-
191- { selectedSuggestionType && (
192- < div
193- ref = { dropdownRef }
194- className = "w-full absolute top-10 z-10 drop-shadow-2xl bg-background border rounded-md p-2"
195- >
196- < p className = "text-muted-foreground text-sm mb-2" >
197- { suggestionTypes [ selectedSuggestionType ] . title }
198- </ p >
199- { suggestions [ selectedSuggestionType ] . map ( ( { queryText, queryNode, openRepoSelector } , index ) => (
200- < div
201- key = { index }
202- className = "flex flex-row items-center gap-2 cursor-pointer hover:bg-muted rounded-md px-1 py-0.5"
203- onClick = { ( ) => {
204- resetEditor ( editor ) ;
205- editor . insertText ( queryText ) ;
206- setSelectedSuggestionType ( undefined ) ;
207-
208- if ( openRepoSelector ) {
209- setIsContextSelectorOpen ( true ) ;
210- } else {
211- ReactEditor . focus ( editor ) ;
212- }
213- } }
214- >
215- < SearchIcon className = "w-4 h-4" />
216- { queryNode ?? queryText }
217- </ div >
218- ) ) }
219- </ div >
220- ) }
22171 </ div >
22272 </ div >
223- < div className = "flex flex-col items-center w-fit gap-6 mt-8 relative" >
224- < div className = "flex flex-row items-center gap-4" >
225- { Object . entries ( suggestionTypes ) . map ( ( [ type , suggestion ] , index ) => (
226- < ExampleButton
227- key = { index }
228- Icon = { suggestion . icon }
229- title = { suggestion . title }
230- onClick = { ( ) => {
231- setSelectedSuggestionType ( type as SuggestionType ) ;
232- } }
233- />
234- ) ) }
235- </ div >
236- </ div >
237- { chatHistory . length > 0 && (
238- < div className = "flex flex-col items-center w-[80%]" >
239- < Separator className = "my-6" />
240- < span className = "font-semibold mb-2" > Recent conversations</ span >
241- < div
242- className = "flex flex-col gap-1 w-full"
243- >
244- { chatHistory
245- . slice ( 0 , MAX_RECENT_CHAT_HISTORY_COUNT )
246- . map ( ( chat ) => (
247- < Link
248- key = { chat . id }
249- className = "flex flex-row items-center justify-between gap-1 w-full rounded-md hover:bg-muted px-2 py-0.5 cursor-pointer group"
250- href = { `/${ domain } /chat/${ chat . id } ` }
251- >
252- < span className = "text-sm text-muted-foreground group-hover:text-foreground" >
253- { chat . name ?? "Untitled Chat" }
254- </ span >
255- < span className = "text-sm text-muted-foreground group-hover:text-foreground" >
256- { getDisplayTime ( chat . createdAt ) }
257- </ span >
258- </ Link >
259- ) ) }
260- </ div >
261- { chatHistory . length > MAX_RECENT_CHAT_HISTORY_COUNT && (
262- < Link
263- href = { `/${ domain } /chat` }
264- className = "text-sm text-link hover:underline mt-6"
265- >
266- View all
267- </ Link >
268- ) }
269- </ div >
270- ) }
271- </ div >
272- )
273- }
274-
27573
276- interface ExampleButtonProps {
277- Icon : LucideIcon ;
278- title : string ;
279- onClick : ( ) => void ;
280- }
281-
282- const ExampleButton = ( {
283- Icon,
284- title,
285- onClick,
286- } : ExampleButtonProps ) => {
287- return (
288- < Button
289- variant = "secondary"
290- onClick = { onClick }
291- className = "h-9"
292- >
293- < Icon className = "w-4 h-4" />
294- { title }
295- </ Button >
74+ { demoExamples && (
75+ < AskSourcebotDemoCards
76+ demoExamples = { demoExamples }
77+ selectedItems = { selectedItems }
78+ setSelectedItems = { setSelectedItems }
79+ searchContexts = { searchContexts }
80+ repos = { repos }
81+ />
82+ ) }
83+ </ div >
29684 )
297- }
85+ }
0 commit comments