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 && }