diff --git a/src/interface/web/app/search/page.tsx b/src/interface/web/app/search/page.tsx index add3f049..f7d8ab46 100644 --- a/src/interface/web/app/search/page.tsx +++ b/src/interface/web/app/search/page.tsx @@ -23,7 +23,6 @@ import { MagnifyingGlass, NoteBlank, NotionLogo, - Eye, Trash, DotsThreeVertical, Waveform, @@ -31,6 +30,8 @@ import { Download, Brain, Check, + BoxArrowDown, + Funnel, } from "@phosphor-icons/react"; import { Button } from "@/components/ui/button"; import Link from "next/link"; @@ -65,8 +66,23 @@ import { PaginationPrevious, } from "@/components/ui/pagination"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" + import { uploadDataForIndexing } from "../common/chatFunctions"; import { Progress } from "@/components/ui/progress"; +import { cn } from "@/lib/utils"; interface AdditionalData { file: string; source: string; @@ -461,19 +477,93 @@ const UploadFiles: React.FC<{ ); }; +interface FileFilterComboBoxProps { + allFiles: string[]; + onChooseFile: (file: string) => void; + isMobileWidth: boolean; + explicitFile?: string; +} + +export function FileFilterComboBox(props: FileFilterComboBoxProps) { + const [open, setOpen] = useState(false) + const [value, setValue] = useState(props.explicitFile || "") + + useEffect(() => { + if (props.explicitFile) { + setValue(props.explicitFile); + } + }, [props.explicitFile]); + + return ( + + + + + + + + + No framework found. + + {props.allFiles.map((file) => ( + { + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + props.onChooseFile(currentValue) + }} + > + {file} + + + ))} + + + + + + ) +} + export default function Search() { const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState(null); const [searchResultsLoading, setSearchResultsLoading] = useState(false); + const searchInputRef = useRef(null); const [focusSearchResult, setFocusSearchResult] = useState(null); const [files, setFiles] = useState([]); const [error, setError] = useState(null); const [fileObjectsLoading, setFileObjectsLoading] = useState(true); - const [fileSuggestions, setFileSuggestions] = useState([]); const [allFiles, setAllFiles] = useState([]); - const [showSuggestions, setShowSuggestions] = useState(false); const searchTimeoutRef = useRef(null); const [selectedFile, setSelectedFile] = useState(null); + const [selectedFileFilter, setSelectedFileFilter] = useState(undefined); const [selectedFileFullText, setSelectedFileFullText] = useState(null); const [isDeleting, setIsDeleting] = useState(false); const [uploadedFiles, setUploadedFiles] = useState([]); @@ -501,30 +591,27 @@ export default function Search() { }); }, []); - function getFileSuggestions(query: string) { - const fileFilterMatch = query.match(/file:([^"\s]*|"[^"]*")?/); - if (!fileFilterMatch) { - setFileSuggestions([]); - setShowSuggestions(false); - return; + useEffect(() => { + // Replace the file: filter with the selected suggestion + const fileFilterMatch = searchQuery.match(/file:([^"\s]*|"[^"]*")?/); + + if (fileFilterMatch) { + const extractedFileFilter = fileFilterMatch[1]; + // Strip the double quotes + const extractedFileFilterValue = extractedFileFilter?.replace(/"/g, ""); + + if (extractedFileFilterValue) { + setSelectedFileFilter(extractedFileFilterValue); + } } + }, [searchQuery]); - const filePrefix = fileFilterMatch[1]?.replace(/^"|"$/g, '').trim() || ''; - const filteredSuggestions = allFiles - .filter(file => file.toLowerCase().includes(filePrefix.toLowerCase())) - .sort() - .slice(0, 10); - - setFileSuggestions(filteredSuggestions); - setShowSuggestions(true); - } function handleSearchInputChange(value: string) { setSearchQuery(value); if (!value.trim()) { setSearchResults(null); - setShowSuggestions(false); return; } @@ -533,9 +620,6 @@ export default function Search() { clearTimeout(searchTimeoutRef.current); } - // Get file suggestions immediately - getFileSuggestions(value); - // Debounce search if (value.trim()) { searchTimeoutRef.current = setTimeout(() => { @@ -545,10 +629,10 @@ export default function Search() { } function applySuggestion(suggestion: string) { - // Replace the file: filter with the selected suggestion - const newQuery = searchQuery.replace(/file:([^"\s]*|"[^"]*")?/, `file:"${suggestion}"`); + // Append the file: filter with the selected suggestion + const newQuery = `file:"${suggestion}" ${searchQuery}`; setSearchQuery(newQuery); - setShowSuggestions(false); + searchInputRef.current?.focus(); search(); } @@ -687,49 +771,36 @@ export default function Search() {
-
+
-
-
+
+
handleSearchInputChange(e.currentTarget.value)} onKeyDown={(e) => { if (e.key === "Enter") { - if (showSuggestions && fileSuggestions.length > 0) { - applySuggestion(fileSuggestions[0]); - } else { - search(); - } + search(); } }} + ref={searchInputRef} type="search" - placeholder="Search Documents (type 'file:' for file suggestions)" + placeholder="Search Documents" value={searchQuery} /> - {showSuggestions && fileSuggestions.length > 0 && ( -
- {fileSuggestions.map((suggestion, index) => ( -
applySuggestion(suggestion)} - > - {suggestion} -
- ))} -
- )}
- +
+ applySuggestion(file)} isMobileWidth={isMobileWidth} explicitFile={selectedFileFilter} /> + +
{searchResults === null && (