From dfb0c05ab33cb20537002eaeb896e6b2ab35af25 Mon Sep 17 00:00:00 2001 From: nexxeln <95541290+nexxeln@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:37:24 +0000 Subject: add spaces selector with search (#600) relevant files to review: \- memory-graph.tsx \- spaces-dropdown.tsx \- spaces-dropdown.css.ts --- .../src/app/api/graph/route.ts | 87 +++-- apps/memory-graph-playground/src/app/globals.css | 26 +- apps/memory-graph-playground/src/app/layout.tsx | 46 +-- apps/memory-graph-playground/src/app/page.tsx | 393 ++++++++++++--------- 4 files changed, 316 insertions(+), 236 deletions(-) (limited to 'apps/memory-graph-playground/src') diff --git a/apps/memory-graph-playground/src/app/api/graph/route.ts b/apps/memory-graph-playground/src/app/api/graph/route.ts index b7901966..c722c625 100644 --- a/apps/memory-graph-playground/src/app/api/graph/route.ts +++ b/apps/memory-graph-playground/src/app/api/graph/route.ts @@ -1,46 +1,55 @@ -import { NextResponse } from 'next/server' +import { NextResponse } from "next/server" export async function POST(request: Request) { - try { - const body = await request.json() - const { apiKey, page = 1, limit = 500, sort = 'createdAt', order = 'desc' } = body + try { + const body = await request.json() + const { + apiKey, + page = 1, + limit = 500, + sort = "createdAt", + order = "desc", + } = body - if (!apiKey) { - return NextResponse.json( - { error: 'API key is required' }, - { status: 400 } - ) - } + if (!apiKey) { + return NextResponse.json( + { error: "API key is required" }, + { status: 400 }, + ) + } - const response = await fetch('https://api.supermemory.ai/v3/documents/documents', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${apiKey}`, - }, - body: JSON.stringify({ - page, - limit, - sort, - order, - }), - }) + const response = await fetch( + "https://api.supermemory.ai/v3/documents/documents", + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + page, + limit, + sort, + order, + }), + }, + ) - if (!response.ok) { - const errorData = await response.json().catch(() => ({})) - return NextResponse.json( - { error: errorData.message || `API error: ${response.status}` }, - { status: response.status } - ) - } + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return NextResponse.json( + { error: errorData.message || `API error: ${response.status}` }, + { status: response.status }, + ) + } - const data = await response.json() - return NextResponse.json(data) - } catch (error) { - console.error('Graph API error:', error) - return NextResponse.json( - { error: 'Failed to fetch documents' }, - { status: 500 } - ) - } + const data = await response.json() + return NextResponse.json(data) + } catch (error) { + console.error("Graph API error:", error) + return NextResponse.json( + { error: "Failed to fetch documents" }, + { status: 500 }, + ) + } } diff --git a/apps/memory-graph-playground/src/app/globals.css b/apps/memory-graph-playground/src/app/globals.css index a2dc41ec..0673ff02 100644 --- a/apps/memory-graph-playground/src/app/globals.css +++ b/apps/memory-graph-playground/src/app/globals.css @@ -1,26 +1,26 @@ @import "tailwindcss"; :root { - --background: #ffffff; - --foreground: #171717; + --background: #ffffff; + --foreground: #171717; } @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); } @media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } + :root { + --background: #0a0a0a; + --foreground: #ededed; + } } body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; } diff --git a/apps/memory-graph-playground/src/app/layout.tsx b/apps/memory-graph-playground/src/app/layout.tsx index f7fa87eb..bb3b3e84 100644 --- a/apps/memory-graph-playground/src/app/layout.tsx +++ b/apps/memory-graph-playground/src/app/layout.tsx @@ -1,34 +1,34 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from "next" +import { Geist, Geist_Mono } from "next/font/google" +import "./globals.css" const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); + variable: "--font-geist-sans", + subsets: ["latin"], +}) const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); + variable: "--font-geist-mono", + subsets: ["latin"], +}) export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; + title: "Create Next App", + description: "Generated by create next app", +} export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { - return ( - - - {children} - - - ); + return ( + + + {children} + + + ) } diff --git a/apps/memory-graph-playground/src/app/page.tsx b/apps/memory-graph-playground/src/app/page.tsx index bf202554..7192c4c2 100644 --- a/apps/memory-graph-playground/src/app/page.tsx +++ b/apps/memory-graph-playground/src/app/page.tsx @@ -1,169 +1,240 @@ "use client" -import { useState, useCallback } from 'react' -import { MemoryGraph, type DocumentWithMemories } from '@supermemory/memory-graph' +import { useState, useCallback } from "react" +import { + MemoryGraph, + type DocumentWithMemories, +} from "@supermemory/memory-graph" interface DocumentsResponse { - documents: DocumentWithMemories[] - pagination: { - currentPage: number - limit: number - totalItems: number - totalPages: number - } + documents: DocumentWithMemories[] + pagination: { + currentPage: number + limit: number + totalItems: number + totalPages: number + } } export default function Home() { - const [apiKey, setApiKey] = useState('') - const [documents, setDocuments] = useState([]) - const [isLoading, setIsLoading] = useState(false) - const [isLoadingMore, setIsLoadingMore] = useState(false) - const [error, setError] = useState(null) - const [hasMore, setHasMore] = useState(false) - const [currentPage, setCurrentPage] = useState(0) - const [showGraph, setShowGraph] = useState(false) - - const PAGE_SIZE = 500 - - const fetchDocuments = useCallback(async (page: number, append = false) => { - if (!apiKey) return - - if (page === 1) { - setIsLoading(true) - } else { - setIsLoadingMore(true) - } - setError(null) - - try { - const response = await fetch('/api/graph', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - apiKey, - page, - limit: PAGE_SIZE, - sort: 'createdAt', - order: 'desc', - }), - }) - - if (!response.ok) { - const errorData = await response.json() - throw new Error(errorData.error || 'Failed to fetch documents') - } - - const data: DocumentsResponse = await response.json() - - if (append) { - setDocuments(prev => [...prev, ...data.documents]) - } else { - setDocuments(data.documents) - } - - setCurrentPage(data.pagination.currentPage) - setHasMore(data.pagination.currentPage < data.pagination.totalPages) - setShowGraph(true) - } catch (err) { - setError(err instanceof Error ? err : new Error('Unknown error')) - } finally { - setIsLoading(false) - setIsLoadingMore(false) - } - }, [apiKey]) - - const loadMoreDocuments = useCallback(async () => { - if (hasMore && !isLoadingMore) { - await fetchDocuments(currentPage + 1, true) - } - }, [hasMore, isLoadingMore, currentPage, fetchDocuments]) - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - if (apiKey) { - setDocuments([]) - setCurrentPage(0) - fetchDocuments(1) - } - } - - return ( -
- {/* Header */} -
-
-
-

Memory Graph Playground

-

Test the @supermemory/memory-graph package

-
- -
- setApiKey(e.target.value)} - className="w-80 rounded-lg border border-zinc-700 bg-zinc-800 px-4 py-2 text-sm text-white placeholder-zinc-500 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" - /> - -
-
-
- - {/* Main content */} -
- {!showGraph ? ( -
-
-
- - - -
-

Get Started

-

- Enter your Supermemory API key above to visualize your memory graph. -

-
-

Features to test:

-
    -
  • Pan and zoom the graph
  • -
  • Click on nodes to see details
  • -
  • Drag nodes around
  • -
  • Use the space selector to filter
  • -
  • Pagination loads more documents
  • -
-
-
-
- ) : ( -
- -
-

No memories found. Add some content to see your graph.

-
-
-
- )} -
-
- ) + const [apiKey, setApiKey] = useState("") + const [documents, setDocuments] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [isLoadingMore, setIsLoadingMore] = useState(false) + const [error, setError] = useState(null) + const [hasMore, setHasMore] = useState(false) + const [currentPage, setCurrentPage] = useState(0) + const [showGraph, setShowGraph] = useState(false) + + // State for controlled space selection + const [selectedSpace, setSelectedSpace] = useState("all") + + const PAGE_SIZE = 500 + + const fetchDocuments = useCallback( + async (page: number, append = false) => { + if (!apiKey) return + + if (page === 1) { + setIsLoading(true) + } else { + setIsLoadingMore(true) + } + setError(null) + + try { + const response = await fetch("/api/graph", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + apiKey, + page, + limit: PAGE_SIZE, + sort: "createdAt", + order: "desc", + }), + }) + + if (!response.ok) { + const errorData = await response.json() + throw new Error(errorData.error || "Failed to fetch documents") + } + + const data: DocumentsResponse = await response.json() + + if (append) { + setDocuments((prev) => [...prev, ...data.documents]) + } else { + setDocuments(data.documents) + } + + setCurrentPage(data.pagination.currentPage) + setHasMore(data.pagination.currentPage < data.pagination.totalPages) + setShowGraph(true) + } catch (err) { + setError(err instanceof Error ? err : new Error("Unknown error")) + } finally { + setIsLoading(false) + setIsLoadingMore(false) + } + }, + [apiKey], + ) + + const loadMoreDocuments = useCallback(async () => { + if (hasMore && !isLoadingMore) { + await fetchDocuments(currentPage + 1, true) + } + }, [hasMore, isLoadingMore, currentPage, fetchDocuments]) + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (apiKey) { + setDocuments([]) + setCurrentPage(0) + setSelectedSpace("all") + fetchDocuments(1) + } + } + + // Handle space change + const handleSpaceChange = useCallback((spaceId: string) => { + setSelectedSpace(spaceId) + }, []) + + // Reset to defaults + const handleReset = () => { + setSelectedSpace("all") + } + + return ( +
+ {/* Header */} +
+
+
+

+ Memory Graph Playground +

+

+ Test the @supermemory/memory-graph package +

+
+ +
+ setApiKey(e.target.value)} + className="w-80 rounded-lg border border-zinc-700 bg-zinc-800 px-4 py-2 text-sm text-white placeholder-zinc-500 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500" + /> + +
+
+
+ + {/* State Display Panel - For Testing */} + {showGraph && ( +
+
+
+
+ Selected Space: + {selectedSpace} +
+
+ Documents: + + {documents.length} + +
+
+ +
+
+ )} + + {/* Main content */} +
+ {!showGraph ? ( +
+
+
+ + + +
+

+ Get Started +

+

+ Enter your Supermemory API key above to visualize your memory + graph. +

+
+

+ Features to test: +

+
    +
  • ✨ Search and filter by spaces
  • +
  • ✨ Arrow key navigation in spaces dropdown
  • +
  • Pan and zoom the graph
  • +
  • Click on nodes to see details
  • +
  • Drag nodes around
  • +
  • Filter by space
  • +
  • Pagination loads more documents
  • +
+
+
+
+ ) : ( +
+ +
+

+ No memories found. Add some content to see your graph. +

+
+
+
+ )} +
+
+ ) } -- cgit v1.2.3