Soma / frontend /src /components /KnowledgeGraph.jsx
Komalpreet Kaur
style: reduce knowledge graph tooltip node and link label font sizes
5cf6adc unverified
import { useEffect, useState, useRef } from 'react';
import ForceGraph3D from 'react-force-graph-3d';
import * as THREE from 'three';
import { apiFetch } from '../api';
import './KnowledgeGraph.css';
// Stunning conceptual mock graph representing SOMA's cognitive architecture
const MOCK_GRAPH = {
nodes: [
{ id: 'SOMA', label: 'SOMA (Core)', connections: 10, type: 'core' },
{ id: 'Cortex', label: 'Cortex Layer', connections: 8, type: 'core' },
{ id: 'Thalamus', label: 'Thalamus (Routing)', connections: 6, type: 'entity' },
{ id: 'Hippocampus', label: 'Hippocampus', connections: 7, type: 'entity' },
{ id: 'Neocortex', label: 'Neocortex', connections: 6, type: 'entity' },
{ id: 'Sensory Cortex', label: 'Sensory Cortex', connections: 4, type: 'concept' },
{ id: 'Working Memory', label: 'Working Memory', connections: 5, type: 'concept' },
{ id: 'Episodic Memory', label: 'Episodic Memory', connections: 4, type: 'concept' },
{ id: 'Sleep Cycle', label: 'Sleep Cycle', connections: 3, type: 'method' },
{ id: 'Neural Inscription', label: 'Inscription Layer', connections: 3, type: 'method' },
{ id: 'Llama 3.1', label: 'Llama 3.1', connections: 3, type: 'metric' },
{ id: 'Groq API', label: 'Groq API', connections: 2, type: 'metric' }
],
links: [
{ source: 'SOMA', target: 'Cortex', label: 'ORCHESTRATES' },
{ source: 'SOMA', target: 'Neural Inscription', label: 'ACCEPTS' },
{ source: 'Cortex', target: 'Thalamus', label: 'ROUTES_BY' },
{ source: 'Cortex', target: 'Hippocampus', label: 'CONSOLIDATES' },
{ source: 'Cortex', target: 'Neocortex', label: 'STORES_IN' },
{ source: 'Sensory Cortex', target: 'Hippocampus', label: 'WRITES_TO' },
{ source: 'Working Memory', target: 'Thalamus', label: 'SYNCS_WITH' },
{ source: 'Episodic Memory', target: 'Hippocampus', label: 'LOGS_IN' },
{ source: 'Sleep Cycle', target: 'Hippocampus', label: 'OPTIMIZES' },
{ source: 'Sleep Cycle', target: 'Neocortex', label: 'REINFORCES' },
{ source: 'Cortex', target: 'Llama 3.1', label: 'COMPUTES' },
{ source: 'Llama 3.1', target: 'Groq API', label: 'HOSTED_ON' }
]
};
function KnowledgeGraph({ refreshTick }) {
const [graphData, setGraphData] = useState({ nodes: [], links: [] });
const [loading, setLoading] = useState(true);
const [dbStatus, setDbStatus] = useState('connecting');
const [dimensions, setDimensions] = useState({ width: 800, height: 500 });
const [stats, setStats] = useState({ node_count: 0, edge_count: 0, top_entities: [] });
const [physicsActive, setPhysicsActive] = useState(true);
const containerRef = useRef(null);
const fgRef = useRef();
// Measure container dimensions to auto-resize the canvas
useEffect(() => {
if (!containerRef.current) return;
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
const { width, height } = entry.contentRect;
setDimensions({ width: width || 800, height: height || 500 });
}
});
resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, []);
// Fetch graph database from backend
const fetchGraph = async () => {
setLoading(true);
try {
const res = await apiFetch('/api/v1/graph');
if (!res.ok) {
throw new Error('Graph fetch returned unhealthy status');
}
const data = await res.json();
if (data.status === 'offline' || data.status === 'error' || !data.nodes || data.nodes.length === 0) {
setDbStatus(data.status || 'offline');
const nodes = MOCK_GRAPH.nodes.map(n => ({ ...n }));
const links = MOCK_GRAPH.links.map(l => ({ ...l }));
setGraphData({ nodes, links });
} else {
setDbStatus('online');
const nodes = data.nodes.map(n => ({
id: n.id,
label: n.label || n.id,
connections: n.connections || 1,
type: 'entity'
}));
const links = data.edges.map(e => ({
source: e.source,
target: e.target,
label: e.label || 'RELATED_TO'
}));
setGraphData({ nodes, links });
}
} catch (error) {
console.error('Graph fetch failed', error);
setDbStatus('offline');
setGraphData({
nodes: MOCK_GRAPH.nodes.map(n => ({ ...n })),
links: MOCK_GRAPH.links.map(l => ({ ...l }))
});
} finally {
setLoading(false);
}
};
const fetchStats = async () => {
try {
const res = await apiFetch('/api/v1/graph/stats');
if (res.ok) {
const data = await res.json();
if (data.status === 'online') {
setStats(data);
}
}
} catch (err) {
console.error('Stats fetch failed', err);
}
};
useEffect(() => {
fetchGraph();
fetchStats();
}, [refreshTick]);
// Physics force configurations to pull the neural cluster into a tight, brain-like shape
useEffect(() => {
if (fgRef.current) {
const d3Force = fgRef.current.d3Force;
if (d3Force) {
d3Force('charge').strength(-50); // Gentle repulsion for an ultra-compact molecular look
d3Force('link').distance(30); // Extremely tight connection paths to pull nodes together
}
// Auto-fit camera with very tight padding to zoom in close and eliminate empty workspace
setTimeout(() => {
fgRef.current.zoomToFit(800, 20);
}, 600);
}
}, [graphData]);
// Calculate max connections dynamically to scale color thresholds
const maxConnections = graphData.nodes && graphData.nodes.length > 0
? Math.max(...graphData.nodes.map(n => n.connections || 1), 1)
: 1;
// Color mapper based on node type & degree
const getNodeColor = (node) => {
if (node.id === 'SOMA') return '#ff6b35'; // Core Hub is SOMA Orange
// For offline/mock mode, retain the pre-assigned structural category colors
if (dbStatus !== 'online') {
if (node.type === 'core') return '#ff6b35'; // Orange
if (node.type === 'method') return '#ff8b54'; // Soft Orange
if (node.type === 'concept') return '#ffa67c'; // Pale Orange
if (node.type === 'metric') return '#a3a3a3'; // Neutral Grey
return '#737373'; // Darker Grey
}
// For live Neo4j data, dynamically scale colors based on relative synaptic density!
const connections = node.connections || 1;
const ratio = connections / maxConnections;
if (ratio >= 0.8) return '#ff6b35'; // High centrality (Orange)
if (ratio >= 0.4) return '#ff9c7a'; // Medium centrality (Soft Orange)
return '#a3a3a3'; // Low centrality (Silver/Grey)
};
return (
<div className="graph-stage fade-in">
<div className="graph-toolbar">
<div className="graph-title-block">
<button className="graph-select">
<span className="material-icons">hub</span>
<span>{dbStatus === 'online' ? 'Real-time 3D Neo4j Graph' : '3D SOMA Cognitive Architecture Model'}</span>
</button>
{dbStatus !== 'online' && (
<span className="db-status-badge warning pulse">
<span className="dot" />
Neo4j Offline - Displaying Interactive System Concept
</span>
)}
{dbStatus === 'online' && (
<span className="db-status-badge success">
<span className="dot" />
Neo4j Synchronized (Live)
</span>
)}
</div>
<div className="graph-actions">
<button
className="graph-icon-button"
onClick={() => {
if (fgRef.current) fgRef.current.zoomToFit(600, 20);
}}
title="Recenter Camera"
>
<span className="material-icons">zoom_out_map</span>
</button>
<button
className="graph-icon-button"
onClick={() => {
if (fgRef.current) {
const currentPos = fgRef.current.cameraPosition();
fgRef.current.cameraPosition(
{ x: currentPos.x * 0.75, y: currentPos.y * 0.75, z: currentPos.z * 0.75 },
null,
300
);
}
}}
title="Zoom In"
>
<span className="material-icons">zoom_in</span>
</button>
<button
className="graph-icon-button"
onClick={() => {
if (fgRef.current) {
const currentPos = fgRef.current.cameraPosition();
fgRef.current.cameraPosition(
{ x: currentPos.x * 1.35, y: currentPos.y * 1.35, z: currentPos.z * 1.35 },
null,
300
);
}
}}
title="Zoom Out"
>
<span className="material-icons">zoom_out</span>
</button>
<button
className="graph-icon-button"
onClick={() => {
const newActive = !physicsActive;
setPhysicsActive(newActive);
if (fgRef.current) {
if (physicsActive) {
fgRef.current.d3PauseSimulation();
} else {
fgRef.current.d3ResumeSimulation();
}
}
}}
title={physicsActive ? "Pause Simulation" : "Resume Simulation"}
>
<span className="material-icons">{physicsActive ? "pause" : "play_arrow"}</span>
</button>
<button
className="graph-icon-button"
onClick={() => {
fetchGraph();
fetchStats();
}}
title="Sync Mesh"
>
<span className="material-icons">sync</span>
</button>
</div>
</div>
<div className="graph-network-container" ref={containerRef}>
{/* Live Telemetry HUD Overlay */}
<div className="graph-hud-overlay">
<div className="hud-panel">
<div className="hud-header">
<span className="material-icons">analytics</span>
<span>Semantic Telemetry</span>
</div>
<div className="hud-row">
<span className="hud-label">Nodes:</span>
<span className="hud-value">{dbStatus === 'online' ? stats.node_count : graphData.nodes.length}</span>
</div>
<div className="hud-row">
<span className="hud-label">Synapses:</span>
<span className="hud-value">{dbStatus === 'online' ? stats.edge_count : graphData.links.length}</span>
</div>
<div className="hud-row">
<span className="hud-label">Storage:</span>
<span className="hud-value status-glow">{dbStatus === 'online' ? 'LTM (Neo4j)' : 'STM (Cache)'}</span>
</div>
</div>
{dbStatus === 'online' && stats.top_entities && stats.top_entities.length > 0 && (
<div className="hud-panel top-entities">
<div className="hud-header">
<span className="material-icons">star</span>
<span>Primary Hubs</span>
</div>
<div className="hud-entity-list">
{stats.top_entities.map((ent, idx) => (
<div key={idx} className="hud-entity-row">
<span className="entity-rank">#{idx+1}</span>
<span className="entity-name">{ent.entity}</span>
<span className="entity-connections">{ent.connections} rx</span>
</div>
))}
</div>
</div>
)}
</div>
<ForceGraph3D
ref={fgRef}
graphData={graphData}
width={dimensions.width}
height={dimensions.height}
backgroundColor="rgba(0, 0, 0, 0)" // Glassmorphic translucent rendering
// Render gorgeous glowing 3D spheres with emissive materials (100% stable!)
nodeThreeObject={node => {
const size = Math.max(3.2, Math.min(node.connections * 1.6, 9.5)); // Perfectly scaled spheres
const geom = new THREE.SphereGeometry(size, 24, 24);
const mat = new THREE.MeshLambertMaterial({
color: getNodeColor(node),
transparent: true,
opacity: 0.95,
emissive: getNodeColor(node),
emissiveIntensity: 0.45
});
return new THREE.Mesh(geom, mat);
}}
// Outlined holographic label rendered natively floating beside cursor on hover (100% stable!)
nodeLabel={node => `
<span style="color: ${getNodeColor(node)}; font-size: 12px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;">
${node.label || node.id}
</span>
`}
// Outlined relationship tag rendered on link hover (100% stable!)
linkLabel={link => `
<span style="color: #ff6b35; font-size: 10px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;">
${link.label || 'RELATED_TO'}
</span>
`}
nodeRelSize={3}
linkColor={() => 'rgba(255, 107, 53, 0.25)'} // Soft, clean axon link fibers
linkWidth={1.8} // Sleek link line thickness
// Glowing Thought Flows (sliding directional particles along axons)
linkDirectionalParticles={3}
linkDirectionalParticleSpeed={0.006}
linkDirectionalParticleWidth={1.5}
linkDirectionalParticleColor={() => '#ff6b35'}
showNavInfo={false}
enablePointerInteraction={true}
enableNodeDrag={true}
/>
{/* Legend removed per user request */}
{loading && (
<div className="graph-loading">
<span className="material-icons pulse">refresh</span>
<span>Synchronizing cognitive mesh...</span>
</div>
)}
</div>
</div>
);
}
export default KnowledgeGraph;