aboutsummaryrefslogtreecommitdiff
path: root/packages/ui/memory-graph
diff options
context:
space:
mode:
Diffstat (limited to 'packages/ui/memory-graph')
-rw-r--r--packages/ui/memory-graph/hooks/use-graph-data.ts18
-rw-r--r--packages/ui/memory-graph/memory-graph.tsx64
-rw-r--r--packages/ui/memory-graph/navigation-controls.tsx97
3 files changed, 97 insertions, 82 deletions
diff --git a/packages/ui/memory-graph/hooks/use-graph-data.ts b/packages/ui/memory-graph/hooks/use-graph-data.ts
index 3e9fa5cc..ec17a756 100644
--- a/packages/ui/memory-graph/hooks/use-graph-data.ts
+++ b/packages/ui/memory-graph/hooks/use-graph-data.ts
@@ -29,26 +29,26 @@ export function useGraphData(
const allEdges: GraphEdge[] = [];
// Filter documents that have memories in selected space
- const filteredDocuments = data.documents
+ const filteredDocuments = (data.documents || [])
.map((doc) => ({
...doc,
memoryEntries:
selectedSpace === "all"
- ? doc.memoryEntries
- : doc.memoryEntries.filter(
+ ? doc.memoryEntries || []
+ : (doc.memoryEntries || []).filter(
(memory) =>
(memory.spaceContainerTag ?? memory.spaceId ?? "default") ===
selectedSpace,
),
}))
- .filter((doc) => doc.memoryEntries.length > 0);
+ .filter((doc) => (doc.memoryEntries || []).length > 0);
// Group documents by space for better clustering
const documentsBySpace = new Map<string, typeof filteredDocuments>();
filteredDocuments.forEach((doc) => {
const docSpace =
- doc.memoryEntries[0]?.spaceContainerTag ??
- doc.memoryEntries[0]?.spaceId ??
+ (doc.memoryEntries || [])[0]?.spaceContainerTag ??
+ (doc.memoryEntries || [])[0]?.spaceId ??
"default";
if (!documentsBySpace.has(docSpace)) {
documentsBySpace.set(docSpace, []);
@@ -171,7 +171,7 @@ export function useGraphData(
const memoryNodeMap = new Map<string, GraphNode>();
const doc = docNode.data as DocumentWithMemories;
- doc.memoryEntries.forEach((memory, memIndex) => {
+ (doc.memoryEntries || []).forEach((memory, memIndex) => {
const memoryId = `${memory.id}`;
const customMemPos = nodePositions.get(memoryId);
@@ -231,8 +231,8 @@ export function useGraphData(
});
// Add version-chain edges (old -> new)
- data.documents.forEach((doc) => {
- doc.memoryEntries.forEach((mem: MemoryEntry) => {
+ (data.documents || []).forEach((doc) => {
+ (doc.memoryEntries || []).forEach((mem: MemoryEntry) => {
// Support both new object structure and legacy array/single parent fields
let parentRelations: Record<string, MemoryRelation> = {};
diff --git a/packages/ui/memory-graph/memory-graph.tsx b/packages/ui/memory-graph/memory-graph.tsx
index 912a741a..72ddf090 100644
--- a/packages/ui/memory-graph/memory-graph.tsx
+++ b/packages/ui/memory-graph/memory-graph.tsx
@@ -157,8 +157,8 @@ export const MemoryGraph = ({
const spaceSet = new Set<string>();
const counts: Record<string, number> = {};
- data.documents.forEach((doc) => {
- doc.memoryEntries.forEach((memory) => {
+ (data.documents || []).forEach((doc) => {
+ (doc.memoryEntries || []).forEach((memory) => {
const spaceId = memory.spaceContainerTag || memory.spaceId || "default";
spaceSet.add(spaceId);
counts[spaceId] = (counts[spaceId] || 0) + 1;
@@ -199,32 +199,47 @@ export const MemoryGraph = ({
const handleCenter = useCallback(() => {
if (nodes.length > 0) {
// Calculate center of all nodes
- let sumX = 0
- let sumY = 0
- let count = 0
-
+ let sumX = 0;
+ let sumY = 0;
+ let count = 0;
+
nodes.forEach((node) => {
- sumX += node.x
- sumY += node.y
- count++
- })
-
+ sumX += node.x;
+ sumY += node.y;
+ count++;
+ });
+
if (count > 0) {
- const centerX = sumX / count
- const centerY = sumY / count
- centerViewportOn(centerX, centerY, containerSize.width, containerSize.height)
+ const centerX = sumX / count;
+ const centerY = sumY / count;
+ centerViewportOn(
+ centerX,
+ centerY,
+ containerSize.width,
+ containerSize.height,
+ );
}
}
- }, [nodes, centerViewportOn, containerSize.width, containerSize.height])
+ }, [nodes, centerViewportOn, containerSize.width, containerSize.height]);
const handleAutoFit = useCallback(() => {
- if (nodes.length > 0 && containerSize.width > 0 && containerSize.height > 0) {
+ if (
+ nodes.length > 0 &&
+ containerSize.width > 0 &&
+ containerSize.height > 0
+ ) {
autoFitToViewport(nodes, containerSize.width, containerSize.height, {
occludedRightPx,
animate: true,
- })
+ });
}
- }, [nodes, containerSize.width, containerSize.height, occludedRightPx, autoFitToViewport])
+ }, [
+ nodes,
+ containerSize.width,
+ containerSize.height,
+ occludedRightPx,
+ autoFitToViewport,
+ ]);
// Get selected node data
const selectedNodeData = useMemo(() => {
@@ -251,7 +266,7 @@ export const MemoryGraph = ({
};
// Count visible documents
- const visibleDocuments = data.documents.filter((doc) => {
+ const visibleDocuments = (data.documents || []).filter((doc) => {
const docNodes = nodes.filter(
(node) => node.type === "document" && node.data.id === doc.id,
);
@@ -265,7 +280,8 @@ export const MemoryGraph = ({
});
// If 80% or more of documents are visible, load more
- const visibilityRatio = visibleDocuments.length / data.documents.length;
+ const visibilityRatio =
+ visibleDocuments.length / (data.documents || []).length;
if (visibilityRatio >= 0.8) {
loadMoreDocuments();
}
@@ -421,8 +437,12 @@ export const MemoryGraph = ({
{containerSize.width > 0 && (
<NavigationControls
onCenter={handleCenter}
- onZoomIn={() => zoomIn(containerSize.width / 2, containerSize.height / 2)}
- onZoomOut={() => zoomOut(containerSize.width / 2, containerSize.height / 2)}
+ onZoomIn={() =>
+ zoomIn(containerSize.width / 2, containerSize.height / 2)
+ }
+ onZoomOut={() =>
+ zoomOut(containerSize.width / 2, containerSize.height / 2)
+ }
onAutoFit={handleAutoFit}
nodes={nodes}
className="absolute bottom-4 left-4"
diff --git a/packages/ui/memory-graph/navigation-controls.tsx b/packages/ui/memory-graph/navigation-controls.tsx
index b2abd67f..afd98a0f 100644
--- a/packages/ui/memory-graph/navigation-controls.tsx
+++ b/packages/ui/memory-graph/navigation-controls.tsx
@@ -1,67 +1,62 @@
-"use client"
+"use client";
-import { memo } from "react"
-import type { GraphNode } from "./types"
+import { memo } from "react";
+import type { GraphNode } from "./types";
interface NavigationControlsProps {
- onCenter: () => void
- onZoomIn: () => void
- onZoomOut: () => void
- onAutoFit: () => void
- nodes: GraphNode[]
- className?: string
+ onCenter: () => void;
+ onZoomIn: () => void;
+ onZoomOut: () => void;
+ onAutoFit: () => void;
+ nodes: GraphNode[];
+ className?: string;
}
-export const NavigationControls = memo<NavigationControlsProps>(({
- onCenter,
- onZoomIn,
- onZoomOut,
- onAutoFit,
- nodes,
- className = "",
-}) => {
- if (nodes.length === 0) {
- return null
- }
+export const NavigationControls = memo<NavigationControlsProps>(
+ ({ onCenter, onZoomIn, onZoomOut, onAutoFit, nodes, className = "" }) => {
+ if (nodes.length === 0) {
+ return null;
+ }
- return (
- <div className={`flex flex-col gap-1 ${className}`}>
- <button
- type="button"
- onClick={onAutoFit}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Auto-fit graph to viewport"
- >
- Fit
- </button>
- <button
- type="button"
- onClick={onCenter}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Center view on graph"
- >
- Center
- </button>
- <div className="flex flex-col">
+ return (
+ <div className={`flex flex-col gap-1 ${className}`}>
<button
type="button"
- onClick={onZoomIn}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-t-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16 border-b-0"
- title="Zoom in"
+ onClick={onAutoFit}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Auto-fit graph to viewport"
>
- +
+ Fit
</button>
<button
type="button"
- onClick={onZoomOut}
- className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-b-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
- title="Zoom out"
+ onClick={onCenter}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Center view on graph"
>
- −
+ Center
</button>
+ <div className="flex flex-col">
+ <button
+ type="button"
+ onClick={onZoomIn}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-t-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16 border-b-0"
+ title="Zoom in"
+ >
+ +
+ </button>
+ <button
+ type="button"
+ onClick={onZoomOut}
+ className="bg-black/20 backdrop-blur-sm hover:bg-black/30 border border-white/10 hover:border-white/20 rounded-b-lg p-2 text-white/70 hover:text-white transition-colors text-xs font-medium min-w-16"
+ title="Zoom out"
+ >
+ −
+ </button>
+ </div>
</div>
- </div>
- )
-})
+ );
+ },
+);
-NavigationControls.displayName = "NavigationControls" \ No newline at end of file
+NavigationControls.displayName = "NavigationControls";