From 6470b6b05d83ae6c07cffcd6f5b0b34e03ff8b92 Mon Sep 17 00:00:00 2001 From: Abhijeet Sharma Date: Mon, 12 Oct 2026 21:43:21 -0700 Subject: [PATCH] fix(docs): correct stale @supermemory/memory-graph API references (#1414) --- apps/docs/integrations/memory-graph.mdx | 426 +++++++++++++++++++----- apps/memory-graph-playground/README.md | 22 +- 2 files changed, 359 insertions(+), 89 deletions(-) diff --git a/apps/docs/integrations/memory-graph.mdx b/apps/docs/integrations/memory-graph.mdx index 767b45d2..3b8dbb6d 100644 --- a/apps/docs/integrations/memory-graph.mdx +++ b/apps/docs/integrations/memory-graph.mdx @@ -25,11 +25,11 @@ npm install @supermemory/memory-graph 'use client'; // For Next.js App Router import { MemoryGraph } from '@supermemory/memory-graph'; -import type { DocumentWithMemories } from '@supermemory/memory-graph'; +import type { GraphApiDocument } from '@supermemory/memory-graph'; import { useEffect, useState } from 'react'; export default function GraphPage() { - const [documents, setDocuments] = useState([]); + const [documents, setDocuments] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); @@ -76,15 +76,97 @@ Existing icon-based document nodes and connection styling remain the defaults. O `openDocumentOnEnter` is off by default. With an `onOpenDocument` callback, `MemoryGraph` exposes a focused Enter action for the selected persisted document or one confirmed memory source. Typing, modifier shortcuts, repeats, composition and keys outside the graph do not open documents. Your callback retains responsibility for routing and access control; consumers without a callback receive no new opening behavior. + + `documents` must be `GraphApiDocument[]`, the shape the API route below returns, with `documentType` and `memories` fields (not the raw API's `type` and `memoryEntries`). + + ## Backend API route -Create an API route to fetch documents from Supermemory: +Create an API route to fetch documents from Supermemory. The `/v3/documents/documents` response uses `type` and `memoryEntries`; `` expects `documentType` and `memories`, so the route normalizes the shape before returning it: ```typescript Next.js App Router // app/api/graph/route.ts import { NextResponse } from 'next/server'; +import type { GraphApiDocument, GraphApiMemory } from '@supermemory/memory-graph'; + +interface RawMemoryEntry { + id: string; + memory?: string | null; + content?: string | null; + spaceId?: string | null; + isStatic?: boolean; + isLatest?: boolean; + isForgotten?: boolean; + forgetAfter?: string | null; + forgetReason?: string | null; + version?: number; + parentMemoryId?: string | null; + rootMemoryId?: string | null; + createdAt: string; + updatedAt: string; + relation?: 'updates' | 'extends' | 'derives' | null; + updatesMemoryId?: string | null; + nextVersionId?: string | null; + memoryRelations?: Record | null; + spaceContainerTag?: string | null; +} + +interface RawDocument { + id: string; + title: string | null; + summary?: string | null; + type: string; + createdAt: string; + updatedAt: string; + memoryEntries: RawMemoryEntry[]; +} + +interface RawDocumentsResponse { + documents: RawDocument[]; + pagination: { + currentPage: number; + limit: number; + totalItems: number; + totalPages: number; + }; +} + +// Maps the raw API document (`type` / `memoryEntries`) onto the shape +// expects (`documentType` / `memories`), filling in the +// defaults for fields the API may omit. +function toGraphDocument(doc: RawDocument): GraphApiDocument { + return { + id: doc.id, + title: doc.title, + summary: doc.summary ?? null, + documentType: doc.type, + createdAt: doc.createdAt, + updatedAt: doc.updatedAt, + memories: doc.memoryEntries.map((mem): GraphApiMemory => ({ + id: mem.id, + memory: mem.memory ?? mem.content ?? '', + content: mem.content ?? null, + isStatic: mem.isStatic ?? false, + spaceId: mem.spaceId ?? '', + isLatest: mem.isLatest ?? true, + isForgotten: mem.isForgotten ?? false, + forgetAfter: mem.forgetAfter ?? null, + forgetReason: mem.forgetReason ?? null, + version: mem.version ?? 1, + parentMemoryId: mem.parentMemoryId ?? null, + rootMemoryId: mem.rootMemoryId ?? null, + createdAt: mem.createdAt, + updatedAt: mem.updatedAt, + relation: mem.relation ?? null, + updatesMemoryId: mem.updatesMemoryId ?? null, + nextVersionId: mem.nextVersionId ?? null, + memoryRelations: mem.memoryRelations ?? null, + spaceContainerTag: mem.spaceContainerTag ?? null, + })), + }; +} export async function GET() { const response = await fetch('https://api.supermemory.ai/v3/documents/documents', { @@ -101,14 +183,95 @@ export async function GET() { }), }); - const data = await response.json(); - return NextResponse.json(data); + const data: RawDocumentsResponse = await response.json(); + return NextResponse.json({ + documents: data.documents.map(toGraphDocument), + pagination: data.pagination, + }); } ``` ```typescript Next.js Pages Router // pages/api/graph.ts import type { NextApiRequest, NextApiResponse } from 'next'; +import type { GraphApiDocument, GraphApiMemory } from '@supermemory/memory-graph'; + +interface RawMemoryEntry { + id: string; + memory?: string | null; + content?: string | null; + spaceId?: string | null; + isStatic?: boolean; + isLatest?: boolean; + isForgotten?: boolean; + forgetAfter?: string | null; + forgetReason?: string | null; + version?: number; + parentMemoryId?: string | null; + rootMemoryId?: string | null; + createdAt: string; + updatedAt: string; + relation?: 'updates' | 'extends' | 'derives' | null; + updatesMemoryId?: string | null; + nextVersionId?: string | null; + memoryRelations?: Record | null; + spaceContainerTag?: string | null; +} + +interface RawDocument { + id: string; + title: string | null; + summary?: string | null; + type: string; + createdAt: string; + updatedAt: string; + memoryEntries: RawMemoryEntry[]; +} + +interface RawDocumentsResponse { + documents: RawDocument[]; + pagination: { + currentPage: number; + limit: number; + totalItems: number; + totalPages: number; + }; +} + +// Maps the raw API document (`type` / `memoryEntries`) onto the shape +// expects (`documentType` / `memories`), filling in the +// defaults for fields the API may omit. +function toGraphDocument(doc: RawDocument): GraphApiDocument { + return { + id: doc.id, + title: doc.title, + summary: doc.summary ?? null, + documentType: doc.type, + createdAt: doc.createdAt, + updatedAt: doc.updatedAt, + memories: doc.memoryEntries.map((mem): GraphApiMemory => ({ + id: mem.id, + memory: mem.memory ?? mem.content ?? '', + content: mem.content ?? null, + isStatic: mem.isStatic ?? false, + spaceId: mem.spaceId ?? '', + isLatest: mem.isLatest ?? true, + isForgotten: mem.isForgotten ?? false, + forgetAfter: mem.forgetAfter ?? null, + forgetReason: mem.forgetReason ?? null, + version: mem.version ?? 1, + parentMemoryId: mem.parentMemoryId ?? null, + rootMemoryId: mem.rootMemoryId ?? null, + createdAt: mem.createdAt, + updatedAt: mem.updatedAt, + relation: mem.relation ?? null, + updatesMemoryId: mem.updatesMemoryId ?? null, + nextVersionId: mem.nextVersionId ?? null, + memoryRelations: mem.memoryRelations ?? null, + spaceContainerTag: mem.spaceContainerTag ?? null, + })), + }; +} export default async function handler(req: NextApiRequest, res: NextApiResponse) { const response = await fetch('https://api.supermemory.ai/v3/documents/documents', { @@ -120,8 +283,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) body: JSON.stringify({ page: 1, limit: 500, sort: 'createdAt', order: 'desc' }), }); - const data = await response.json(); - res.json(data); + const data: RawDocumentsResponse = await response.json(); + res.json({ + documents: data.documents.map(toGraphDocument), + pagination: data.pagination, + }); } ``` @@ -137,7 +303,41 @@ app.get('/api/graph', async (req, res) => { }); const data = await response.json(); - res.json(data); + + // Map the raw API document (`type` / `memoryEntries`) onto the shape + // expects (`documentType` / `memories`), filling in the + // defaults for fields the API may omit. + const documents = data.documents.map((doc) => ({ + id: doc.id, + title: doc.title, + summary: doc.summary ?? null, + documentType: doc.type, + createdAt: doc.createdAt, + updatedAt: doc.updatedAt, + memories: doc.memoryEntries.map((mem) => ({ + id: mem.id, + memory: mem.memory ?? mem.content ?? '', + content: mem.content ?? null, + isStatic: mem.isStatic ?? false, + spaceId: mem.spaceId ?? '', + isLatest: mem.isLatest ?? true, + isForgotten: mem.isForgotten ?? false, + forgetAfter: mem.forgetAfter ?? null, + forgetReason: mem.forgetReason ?? null, + version: mem.version ?? 1, + parentMemoryId: mem.parentMemoryId ?? null, + rootMemoryId: mem.rootMemoryId ?? null, + createdAt: mem.createdAt, + updatedAt: mem.updatedAt, + relation: mem.relation ?? null, + updatesMemoryId: mem.updatesMemoryId ?? null, + nextVersionId: mem.nextVersionId ?? null, + memoryRelations: mem.memoryRelations ?? null, + spaceContainerTag: mem.spaceContainerTag ?? null, + })), + })); + + res.json({ documents, pagination: data.pagination }); }); ``` @@ -151,13 +351,13 @@ app.get('/api/graph', async (req, res) => { ## Variants -**Console Variant** - Full-featured dashboard view (0.8x zoom, space selector visible): +**Console Variant** - Full-featured dashboard view (0.8x initial zoom): ```tsx ``` -**Consumer Variant** - Embedded widget view (0.5x zoom, space selector hidden): +**Consumer Variant** - Embedded widget view (0.5x initial zoom): ```tsx @@ -173,10 +373,11 @@ app.get('/api/graph', async (req, res) => { 'use client'; import { MemoryGraph } from '@supermemory/memory-graph'; +import type { GraphApiDocument } from '@supermemory/memory-graph'; import { useCallback, useEffect, useState } from 'react'; export default function PaginatedGraph() { - const [documents, setDocuments] = useState([]); + const [documents, setDocuments] = useState([]); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [isLoading, setIsLoading] = useState(true); @@ -210,8 +411,8 @@ export default function PaginatedGraph() { isLoading={isLoading} isLoadingMore={isLoadingMore} hasMore={hasMore} - totalLoaded={documents.length} - loadMoreDocuments={loadMore} + totalCount={documents.length} + onLoadMore={loadMore} /> ); } @@ -227,17 +428,6 @@ export default function PaginatedGraph() { /> ``` -### Controlled space selection - -```tsx - -``` - ### Custom empty state ```tsx @@ -257,80 +447,132 @@ export default function PaginatedGraph() { | Prop | Type | Default | Description | |------|------|---------|-------------| -| `documents` | `DocumentWithMemories[]` | required | Array of documents to display | -| `isLoading` | `boolean` | `false` | Shows loading indicator | -| `error` | `Error \| null` | `null` | Error to display | +| `documents` | `GraphApiDocument[]` | `[]` | Documents to display - pass this for direct data mode | +| `isLoading` | `boolean` | `false` | Whether data is loading | +| `error` | `Error \| null` | `null` | Error from data fetching | | `variant` | `"console" \| "consumer"` | `"console"` | Visual variant | -| `children` | `ReactNode` | - | Custom empty state content | +| `children` | `ReactNode` | - | Rendered when there are no documents | ### Pagination props | Prop | Type | Default | Description | |------|------|---------|-------------| -| `isLoadingMore` | `boolean` | `false` | Shows indicator when loading more | -| `hasMore` | `boolean` | `false` | Whether more documents available | -| `totalLoaded` | `number` | - | Total documents currently loaded | -| `loadMoreDocuments` | `() => Promise` | - | Callback to load more | -| `autoLoadOnViewport` | `boolean` | `true` | Auto-load when 80% visible | +| `isLoadingMore` | `boolean` | `false` | Whether more data is being loaded | +| `hasMore` | `boolean` | `false` | Whether there are more documents to load | +| `onLoadMore` | `() => void` | - | Callback to load more documents | +| `totalCount` | `number` | - | Total count, used by the loading indicator | ### Display props | Prop | Type | Default | Description | |------|------|---------|-------------| -| `showSpacesSelector` | `boolean` | variant-based | Show space filter dropdown | | `highlightDocumentIds` | `string[]` | `[]` | Document IDs to highlight | -| `highlightsVisible` | `boolean` | `true` | Whether highlights shown | -| `occludedRightPx` | `number` | `0` | Pixels occluded on right | +| `highlightsVisible` | `boolean` | `true` | Whether highlights are visible | +| `showFps` | `boolean` | `false` | Show FPS counter overlay | +| `canvasRef` | `RefObject` | - | External ref to the canvas (e.g. for screenshot export) | +| `colors` | `Partial` | - | Custom theme colors, merged with CSS-variable/default values | +| `labels` | `MemoryGraphLabels` | - | Custom user-facing labels, merged with defaults | +| `layering` | `MemoryGraphLayering` | - | Overlay layering controls (e.g. hover popover z-index) | +| `appearance` | `GraphAppearance` | - | Opt-in `documentNodes: 'stacked'` and `connections: 'emphasized'` rendering | -### Controlled state props +### Filtering props -| Prop | Type | Description | -|------|------|-------------| -| `selectedSpace` | `string` | Currently selected space (use `"all"` for all) | -| `onSpaceChange` | `(spaceId: string) => void` | Callback when space changes | -| `memoryLimit` | `number` | Max memories per document when space selected | +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `maxNodes` | `number` | - | Caps the total document plus memory node count | + +`containerTags`, `documentIds` and `legendId` are in `MemoryGraphProps` but `MemoryGraph` does not use them. Filter `documents` before you pass them in. + +### Slideshow props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `isSlideshowActive` | `boolean` | - | Whether slideshow mode is active | +| `onSlideshowNodeChange` | `(nodeId: string \| null) => void` | - | Called as the slideshow moves between nodes | +| `onSlideshowStop` | `() => void` | - | Called when the slideshow ends | + +### Other + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `onOpenDocument` | `(documentId: string) => void` | - | Called when the user wants to view a document's full content | +| `openDocumentOnEnter` | `boolean` | `false` | Let Enter call `onOpenDocument` for the selected document | --- ## Data types -### DocumentWithMemories +### GraphApiDocument / GraphApiMemory + +This is the shape `documents` actually takes: `documentType` and `memories`, matching what the API route in [Backend API Route](#backend-api-route) normalizes into: + +```typescript +interface GraphApiDocument { + id: string; + title: string | null; + summary: string | null; + documentType: string; + createdAt: string; + updatedAt: string; + memories: GraphApiMemory[]; +} + +interface GraphApiMemory { + id: string; + memory: string; + content?: string | null; + isStatic: boolean; + spaceId: string; + isLatest: boolean; + isForgotten: boolean; + forgetAfter: string | null; + forgetReason: string | null; + version: number; + parentMemoryId: string | null; + rootMemoryId: string | null; + createdAt: string; + updatedAt: string; + relation?: 'updates' | 'extends' | 'derives' | null; + updatesMemoryId?: string | null; + nextVersionId?: string | null; + memoryRelations?: Record | null; + spaceContainerTag?: string | null; +} +``` + +### DocumentWithMemories / MemoryEntry + +Also exported for backward compatibility with older API response shapes. Not what `documents` takes today, but still useful for typing a raw fetch response before mapping it to `GraphApiDocument`: ```typescript interface DocumentWithMemories { id: string; - customId?: string | null; - title?: string | null; - content?: string | null; + title: string | null; + url: string | null; + documentType: string; + createdAt: string; + updatedAt: string; summary?: string | null; - url?: string | null; - source?: string | null; - type?: string | null; - status: 'pending' | 'processing' | 'done' | 'failed'; - metadata?: Record | null; - createdAt: string | Date; - updatedAt: string | Date; - memoryEntries: MemoryEntry[]; + memories: MemoryEntry[]; } -``` -### MemoryEntry - -```typescript interface MemoryEntry { id: string; - documentId: string; - content: string | null; - summary?: string | null; - title?: string | null; - type?: string | null; - metadata?: Record | null; - createdAt: string | Date; - updatedAt: string | Date; - spaceContainerTag?: string | null; - relation?: 'updates' | 'extends' | 'derives' | null; - isLatest?: boolean; + memory: string; + content?: string | null; + createdAt: string; + updatedAt: string; spaceId?: string | null; + isStatic?: boolean; + isForgotten?: boolean; + forgetAfter?: string | null; + forgetReason?: string | null; + version?: number; + parentMemoryId?: string | null; + rootMemoryId?: string | null; + isLatest?: boolean; + relation?: 'updates' | 'extends' | 'derives' | null; + spaceContainerTag?: string | null; } ``` @@ -341,26 +583,52 @@ interface MemoryEntry { ### Components ```typescript -import { - MemoryGraph, - GraphCanvas, - Legend, - LoadingIndicator, - NodeDetailPanel, - SpacesDropdown -} from '@supermemory/memory-graph'; +import { MemoryGraph, GraphCanvas } from '@supermemory/memory-graph'; ``` ### Hooks ```typescript -import { useGraphData, useGraphInteractions } from '@supermemory/memory-graph'; +import { useGraphData, useGraphTheme } from '@supermemory/memory-graph'; +``` + +### Engine classes + +For advanced usage: driving the force simulation, viewport, or hit-testing directly: + +```typescript +import { + ForceSimulation, + ViewportState, + SpatialIndex, + VersionChainIndex, +} from '@supermemory/memory-graph'; ``` ### Constants ```typescript -import { colors, GRAPH_SETTINGS, LAYOUT_CONSTANTS } from '@supermemory/memory-graph'; +import { + DEFAULT_COLORS, + DEFAULT_HOVER_POPOVER_Z_INDEX, + DEFAULT_LABELS, + FORCE_CONFIG, + GRAPH_SETTINGS, +} from '@supermemory/memory-graph'; +``` + +### Mock data + +`@supermemory/memory-graph/mock-data` generates fake `GraphApiDocument[]` data for local development and stress testing, without a live API key: + +```typescript +import { generateMockGraphData } from '@supermemory/memory-graph/mock-data'; + +const { documents } = generateMockGraphData({ + documentCount: 100, + memoriesPerDoc: [2, 5], + seed: 12345, +}); ``` --- diff --git a/apps/memory-graph-playground/README.md b/apps/memory-graph-playground/README.md index 5c143d9b..4c116ee4 100644 --- a/apps/memory-graph-playground/README.md +++ b/apps/memory-graph-playground/README.md @@ -14,10 +14,10 @@ Open [http://localhost:3000](http://localhost:3000) and enter your Supermemory A ## Usage Example ```tsx -import { MemoryGraph, type DocumentWithMemories } from '@supermemory/memory-graph' +import { MemoryGraph, type GraphApiDocument } from '@supermemory/memory-graph' function App() { - const [documents, setDocuments] = useState([]) + const [documents, setDocuments] = useState([]) return ( {}} - totalLoaded={documents.length} + onLoadMore={() => {}} + totalCount={documents.length} variant="console" - showSpacesSelector={true} >
No memories found
@@ -37,16 +36,19 @@ function App() { } ``` +See [`src/app/page.tsx`](./src/app/page.tsx) for the full working example, including converting a raw API response into `GraphApiDocument[]`. + ## Props | Prop | Type | Description | |------|------|-------------| -| `documents` | `DocumentWithMemories[]` | Array of documents to display | +| `documents` | `GraphApiDocument[]` | Array of documents to display | | `isLoading` | `boolean` | Initial loading state | | `isLoadingMore` | `boolean` | Loading more documents state | | `error` | `Error \| null` | Error to display | | `hasMore` | `boolean` | Whether more documents can be loaded | -| `loadMoreDocuments` | `() => void` | Callback to load more documents | -| `totalLoaded` | `number` | Total number of loaded documents | -| `variant` | `"default" \| "console"` | Visual theme variant | -| `showSpacesSelector` | `boolean` | Show space filter dropdown | +| `onLoadMore` | `() => void` | Callback to load more documents | +| `totalCount` | `number` | Total number of loaded documents | +| `variant` | `"console" \| "consumer"` | Visual variant | + +See [`packages/memory-graph`](../../packages/memory-graph/src/types.ts) for the full `MemoryGraphProps` reference.