mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-19 07:42:43 +00:00
fix: memory graph progressively loads all data and remove background
- Switch to infinite query with viewport-triggered pagination (loads more when user zooms out 3x past node bounds) - Remove maxNodes cap so all data renders - Remove background color and dot pattern from graph - Make document-memory edges light grey Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a91136dad8
commit
26ad0e32e2
6 changed files with 58 additions and 44 deletions
|
|
@ -42,7 +42,7 @@ export const GraphLayoutView = memo<GraphLayoutViewProps>(({ isChatOpen }) => {
|
|||
variant="consumer"
|
||||
highlightDocumentIds={allHighlightDocumentIds}
|
||||
highlightsVisible={isChatOpen}
|
||||
maxNodes={200}
|
||||
maxNodes={undefined}
|
||||
canvasRef={canvasRef}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -126,10 +126,7 @@ export function useGraphApi(options: UseGraphApiOptions = {}) {
|
|||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const { currentPage, totalPages } = lastPage.pagination
|
||||
if (currentPage < totalPages) {
|
||||
return currentPage + 1
|
||||
}
|
||||
return undefined
|
||||
return currentPage < totalPages ? currentPage + 1 : undefined
|
||||
},
|
||||
staleTime: 30 * 1000,
|
||||
enabled,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export function MemoryGraph({
|
|||
error: externalError = null,
|
||||
variant = "console",
|
||||
containerTags,
|
||||
maxNodes = 200,
|
||||
maxNodes,
|
||||
canvasRef,
|
||||
...rest
|
||||
}: MemoryGraphWrapperProps) {
|
||||
|
|
@ -59,7 +59,7 @@ export function MemoryGraph({
|
|||
})
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="w-full h-full">
|
||||
<div ref={containerRef} className="w-full h-full [&>div]:!bg-none">
|
||||
<MemoryGraphBase
|
||||
documents={documents}
|
||||
isLoading={externalIsLoading || apiIsLoading}
|
||||
|
|
@ -71,6 +71,10 @@ export function MemoryGraph({
|
|||
maxNodes={maxNodes}
|
||||
canvasRef={canvasRef}
|
||||
totalCount={totalCount}
|
||||
colors={{
|
||||
bg: "transparent",
|
||||
edgeDerives: "#9ca3af",
|
||||
} as any}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@
|
|||
"@react-router/fs-routes": "^7.6.2",
|
||||
"@react-router/node": "^7.6.2",
|
||||
"@react-router/serve": "^7.6.2",
|
||||
"@repo/lib": "workspace:*",
|
||||
"@repo/validation": "workspace:*",
|
||||
"@sentry/nextjs": "^10.33.0",
|
||||
"@supermemory/memory-graph": "^0.2.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/react-form": "^1.12.4",
|
||||
"@tanstack/react-query": "^5.90.14",
|
||||
|
|
@ -104,10 +107,7 @@
|
|||
"tw-animate-css": "^1.3.4",
|
||||
"use-debounce": "^10.1.0",
|
||||
"vaul": "^1.1.2",
|
||||
"zustand": "^5.0.7",
|
||||
"@repo/lib": "workspace:*",
|
||||
"@repo/validation": "workspace:*",
|
||||
"@supermemory/memory-graph": "workspace:*"
|
||||
"zustand": "^5.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "^2.2.2",
|
||||
|
|
|
|||
2
bun.lock
2
bun.lock
|
|
@ -161,7 +161,7 @@
|
|||
"@repo/lib": "workspace:*",
|
||||
"@repo/validation": "workspace:*",
|
||||
"@sentry/nextjs": "^10.33.0",
|
||||
"@supermemory/memory-graph": "workspace:*",
|
||||
"@supermemory/memory-graph": "^0.2.0",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/react-form": "^1.12.4",
|
||||
"@tanstack/react-query": "^5.90.14",
|
||||
|
|
|
|||
|
|
@ -185,17 +185,56 @@ export function MemoryGraph({
|
|||
// Drag end handled by InputHandler
|
||||
}, [])
|
||||
|
||||
// Load more when user zooms out enough that visible area dwarfs the node extent
|
||||
const loadMoreTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const loadMoreRef = useRef({ hasMore, isLoadingMore, onLoadMore })
|
||||
loadMoreRef.current = { hasMore, isLoadingMore, onLoadMore }
|
||||
|
||||
const handleViewportChange = useCallback(
|
||||
(zoom: number, popoverVisible: boolean) => {
|
||||
setZoomDisplay(Math.round(zoom * 100))
|
||||
// Only increment viewportVersion (which triggers popover repositioning
|
||||
// via activePopoverPosition useMemo) when a popover is actually visible.
|
||||
// This avoids 60fps React reconciliation during plain panning/zooming.
|
||||
if (popoverVisible) {
|
||||
setViewportVersion((v) => v + 1)
|
||||
}
|
||||
|
||||
const { hasMore: more, isLoadingMore: loading, onLoadMore: load } = loadMoreRef.current
|
||||
if (!more || loading || !load || !viewportRef.current) return
|
||||
|
||||
const vp = viewportRef.current
|
||||
const currentNodes = nodes
|
||||
if (currentNodes.length === 0) return
|
||||
|
||||
const topLeft = vp.screenToWorld(0, 0)
|
||||
const bottomRight = vp.screenToWorld(containerSize.width, containerSize.height)
|
||||
const viewW = bottomRight.x - topLeft.x
|
||||
const viewH = bottomRight.y - topLeft.y
|
||||
|
||||
let minX = Infinity
|
||||
let minY = Infinity
|
||||
let maxX = -Infinity
|
||||
let maxY = -Infinity
|
||||
for (const n of currentNodes) {
|
||||
if (n.x < minX) minX = n.x
|
||||
if (n.y < minY) minY = n.y
|
||||
if (n.x > maxX) maxX = n.x
|
||||
if (n.y > maxY) maxY = n.y
|
||||
}
|
||||
|
||||
const nodeW = maxX - minX || 1
|
||||
const nodeH = maxY - minY || 1
|
||||
|
||||
// Only trigger when the visible area is 3x larger than the node extent
|
||||
// (i.e. user has zoomed out significantly past the data)
|
||||
const zoomedOut = viewW > nodeW * 3 || viewH > nodeH * 3
|
||||
|
||||
if (zoomedOut && !loadMoreTimerRef.current) {
|
||||
loadMoreTimerRef.current = setTimeout(() => {
|
||||
loadMoreTimerRef.current = null
|
||||
loadMoreRef.current.onLoadMore?.()
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
[],
|
||||
[nodes, containerSize.width, containerSize.height],
|
||||
)
|
||||
|
||||
// Navigation
|
||||
|
|
@ -509,7 +548,7 @@ export function MemoryGraph({
|
|||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: colors.bg,
|
||||
backgroundColor: "transparent",
|
||||
borderRadius: 12,
|
||||
}
|
||||
|
||||
|
|
@ -535,9 +574,7 @@ export function MemoryGraph({
|
|||
height: "100%",
|
||||
borderRadius: 12,
|
||||
overflow: "hidden",
|
||||
backgroundColor: colors.bg,
|
||||
backgroundImage: `radial-gradient(circle, ${colors.textMuted} 0.5px, transparent 0.5px)`,
|
||||
backgroundSize: "16px 16px",
|
||||
backgroundColor: "transparent",
|
||||
}
|
||||
|
||||
const canvasContainerStyle: React.CSSProperties = {
|
||||
|
|
@ -576,30 +613,6 @@ export function MemoryGraph({
|
|||
colors={colors}
|
||||
/>
|
||||
|
||||
{!isLoading && hasMore && onLoadMore && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onLoadMore}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 16,
|
||||
right: 16,
|
||||
zIndex: 30,
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${colors.controlBorder}`,
|
||||
backgroundColor: colors.controlBg,
|
||||
color: colors.textSecondary,
|
||||
paddingLeft: 16,
|
||||
paddingRight: 16,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
fontSize: 13,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{isLoadingMore ? "Loading..." : "Load more"}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{!isLoading && !nodes.some((n) => n.type === "document") && children && (
|
||||
<div style={emptyStateStyle}>{children}</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue