| // src/components/discovery/KnowledgeGraphExplorer.tsx | |
| import { useRef, useCallback, useEffect, useState } from "react"; | |
| import { useKnowledgeGraph } from "@/hooks/useKnowledgeGraph"; | |
| import ForceGraph2D from "react-force-graph-2d"; | |
| import { Loader2, ZoomIn, ZoomOut, Maximize2, Search } from "lucide-react"; | |
| const NODE_COLORS: Record<string, string> = { | |
| paper: "#0d9488", author: "#7c3aed", concept: "#ea580c", citation: "#2563eb", | |
| }; | |
| export function KnowledgeGraphExplorer() { | |
| const { graph, loading, centerNodeId, setCenterNodeId } = useKnowledgeGraph(); | |
| const fgRef = useRef<any>(null); | |
| const containerRef = useRef<HTMLDivElement>(null); | |
| const [dimensions, setDimensions] = useState({ width: 800, height: 600 }); | |
| useEffect(() => { | |
| const resize = () => { | |
| if (containerRef.current) { | |
| setDimensions({ width: containerRef.current.offsetWidth, height: containerRef.current.offsetHeight }); | |
| } | |
| }; | |
| resize(); | |
| window.addEventListener("resize", resize); | |
| return () => window.removeEventListener("resize", resize); | |
| }, []); | |
| if (loading || !graph) { | |
| return <div className="flex justify-center py-12"><Loader2 className="w-6 h-6 animate-spin text-gray-400"/></div>; | |
| } | |
| return ( | |
| <div className="space-y-3"> | |
| <h2 className="text-xl font-bold flex items-center gap-2">Knowledge Graph Explorer</h2> | |
| <div className="flex gap-2"> | |
| <input type="text" placeholder="Focus on a paper or author..." className="input flex-1 text-sm" | |
| onKeyDown={e => { if (e.key === "Enter") setCenterNodeId(e.currentTarget.value); }} /> | |
| <button onClick={() => fgRef.current?.zoomToFit(400)} className="btn btn-ghost btn-sm"><Maximize2 className="w-4 h-4"/></button> | |
| <button onClick={() => fgRef.current?.zoom(1.5)} className="btn btn-ghost btn-sm"><ZoomIn className="w-4 h-4"/></button> | |
| <button onClick={() => fgRef.current?.zoom(0.75)} className="btn btn-ghost btn-sm"><ZoomOut className="w-4 h-4"/></button> | |
| </div> | |
| <div ref={containerRef} className="glass rounded-xl overflow-hidden" style={{ height: 500 }}> | |
| <ForceGraph2D | |
| ref={fgRef} | |
| graphData={graph} | |
| width={dimensions.width} | |
| height={dimensions.height} | |
| nodeLabel="label" | |
| nodeColor={node => NODE_COLORS[(node as any).type] || "#999"} | |
| nodeVal={node => (node as any).size ?? 3} | |
| linkWidth={link => (link as any).strength ?? 1} | |
| linkDirectionalArrowLength={3} | |
| onNodeClick={node => setCenterNodeId((node as any).id)} | |
| /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
Xet Storage Details
- Size:
- 2.61 kB
- Xet hash:
- a3a43c1cfc9643c631aef72d1bf67906b44a28d5dc638405612415de867f6f3f
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.