'use client'; import { Button } from "@/components/ui/button"; import { Separator } from "@/components/ui/separator"; import { ChatBox } from "@/features/chat/components/chatBox"; import { ChatBoxToolbar } from "@/features/chat/components/chatBox/chatBoxToolbar"; import { LanguageModelInfo } from "@/features/chat/types"; import { useCreateNewChatThread } from "@/features/chat/useCreateNewChatThread"; import { resetEditor } from "@/features/chat/utils"; import { useDomain } from "@/hooks/useDomain"; import { RepositoryQuery } from "@/lib/types"; import { getDisplayTime } from "@/lib/utils"; import { BrainIcon, FileIcon, LucideIcon, SearchIcon } from "lucide-react"; import Link from "next/link"; import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; import { ReactEditor, useSlate } from "slate-react"; import { SearchModeSelector, SearchModeSelectorProps } from "./toolbar"; import { useLocalStorage } from "usehooks-ts"; // @todo: we should probably rename this to a different type since it sort-of clashes // with the Suggestion system we have built into the chat box. type SuggestionType = "understand" | "find" | "summarize"; const suggestionTypes: Record = { understand: { icon: BrainIcon, title: "Understand", description: "Understand the codebase", }, find: { icon: SearchIcon, title: "Find", description: "Find the codebase", }, summarize: { icon: FileIcon, title: "Summarize", description: "Summarize the codebase", }, } const Highlight = ({ children }: { children: React.ReactNode }) => { return ( {children} ) } const suggestions: Record = { understand: [ { queryText: "How does authentication work in this codebase?", openRepoSelector: true, }, { queryText: "How are API endpoints structured and organized?", openRepoSelector: true, }, { queryText: "How does the build and deployment process work?", openRepoSelector: true, }, { queryText: "How is error handling implemented across the application?", openRepoSelector: true, }, ], find: [ { queryText: "Find examples of different logging libraries used throughout the codebase.", }, { queryText: "Find examples of potential security vulnerabilities or authentication issues.", }, { queryText: "Find examples of API endpoints and route handlers.", } ], summarize: [ { queryText: "Summarize the purpose of this file @file:", queryNode: Summarize the purpose of this file @file: }, { queryText: "Summarize the project structure and architecture.", openRepoSelector: true, }, { queryText: "Provide a quick start guide for ramping up on this codebase.", openRepoSelector: true, } ], } const MAX_RECENT_CHAT_HISTORY_COUNT = 10; interface AgenticSearchProps { searchModeSelectorProps: SearchModeSelectorProps; languageModels: LanguageModelInfo[]; repos: RepositoryQuery[]; chatHistory: { id: string; createdAt: Date; name: string | null; }[]; } export const AgenticSearch = ({ searchModeSelectorProps, languageModels, repos, chatHistory, }: AgenticSearchProps) => { const [selectedSuggestionType, _setSelectedSuggestionType] = useState(undefined); const { createNewChatThread, isLoading } = useCreateNewChatThread(); const dropdownRef = useRef(null); const editor = useSlate(); const [selectedRepos, setSelectedRepos] = useLocalStorage("selectedRepos", []); const domain = useDomain(); const [isRepoSelectorOpen, setIsRepoSelectorOpen] = useState(false); const setSelectedSuggestionType = useCallback((type: SuggestionType | undefined) => { _setSelectedSuggestionType(type); if (type) { ReactEditor.focus(editor); } }, [editor, _setSelectedSuggestionType]); // Close dropdown when clicking outside useEffect(() => { function handleClickOutside(event: MouseEvent) { if ( !dropdownRef.current?.contains(event.target as Node) ) { setSelectedSuggestionType(undefined); } } document.addEventListener("mousedown", handleClickOutside) return () => document.removeEventListener("mousedown", handleClickOutside) }, [setSelectedSuggestionType]); return (
{ createNewChatThread(children, selectedRepos); }} className="min-h-[50px]" isRedirecting={isLoading} languageModels={languageModels} selectedRepos={selectedRepos} onRepoSelectorOpenChanged={setIsRepoSelectorOpen} />
{selectedSuggestionType && (

{suggestionTypes[selectedSuggestionType].title}

{suggestions[selectedSuggestionType].map(({ queryText, queryNode, openRepoSelector }, index) => (
{ resetEditor(editor); editor.insertText(queryText); setSelectedSuggestionType(undefined); if (openRepoSelector) { setIsRepoSelectorOpen(true); } else { ReactEditor.focus(editor); } }} > {queryNode ?? queryText}
))}
)}
{Object.entries(suggestionTypes).map(([type, suggestion], index) => ( { setSelectedSuggestionType(type as SuggestionType); }} /> ))}
{chatHistory.length > 0 && (
Recent conversations
{chatHistory .slice(0, MAX_RECENT_CHAT_HISTORY_COUNT) .map((chat) => ( {chat.name ?? "Untitled Chat"} {getDisplayTime(chat.createdAt)} ))}
{chatHistory.length > MAX_RECENT_CHAT_HISTORY_COUNT && ( View all )}
)}
) } interface ExampleButtonProps { Icon: LucideIcon; title: string; onClick: () => void; } const ExampleButton = ({ Icon, title, onClick, }: ExampleButtonProps) => { return ( ) }