import React, { useCallback, useMemo, useRef, useState } from "react"; import { Box, Collapse, IconButton, Paper, Typography } from "@mui/material"; import { ExpandLess, ExpandMore } from "@mui/icons-material"; import ForceGraph2D from "react-force-graph-2d"; import type { ForceGraphMethods, LinkObject, NodeObject } from "react-force-graph-2d"; import type { FactAnnotationResponse } from "../types"; interface TemporalGraphProps { annotations: FactAnnotationResponse[]; } // Extend the library base types with our own fields type GraphNode = NodeObject<{ label: string }>; type GraphLink = LinkObject; const COLOR_MAP: Record = { green: "#22c55e", red: "#ef4444", orange: "#f97316", yellow: "#eab308", }; function getEdgeColor(color: string): string { return COLOR_MAP[color.toLowerCase()] ?? "#94a3b8"; } const LEGEND_ITEMS = [ { color: "#22c55e", label: "Consistent" }, { color: "#ef4444", label: "Inconsistent" }, ] as const; function TemporalGraph({ annotations }: TemporalGraphProps): React.ReactElement { const [expanded, setExpanded] = useState(false); const graphRef = useRef | undefined>(undefined); const graphData = useMemo(() => { const nodeSet = new Set(); annotations.forEach((ann) => { nodeSet.add(ann.subject); nodeSet.add(ann.object); }); const nodes: GraphNode[] = Array.from(nodeSet).map((name) => ({ id: name, label: name, })); const links: GraphLink[] = annotations.map((ann) => ({ source: ann.subject, target: ann.object, label: `${ann.predicate} (${ann.time})`, color: getEdgeColor(ann.color), })); return { nodes, links }; }, [annotations]); const handleEngineStop = useCallback((): void => { graphRef.current?.zoomToFit(400); }, []); return ( setExpanded((prev) => !prev)} > Temporal Knowledge Graph {expanded ? : } ref={graphRef} graphData={graphData} width={700} height={350} nodeLabel={(node) => node.label} nodeColor={() => "#60a5fa"} nodeRelSize={6} linkColor={(link) => link.color} linkDirectionalArrowLength={6} linkLabel={(link) => link.label} linkWidth={2} cooldownTicks={100} onEngineStop={handleEngineStop} /> {LEGEND_ITEMS.map(({ color, label }) => ( {label} ))} ); } export default TemporalGraph;