import { useState, useEffect, useCallback, useMemo } from 'react'; import { apiFetch } from '../api'; import './MemoryExplorer.css'; function MemoryExplorer() { const [search, setSearch] = useState(''); const [memories, setMemories] = useState([]); const [graphData, setGraphData] = useState({ nodes: [], edges: [] }); const [selectedId, setSelectedId] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSearching, setIsSearching] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [activeFilter, setActiveFilter] = useState('all'); const [insightEntity, setInsightEntity] = useState(null); const fetchData = useCallback(async (query = '') => { const isSearch = query.trim().length > 0; if (isSearch) setIsSearching(true); else setIsLoading(true); try { const memoryUrl = isSearch ? `/api/v1/memory/search?q=${encodeURIComponent(query)}` : '/api/v1/memory/sensory'; const [memRes, graphRes] = await Promise.all([ apiFetch(memoryUrl), apiFetch('/api/v1/graph') ]); if (memRes.ok) { const data = await memRes.json(); const results = data.memories || []; setMemories(results); if (results.length > 0) { if (!selectedId || !results.find(m => m.id === selectedId)) { setSelectedId(results[0].id); } } else { setSelectedId(null); } } if (graphRes.ok) { const gData = await graphRes.json(); setGraphData(gData); } } catch (error) { console.error('Neural synchronization failure', error); } finally { setIsLoading(false); setIsSearching(false); } }, [selectedId]); const handlePurge = async (id) => { if (!window.confirm("Are you sure you want to purge this memory chunk from the neural cortex? This action is irreversible.")) return; setIsDeleting(true); try { const res = await apiFetch(`/api/v1/memory/${id}`, { method: 'DELETE' }); if (res.ok) { setMemories(prev => prev.filter(m => m.id !== id)); setSelectedId(null); fetchData(search); } } catch (error) { console.error('Purge failed', error); } finally { setIsDeleting(false); } }; useEffect(() => { fetchData(); }, []); useEffect(() => { const timer = setTimeout(() => { fetchData(search); }, 500); return () => clearTimeout(timer); }, [search]); const filteredMemories = useMemo(() => { if (activeFilter === 'all') return memories; return memories.filter(m => { const type = m.metadata?.type; if (activeFilter === 'episodic') return type === 'conversation' || type === 'chat_exchange'; if (activeFilter === 'semantic') return type === 'concept' || type === 'note'; if (activeFilter === 'sleep') return type === 'sleep_summary'; return true; }); }, [memories, activeFilter]); const selectedMemory = useMemo(() => memories.find(m => m.id === selectedId) || memories[0], [memories, selectedId] ); const linkedEntities = useMemo(() => { if (!selectedMemory || !graphData.nodes) return []; const content = selectedMemory.content.toLowerCase(); return graphData.nodes.filter(node => content.includes(node.id.toLowerCase()) || (node.label && content.includes(node.label.toLowerCase())) ); }, [selectedMemory, graphData]); const getTitle = (content) => { if (!content) return 'Untitled Memory Chunks'; const lines = content.split('\n'); const firstLine = lines[0].trim(); if (firstLine.length > 40) return firstLine.substring(0, 37) + '...'; return firstLine || 'Untitled Memory Chunks'; }; const getMemoryTheme = (type) => { switch (type) { case 'conversation': return { color: '#3b82f6', label: 'Episodic' }; case 'chat_exchange': return { color: '#8b5cf6', label: 'Social' }; case 'concept': return { color: '#ff6b35', label: 'Semantic' }; case 'note': return { color: '#10b981', label: 'Explicit' }; case 'sleep_summary': return { color: '#f59e0b', label: 'Consolidated' }; default: return { color: '#94a3b8', label: 'Raw Sensory' }; } }; return (
{/* ── SIDEBAR: Memory Feed ── */}
{isSearching ? 'sync' : 'search'} setSearch(e.target.value)} placeholder="Query sensory cortex..." />
{['all', 'episodic', 'semantic', 'sleep'].map(f => ( ))}
dns {filteredMemories.length} Active Nodes
{isLoading ? (
settings_input_component

Calibrating neural paths...

) : filteredMemories.length === 0 ? (
folder_open

No memories found

Query the sensory cortex or select another layer above.

) : ( filteredMemories.map((memory) => { const theme = getMemoryTheme(memory.metadata?.type); return ( ); }) )}
{/* ── MAIN: Diagnostic Detail ── */}
{selectedMemory ? (
Layer: {getMemoryTheme(selectedMemory.metadata?.type).label}
UUID: {selectedMemory.id}

{getTitle(selectedMemory.content)}

event Encoded: {selectedMemory.metadata?.timestamp ? new Date(selectedMemory.metadata.timestamp).toLocaleString() : 'System Boot Sequence'}

{selectedMemory.content}
{/* Instrument Metrics */}

Retrieval Salience

{(selectedMemory.similarity || 0.85).toFixed(4)}

Graph Associations

{linkedEntities.length} Links

Entropy Score

0.4281
{/* Knowledge Links */}
account_tree Knowledge Graph Intersections (Click for Insights)
{linkedEntities.map(node => ( ))} {linkedEntities.length === 0 && (

No direct semantic associations detected in current graph state.

)}
{/* Raw Metadata */}
terminal Raw Metadata Explorer
{Object.entries(selectedMemory.metadata || {}).map(([key, val]) => (
{key} {String(val)}
))}
{/* Neural Insight Overlay */} {insightEntity && (
psychology

Neural Insight: {insightEntity.id}

{insightEntity.connections * 12.5} nm
{insightEntity.connections} Nodes

This entity is a core semantic node within your current knowledge base. It facilitates retrieval of related episodic contexts and strengthens the reasoning cortex.

)}
) : (
sensors

No Active Focus

Select a sensory chunk from the feed to begin deep-layer diagnostic analysis.

)}
); } export default MemoryExplorer;