supermemory/apps/web/components/graph-layout-view.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

80 lines
2 KiB
TypeScript

"use client"
import { memo, useCallback, useRef } from "react"
import { useQueryState } from "nuqs"
import Image from "next/image"
import { MemoryGraph } from "./memory-graph"
import { useProject } from "@/stores"
import { useGraphHighlights } from "@/stores/highlights"
import { Button } from "@ui/components/button"
import { cn } from "@lib/utils"
import { dmSansClassName } from "@/lib/fonts"
import { ShareModal } from "./share-modal"
import { shareParam } from "@/lib/search-params"
interface GraphLayoutViewProps {
isChatOpen: boolean
}
export const GraphLayoutView = memo<GraphLayoutViewProps>(({ isChatOpen }) => {
const { effectiveContainerTags } = useProject()
const { documentIds: allHighlightDocumentIds } = useGraphHighlights()
const [isShareModalOpen, setIsShareModalOpen] = useQueryState(
"share",
shareParam,
)
const canvasRef = useRef<HTMLCanvasElement>(null)
const handleShare = useCallback(() => {
setIsShareModalOpen(true)
}, [setIsShareModalOpen])
const handleCloseShareModal = useCallback(() => {
setIsShareModalOpen(false)
}, [setIsShareModalOpen])
return (
<div className="relative w-full h-[calc(100vh-86px)]">
{/* Full-width graph */}
<div className="absolute inset-0">
<MemoryGraph
containerTags={effectiveContainerTags}
variant="consumer"
highlightDocumentIds={allHighlightDocumentIds}
highlightsVisible={isChatOpen}
maxNodes={200}
canvasRef={canvasRef}
/>
</div>
{/* Share graph button - top left */}
<div className="absolute top-4 left-4 z-15">
<Button
variant="headers"
className={cn(
"rounded-full text-base gap-2 h-10!",
dmSansClassName(),
)}
onClick={handleShare}
>
<Image
src="/icons/share-graph.svg"
alt="Share"
width={16}
height={16}
/>
Share graph
</Button>
</div>
{/* Share modal */}
<ShareModal
isOpen={isShareModalOpen}
onClose={handleCloseShareModal}
graphCanvasRef={canvasRef}
/>
</div>
)
})
GraphLayoutView.displayName = "GraphLayoutView"