From 0d8db63fcf1b8fb8816db8f6f3ef1413a57bf49f Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Fri, 16 Jan 2026 03:30:18 +0000 Subject: [PATCH] Generated by Spark: Add recent searches and search history --- src/components/GlobalSearch.tsx | 137 +++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 1 deletion(-) diff --git a/src/components/GlobalSearch.tsx b/src/components/GlobalSearch.tsx index 91dcc04..56e8d81 100644 --- a/src/components/GlobalSearch.tsx +++ b/src/components/GlobalSearch.tsx @@ -1,4 +1,5 @@ import { useState, useMemo, useEffect } from 'react' +import { useKV } from '@github/spark/hooks' import { CommandDialog, CommandEmpty, @@ -28,9 +29,12 @@ import { File, Folder, MagnifyingGlass, + ClockCounterClockwise, + X, } from '@phosphor-icons/react' import { ProjectFile, PrismaModel, ComponentNode, ComponentTree, Workflow, Lambda, PlaywrightTest, StorybookStory, UnitTest } from '@/types/project' import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' interface SearchResult { id: string @@ -42,6 +46,15 @@ interface SearchResult { tags?: string[] } +interface SearchHistoryItem { + id: string + query: string + timestamp: number + resultId?: string + resultTitle?: string + resultCategory?: string +} + interface GlobalSearchProps { open: boolean onOpenChange: (open: boolean) => void @@ -74,6 +87,7 @@ export function GlobalSearch({ onFileSelect, }: GlobalSearchProps) { const [searchQuery, setSearchQuery] = useState('') + const [searchHistory, setSearchHistory] = useKV('search-history', []) useEffect(() => { if (!open) { @@ -81,6 +95,36 @@ export function GlobalSearch({ } }, [open]) + const addToHistory = (query: string, result?: SearchResult) => { + if (!query.trim()) return + + const historyItem: SearchHistoryItem = { + id: `history-${Date.now()}`, + query: query.trim(), + timestamp: Date.now(), + resultId: result?.id, + resultTitle: result?.title, + resultCategory: result?.category, + } + + setSearchHistory((currentHistory) => { + const filtered = (currentHistory || []).filter( + (item) => item.query.toLowerCase() !== query.toLowerCase() + ) + return [historyItem, ...filtered].slice(0, 20) + }) + } + + const clearHistory = () => { + setSearchHistory([]) + } + + const removeHistoryItem = (id: string) => { + setSearchHistory((currentHistory) => + (currentHistory || []).filter((item) => item.id !== id) + ) + } + const allResults = useMemo(() => { const results: SearchResult[] = [] @@ -402,7 +446,7 @@ export function GlobalSearch({ const filteredResults = useMemo(() => { if (!searchQuery.trim()) { - return allResults.slice(0, 50) + return [] } const query = searchQuery.toLowerCase().trim() @@ -442,6 +486,19 @@ export function GlobalSearch({ .map(({ result }) => result) }, [allResults, searchQuery]) + const recentSearches = useMemo(() => { + const safeHistory = searchHistory || [] + return safeHistory + .slice(0, 10) + .map((item) => { + const result = allResults.find((r) => r.id === item.resultId) + return { + historyItem: item, + result, + } + }) + }, [searchHistory, allResults]) + const groupedResults = useMemo(() => { const groups: Record = {} filteredResults.forEach((result) => { @@ -454,10 +511,20 @@ export function GlobalSearch({ }, [filteredResults]) const handleSelect = (result: SearchResult) => { + addToHistory(searchQuery, result) result.action() onOpenChange(false) } + const handleHistorySelect = (historyItem: SearchHistoryItem, result?: SearchResult) => { + if (result) { + result.action() + onOpenChange(false) + } else { + setSearchQuery(historyItem.query) + } + } + return ( No results found

+ + {!searchQuery.trim() && recentSearches.length > 0 && ( + <> + + Recent Searches + + + } + > + {recentSearches.map(({ historyItem, result }) => ( + handleHistorySelect(historyItem, result)} + className="flex items-center gap-3 px-4 py-3 cursor-pointer group" + > +
+ +
+
+ {result ? ( + <> +
{result.title}
+
+ Searched: {historyItem.query} +
+ + ) : ( + <> +
{historyItem.query}
+
+ Search again +
+ + )} +
+ {result && ( + + {result.category} + + )} + +
+ ))} +
+ + + )} + {Object.entries(groupedResults).map(([category, results], index) => (
{index > 0 && }