aboutsummaryrefslogtreecommitdiff
path: root/apps/memory-graph-playground/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'apps/memory-graph-playground/src/app')
-rw-r--r--apps/memory-graph-playground/src/app/api/graph/route.ts46
-rw-r--r--apps/memory-graph-playground/src/app/favicon.icobin0 -> 25931 bytes
-rw-r--r--apps/memory-graph-playground/src/app/globals.css26
-rw-r--r--apps/memory-graph-playground/src/app/layout.tsx34
-rw-r--r--apps/memory-graph-playground/src/app/page.tsx169
5 files changed, 275 insertions, 0 deletions
diff --git a/apps/memory-graph-playground/src/app/api/graph/route.ts b/apps/memory-graph-playground/src/app/api/graph/route.ts
new file mode 100644
index 00000000..b7901966
--- /dev/null
+++ b/apps/memory-graph-playground/src/app/api/graph/route.ts
@@ -0,0 +1,46 @@
+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
+
+ 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,
+ }),
+ })
+
+ 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 }
+ )
+ }
+}
diff --git a/apps/memory-graph-playground/src/app/favicon.ico b/apps/memory-graph-playground/src/app/favicon.ico
new file mode 100644
index 00000000..718d6fea
--- /dev/null
+++ b/apps/memory-graph-playground/src/app/favicon.ico
Binary files differ
diff --git a/apps/memory-graph-playground/src/app/globals.css b/apps/memory-graph-playground/src/app/globals.css
new file mode 100644
index 00000000..a2dc41ec
--- /dev/null
+++ b/apps/memory-graph-playground/src/app/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --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);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ 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
new file mode 100644
index 00000000..f7fa87eb
--- /dev/null
+++ b/apps/memory-graph-playground/src/app/layout.tsx
@@ -0,0 +1,34 @@
+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"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+ <html lang="en">
+ <body
+ className={`${geistSans.variable} ${geistMono.variable} antialiased`}
+ >
+ {children}
+ </body>
+ </html>
+ );
+}
diff --git a/apps/memory-graph-playground/src/app/page.tsx b/apps/memory-graph-playground/src/app/page.tsx
new file mode 100644
index 00000000..bf202554
--- /dev/null
+++ b/apps/memory-graph-playground/src/app/page.tsx
@@ -0,0 +1,169 @@
+"use client"
+
+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
+ }
+}
+
+export default function Home() {
+ const [apiKey, setApiKey] = useState('')
+ const [documents, setDocuments] = useState<DocumentWithMemories[]>([])
+ const [isLoading, setIsLoading] = useState(false)
+ const [isLoadingMore, setIsLoadingMore] = useState(false)
+ const [error, setError] = useState<Error | null>(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 (
+ <div className="flex flex-col h-screen bg-zinc-950">
+ {/* Header */}
+ <header className="shrink-0 border-b border-zinc-800 bg-zinc-900 px-6 py-4">
+ <div className="flex items-center justify-between">
+ <div>
+ <h1 className="text-xl font-semibold text-white">Memory Graph Playground</h1>
+ <p className="text-sm text-zinc-400">Test the @supermemory/memory-graph package</p>
+ </div>
+
+ <form onSubmit={handleSubmit} className="flex items-center gap-3">
+ <input
+ type="password"
+ placeholder="Enter your Supermemory API key"
+ value={apiKey}
+ onChange={(e) => 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"
+ />
+ <button
+ type="submit"
+ disabled={!apiKey || isLoading}
+ className="rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50"
+ >
+ {isLoading ? 'Loading...' : 'Load Graph'}
+ </button>
+ </form>
+ </div>
+ </header>
+
+ {/* Main content */}
+ <main className="flex-1 overflow-hidden">
+ {!showGraph ? (
+ <div className="flex h-full items-center justify-center">
+ <div className="max-w-md text-center">
+ <div className="mb-6 text-6xl">
+ <svg className="mx-auto h-16 w-16 text-zinc-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
+ </svg>
+ </div>
+ <h2 className="mb-2 text-xl font-semibold text-white">Get Started</h2>
+ <p className="mb-6 text-zinc-400">
+ Enter your Supermemory API key above to visualize your memory graph.
+ </p>
+ <div className="text-left text-sm text-zinc-500">
+ <p className="mb-2 font-medium text-zinc-400">Features to test:</p>
+ <ul className="list-inside list-disc space-y-1">
+ <li>Pan and zoom the graph</li>
+ <li>Click on nodes to see details</li>
+ <li>Drag nodes around</li>
+ <li>Use the space selector to filter</li>
+ <li>Pagination loads more documents</li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ ) : (
+ <div className="h-full w-full">
+ <MemoryGraph
+ documents={documents}
+ isLoading={isLoading}
+ isLoadingMore={isLoadingMore}
+ error={error}
+ hasMore={hasMore}
+ loadMoreDocuments={loadMoreDocuments}
+ totalLoaded={documents.length}
+ variant="console"
+ showSpacesSelector={true}
+ >
+ <div className="flex h-full items-center justify-center">
+ <p className="text-zinc-400">No memories found. Add some content to see your graph.</p>
+ </div>
+ </MemoryGraph>
+ </div>
+ )}
+ </main>
+ </div>
+ )
+}