diff --git a/src/interface/web/app/search/page.tsx b/src/interface/web/app/search/page.tsx index b700e5c11..902b96310 100644 --- a/src/interface/web/app/search/page.tsx +++ b/src/interface/web/app/search/page.tsx @@ -173,8 +173,10 @@ export default function Search() { const [searchResultsLoading, setSearchResultsLoading] = useState(false); const [focusSearchResult, setFocusSearchResult] = useState(null); const [exampleQuery, setExampleQuery] = useState(""); + const [fileSuggestions, setFileSuggestions] = useState([]); + const [allFiles, setAllFiles] = useState([]); + const [showSuggestions, setShowSuggestions] = useState(false); const searchTimeoutRef = useRef(null); - const isMobileWidth = useIsMobileWidth(); useEffect(() => { @@ -183,8 +185,68 @@ export default function Search() { Math.floor(Math.random() * naturalLanguageSearchQueryExamples.length) ], ); + + // Load all files once on page load + fetch('/api/content/computer', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(response => response.json()) + .then(data => { + setAllFiles(data); + }) + .catch(error => { + console.error('Error loading files:', error); + }); }, []); + function getFileSuggestions(query: string) { + const fileFilterMatch = query.match(/file:([^"\s]*|"[^"]*")?/); + if (!fileFilterMatch) { + setFileSuggestions([]); + setShowSuggestions(false); + return; + } + + 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); + + // Clear previous search timeout + if (searchTimeoutRef.current) { + clearTimeout(searchTimeoutRef.current); + } + + // Get file suggestions immediately + getFileSuggestions(value); + + // Debounce search + if (value.trim()) { + searchTimeoutRef.current = setTimeout(() => { + search(); + }, 750); + } + } + + function applySuggestion(suggestion: string) { + // Replace the file: filter with the selected suggestion + const newQuery = searchQuery.replace(/file:([^"\s]*|"[^"]*")?/, `file:"${suggestion}"`); + setSearchQuery(newQuery); + setShowSuggestions(false); + search(); + } + function search() { if (searchResultsLoading || !searchQuery.trim()) return; @@ -205,30 +267,6 @@ export default function Search() { }); } - useEffect(() => { - if (!searchQuery.trim()) { - return; - } - - setFocusSearchResult(null); - - if (searchTimeoutRef.current) { - clearTimeout(searchTimeoutRef.current); - } - - if (searchQuery.trim()) { - searchTimeoutRef.current = setTimeout(() => { - search(); - }, 750); // 1000 milliseconds = 1 second - } - - return () => { - if (searchTimeoutRef.current) { - clearTimeout(searchTimeoutRef.current); - } - }; - }, [searchQuery]); - return ( @@ -247,14 +285,38 @@ export default function Search() {
- setSearchQuery(e.currentTarget.value)} - onKeyDown={(e) => e.key === "Enter" && search()} - type="search" - placeholder="Search Documents" - /> +
+ handleSearchInputChange(e.currentTarget.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + if (showSuggestions && fileSuggestions.length > 0) { + applySuggestion(fileSuggestions[0]); + } else { + search(); + } + } + }} + type="search" + placeholder="Search Documents (type 'file:' for file suggestions)" + value={searchQuery} + /> + {showSuggestions && fileSuggestions.length > 0 && ( +
+ {fileSuggestions.map((suggestion, index) => ( +
applySuggestion(suggestion)} + > + {suggestion} +
+ ))} +
+ )} +