supermemory/apps/web/components/memory-graph/memory-graph-wrapper.tsx
Vorflux AI 1c5ef7e5ed Rewrite @supermemory/memory-graph with perf optimizations and consolidate consumers
Package changes (packages/memory-graph):
- Spatial grid nearest-neighbor: O(n^2) -> O(n*k) for doc-doc edge rendering
- Zoom-based edge culling: skip low-similarity edges at low zoom levels
- Modular canvas engine from mono repo (renderer, simulation, viewport, hit-test)
- Remove vanilla-extract CSS, use inline styles only
- Add motion/react for slide-in animations
- Export mock data generator for stress testing
- Add performance and integration tests (15/15 pass)

Web app changes (apps/web):
- Remove local graph implementation (~4400 lines deleted)
- Add thin wrapper (memory-graph-wrapper.tsx) using useGraphApi hook
- Keep app-specific graph-card.tsx and use-graph-api.ts

Playground changes (apps/memory-graph-playground):
- Add stress test buttons (50/100/200/500 docs)
- Add mock data generation via package
- Convert DocumentWithMemories to GraphApiDocument format
- Show FPS counter during stress tests

All TypeScript compiles clean. Performance tests: 15/15 pass.
2026-03-26 17:58:28 +00:00

78 lines
2 KiB
TypeScript

"use client"
import { useEffect, useRef, useState } from "react"
import { MemoryGraph as MemoryGraphBase } from "@supermemory/memory-graph"
import type { GraphApiDocument, GraphApiEdge } from "@supermemory/memory-graph"
import { useGraphApi } from "./hooks/use-graph-api"
export interface MemoryGraphWrapperProps {
children?: React.ReactNode
isLoading?: boolean
error?: Error | null
variant?: "console" | "consumer"
legendId?: string
highlightDocumentIds?: string[]
highlightsVisible?: boolean
containerTags?: string[]
documentIds?: string[]
maxNodes?: number
isSlideshowActive?: boolean
onSlideshowNodeChange?: (nodeId: string | null) => void
onSlideshowStop?: () => void
canvasRef?: React.RefObject<HTMLCanvasElement | null>
}
export function MemoryGraph({
children,
isLoading: externalIsLoading = false,
error: externalError = null,
variant = "console",
containerTags,
documentIds,
maxNodes = 200,
canvasRef,
...rest
}: MemoryGraphWrapperProps) {
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 })
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
const el = containerRef.current
if (!el) return
const ro = new ResizeObserver(() => {
setContainerSize({ width: el.clientWidth, height: el.clientHeight })
})
ro.observe(el)
setContainerSize({ width: el.clientWidth, height: el.clientHeight })
return () => ro.disconnect()
}, [])
const {
data: apiData,
isLoading: apiIsLoading,
error: apiError,
} = useGraphApi({
containerTags,
documentIds,
limit: maxNodes,
enabled: containerSize.width > 0 && containerSize.height > 0,
})
return (
<div ref={containerRef} className="w-full h-full">
<MemoryGraphBase
documents={apiData.documents as GraphApiDocument[]}
apiEdges={apiData.edges as GraphApiEdge[]}
isLoading={externalIsLoading || apiIsLoading}
error={externalError || apiError}
variant={variant}
maxNodes={maxNodes}
canvasRef={canvasRef}
totalCount={apiData.totalCount}
{...rest}
>
{children}
</MemoryGraphBase>
</div>
)
}