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
---
apps/memory-graph-playground/next.config.ts | 10 +-
apps/memory-graph-playground/postcss.config.mjs | 10 +-
.../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 ++++++++++++---------
apps/memory-graph-playground/tsconfig.json | 64 ++--
7 files changed, 358 insertions(+), 278 deletions(-)
(limited to 'apps')
diff --git a/apps/memory-graph-playground/next.config.ts b/apps/memory-graph-playground/next.config.ts
index 66e15661..05ac1393 100644
--- a/apps/memory-graph-playground/next.config.ts
+++ b/apps/memory-graph-playground/next.config.ts
@@ -1,8 +1,8 @@
-import type { NextConfig } from "next";
+import type { NextConfig } from "next"
const nextConfig: NextConfig = {
- /* config options here */
- reactCompiler: true,
-};
+ /* config options here */
+ reactCompiler: true,
+}
-export default nextConfig;
+export default nextConfig
diff --git a/apps/memory-graph-playground/postcss.config.mjs b/apps/memory-graph-playground/postcss.config.mjs
index 61e36849..d8af3e5d 100644
--- a/apps/memory-graph-playground/postcss.config.mjs
+++ b/apps/memory-graph-playground/postcss.config.mjs
@@ -1,7 +1,7 @@
const config = {
- plugins: {
- "@tailwindcss/postcss": {},
- },
-};
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+}
-export default config;
+export default config
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
-
-
-
-
-
-
- {/* 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
+
+
+
+
+
+
+
+ {/* 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.
+
+
+
+
+ )}
+
+
+ )
}
diff --git a/apps/memory-graph-playground/tsconfig.json b/apps/memory-graph-playground/tsconfig.json
index cf9c65d3..76e73ca8 100644
--- a/apps/memory-graph-playground/tsconfig.json
+++ b/apps/memory-graph-playground/tsconfig.json
@@ -1,34 +1,34 @@
{
- "compilerOptions": {
- "target": "ES2017",
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "react-jsx",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": ["./src/*"]
- }
- },
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts",
- ".next/dev/types/**/*.ts",
- "**/*.mts"
- ],
- "exclude": ["node_modules"]
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
}
--
cgit v1.2.3