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 (
Calibrating neural paths...
No memories found
Query the sensory cortex or select another layer above.
event Encoded: {selectedMemory.metadata?.timestamp ? new Date(selectedMemory.metadata.timestamp).toLocaleString() : 'System Boot Sequence'}
No direct semantic associations detected in current graph state.
)}This entity is a core semantic node within your current knowledge base. It facilitates retrieval of related episodic contexts and strengthens the reasoning cortex.
Select a sensory chunk from the feed to begin deep-layer diagnostic analysis.