diff --git a/ui/web/src/pages/memory/kg-graph-view.tsx b/ui/web/src/pages/memory/kg-graph-view.tsx index 65f50005..111d73bb 100644 --- a/ui/web/src/pages/memory/kg-graph-view.tsx +++ b/ui/web/src/pages/memory/kg-graph-view.tsx @@ -1,6 +1,6 @@ import { useRef, useMemo, useCallback, useState, useEffect, useLayoutEffect } from "react"; import ForceGraph2D, { type ForceGraphMethods } from "react-force-graph-2d"; -import { Maximize2 } from "lucide-react"; +import { Maximize2, ZoomIn, ZoomOut } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useTranslation } from "react-i18next"; import { useUiStore } from "@/stores/use-ui-store"; @@ -30,6 +30,21 @@ export function KGGraphView({ entities: allEntities, relations: allRelations, on const [selectedNodeId, setSelectedNodeId] = useState(null); const [nodeLimit, setNodeLimit] = useState(DEFAULT_NODE_LIMIT); const [ready, setReady] = useState(false); + const [zoomLevel, setZoomLevel] = useState(1); + + const ZOOM_STEP = 1.5; + + const handleZoomIn = useCallback(() => { + const fg = graphRef.current; + if (!fg) return; + fg.zoom(Math.min((fg.zoom() ?? 1) * ZOOM_STEP, 8), 300); + }, []); + + const handleZoomOut = useCallback(() => { + const fg = graphRef.current; + if (!fg) return; + fg.zoom(Math.max((fg.zoom() ?? 1) / ZOOM_STEP, 0.1), 300); + }, []); // Double-click detection refs const clickTimerRef = useRef | null>(null); @@ -223,6 +238,7 @@ export function KGGraphView({ entities: allEntities, relations: allRelations, on linkDirectionalParticleSpeed={0.004} onNodeClick={handleNodeClick} onBackgroundClick={handleBackgroundClick} + onZoom={(transform) => setZoomLevel(transform.k)} onEngineStop={handleEngineStop} backgroundColor="transparent" d3AlphaDecay={0.04} @@ -243,6 +259,15 @@ export function KGGraphView({ entities: allEntities, relations: allRelations, on ยท {t("kg.graphView.limitNote", { limit: nodeLimit, total: totalCount })} )}
+
+ + {Math.round(zoomLevel * 100)}% + +