From 93ec22555c19e8227e91084d083d3136c7b0fadf Mon Sep 17 00:00:00 2001 From: Sreeram Sreedhar Date: Mon, 22 Jun 2026 15:22:36 -0700 Subject: [PATCH] Add real desktop memory search --- apps/desktop/app/providers.tsx | 8 ++- apps/desktop/components/search-command.tsx | 82 ++++++++++++++++------ apps/desktop/lib/search.ts | 57 +++++++++++++++ packages/lib/token-provider.ts | 11 +++ 4 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 apps/desktop/lib/search.ts create mode 100644 packages/lib/token-provider.ts diff --git a/apps/desktop/app/providers.tsx b/apps/desktop/app/providers.tsx index 6ade08dc..8c7ac7d2 100644 --- a/apps/desktop/app/providers.tsx +++ b/apps/desktop/app/providers.tsx @@ -1,9 +1,11 @@ "use client" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" +import { setTokenProvider } from "@lib/token-provider" import { Toaster } from "@ui/components/sonner" import { ThemeProvider } from "next-themes" -import { type ReactNode, useState } from "react" +import { type ReactNode, useEffect, useState } from "react" +import { getStoredToken } from "@/lib/auth" export function Providers({ children }: { children: ReactNode }) { // One QueryClient for the app's lifetime; lazy init avoids re-creating it on @@ -20,6 +22,10 @@ export function Providers({ children }: { children: ReactNode }) { }), ) + useEffect(() => { + setTokenProvider(getStoredToken) + }, []) + // The app is dark-only, matching apps/web (forcedTheme="dark"). return ( void }) { + const [query, setQuery] = useState("") + const trimmedQuery = query.trim() + const searchQuery = useQuery({ + queryKey: ["desktop-search", trimmedQuery], + queryFn: () => searchMemories(trimmedQuery), + enabled: open && trimmedQuery.length > 0, + staleTime: 30 * 1000, + }) + const results = searchQuery.data?.results ?? [] + + useEffect(() => { + if (!open) setQuery("") + }, [open]) + return ( - + - No results found. + {trimmedQuery ? ( + + {searchQuery.isFetching ? "Searching..." : "No results found."} + + ) : ( +
+ Type to search your memories. +
+ )} - {MOCK_RESULTS.map((result) => { - const Icon = ICONS[result.kind] + {searchQuery.isFetching ? ( + + + Searching... + + ) : null} + {searchQuery.isError ? ( + + + Search failed. Check your token and API URL. + + ) : null} + {results.map((result) => { + const title = result.title ?? result.documentId + const preview = + result.summary ?? + result.chunks.find((chunk) => chunk.isRelevant)?.content return ( onOpenChange(false)} + className="items-start" > - - {result.title} + +
+
{title}
+ {preview ? ( +
+ {preview} +
+ ) : null} +
) })} diff --git a/apps/desktop/lib/search.ts b/apps/desktop/lib/search.ts new file mode 100644 index 00000000..088bf557 --- /dev/null +++ b/apps/desktop/lib/search.ts @@ -0,0 +1,57 @@ +"use client" + +import { getAuthToken } from "@lib/token-provider" + +const DEFAULT_API_URL = "https://api.supermemory.ai" + +export type SearchResult = { + documentId: string + title: string | null + summary?: string | null + content?: string | null + type: string | null + score: number + chunks: Array<{ + content: string + isRelevant: boolean + score: number + }> +} + +type SearchResponse = { + results: SearchResult[] + total: number + timing: number +} + +function apiUrl() { + return process.env.NEXT_PUBLIC_BACKEND_URL ?? DEFAULT_API_URL +} + +export async function searchMemories(query: string) { + const token = await getAuthToken() + if (!token) { + throw new Error("No stored desktop token") + } + + const response = await fetch(`${apiUrl().replace(/\/$/, "")}/v3/search`, { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + "X-App-Source": "desktop", + }, + body: JSON.stringify({ + q: query, + limit: 10, + includeSummary: true, + }), + }) + + if (!response.ok) { + const body = await response.text() + throw new Error(`Search failed (${response.status}): ${body}`) + } + + return (await response.json()) as SearchResponse +} diff --git a/packages/lib/token-provider.ts b/packages/lib/token-provider.ts new file mode 100644 index 00000000..e513870f --- /dev/null +++ b/packages/lib/token-provider.ts @@ -0,0 +1,11 @@ +export type TokenProvider = () => Promise + +let tokenProvider: TokenProvider = async () => null + +export function setTokenProvider(provider: TokenProvider) { + tokenProvider = provider +} + +export function getAuthToken() { + return tokenProvider() +}