// Soma Cognitive Console: Refined Responsive Shell
import { useEffect, useState, useCallback } from 'react';
import ChatPanel from './components/ChatPanel';
import CognitiveBrainImageScene from './components/CognitiveBrainImageScene';
import CognitiveTimeline from './components/CognitiveTimeline';
import MemoryExplorer from './components/MemoryExplorer';
import KnowledgeGraph from './components/KnowledgeGraph';
import CognitiveDashboard from './components/CognitiveDashboard';
import KnowledgeInput from './components/KnowledgeInput';
import { SleepProgress } from './components/DreamSequence';
import AuthScreen from './components/AuthScreen';
import VisitorAnalytics from './components/VisitorAnalytics';
import { apiFetch } from './api';
import './App.css';
const NAV_ITEMS = [
{ id: 'console', label: 'Console', icon: 'chat_bubble_outline' },
{ id: 'memory', label: 'Memory', icon: 'layers' },
{ id: 'graph', label: 'Graph', icon: 'share' },
{ id: 'knowledge', label: 'Inscription', icon: 'auto_awesome' },
{ id: 'sleep', label: 'Sleep', icon: 'hotel' },
];
const COGNITIVE_PHASES = {
PERCEPTION: 'perception',
ATTENTION: 'attention',
RECALL: 'recall',
REASONING: 'reasoning',
RESPONDING: 'responding',
IDLE: 'idle',
LISTENING: 'listening'
};
function App() {
const [activePage, setActivePage] = useState('console');
const [username, setUsername] = useState(localStorage.getItem('soma_username'));
const [messages, setMessages] = useState([]);
const [vitals, setVitals] = useState(null);
const [trace, setTrace] = useState([]);
const [cognitiveState, setCognitiveState] = useState(COGNITIVE_PHASES.IDLE);
const [refreshTick, setRefreshTick] = useState(0);
const [knowledgeStatus, setKnowledgeStatus] = useState('');
const [sleepPhaseIndex, setSleepPhaseIndex] = useState(0);
const [sleepSummary, setSleepSummary] = useState(null);
const [showStatus, setShowStatus] = useState(false);
const [darkMode, setDarkMode] = useState(localStorage.getItem('soma_dark') === 'true');
const [showAnalyticsModal, setShowAnalyticsModal] = useState(false);
useEffect(() => {
if (darkMode) {
document.body.classList.add('dark-theme');
localStorage.setItem('soma_dark', 'true');
} else {
document.body.classList.remove('dark-theme');
localStorage.setItem('soma_dark', 'false');
}
}, [darkMode]);
useEffect(() => {
if (!username) return;
fetchHistory();
fetchVitals();
const interval = setInterval(fetchVitals, 10000);
return () => clearInterval(interval);
}, [username]);
useEffect(() => {
if (!username) return;
// Verify if session hit is already registered to protect Upstash limits
if (sessionStorage.getItem('soma_hit_registered') === 'true') return;
// Get or create unique persistent visitor ID
let visitorId = localStorage.getItem('soma_visitor_id');
if (!visitorId) {
visitorId = 'visitor_' + Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
localStorage.setItem('soma_visitor_id', visitorId);
}
const registerHit = async () => {
try {
const res = await apiFetch('/api/v1/analytics/hit', {
method: 'POST',
body: JSON.stringify({ visitor_id: visitorId })
});
if (res.ok) {
sessionStorage.setItem('soma_hit_registered', 'true');
}
} catch (error) {
console.error("Failed to register visitor telemetry hit:", error);
}
};
registerHit();
}, [username]);
const fetchHistory = async () => {
try {
const res = await apiFetch('/api/v1/history');
if (res.ok) {
const data = await res.json();
setMessages(data.messages || []);
}
} catch (error) { console.error('History fetch failed', error); }
};
const fetchVitals = async () => {
try {
const res = await apiFetch('/api/v1/brain/vitals');
if (res.ok) {
const data = await res.json();
setVitals(data);
}
} catch (error) { console.error('Vitals fetch failed', error); }
};
const handleSendMessage = async (text) => {
if (!text.trim()) return;
const timestamp = new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
setMessages(prev => [...prev, { role: 'user', content: text, timestamp }]);
setTrace([]);
// Remove artificial frontend phases since backend now streams them.
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 15000); // 15s timeout
const token = localStorage.getItem('soma_token');
const response = await fetch('/api/v1/query/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {})
},
body: JSON.stringify({ text }),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok || !response.body) throw new Error('Query failed');
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const dataStr = line.slice(6).trim();
if (!dataStr) continue;
try {
const data = JSON.parse(dataStr);
if (data.phase) {
// Live trace and state updates from backend
setCognitiveState(data.phase);
if (data.message) {
setTrace(prev => [{ time: new Date().toLocaleTimeString([], { hour12: false }), ...data }, ...prev]);
}
} else if (data.response) {
// Phase E: Response Generation
setCognitiveState(COGNITIVE_PHASES.RESPONDING);
setMessages(prev => [...prev, {
role: 'soma',
content: data.response,
timestamp: new Date().toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
}]);
fetchVitals();
setRefreshTick(prev => prev + 1);
}
} catch (e) {
console.error('JSON parse error', e);
}
}
}
} catch (error) {
console.error('Query failed', error);
setTrace(prev => [{ phase: 'Error', content: 'Neural connection interrupted.', time: 'ERROR' }, ...prev]);
} finally {
// Ensure we always return to idle
setCognitiveState(COGNITIVE_PHASES.IDLE);
}
};
const handleKnowledgeSubmit = async (text) => {
setKnowledgeStatus('Integrating knowledge into neural layers...');
try {
const res = await apiFetch('/api/v1/ingest', {
method: 'POST',
body: JSON.stringify({ text })
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || 'Ingestion failed');
setKnowledgeStatus(data.message || 'Knowledge stored successfully.');
setTimeout(() => setKnowledgeStatus(''), 10000);
fetchVitals();
} catch (error) {
setKnowledgeStatus('Neural integration failed: ' + error.message);
}
};
const handleSleepCycle = async () => {
setCognitiveState('consolidating');
setActivePage('sleep');
setSleepPhaseIndex(0);
setSleepSummary(null);
// Animate checklist steps gracefully during the pending API request
const stepInterval = setInterval(() => {
setSleepPhaseIndex(prev => {
if (prev < 2) return prev + 1;
return prev;
});
}, 1000);
try {
const res = await apiFetch('/api/v1/sleep', { method: 'POST' });
const data = await res.json();
clearInterval(stepInterval);
setSleepPhaseIndex(2); // Mark all checklists as complete
setSleepSummary({
linked: data.graph_relations_extracted || 0,
consolidated: data.summaries_created || 0,
pruned: data.messages_pruned || 0
});
} catch (error) {
clearInterval(stepInterval);
console.error("Sleep cycle failed:", error);
setSleepSummary({ linked: 0, consolidated: 0, pruned: 0 });
} finally {
setCognitiveState(COGNITIVE_PHASES.IDLE);
fetchVitals(); // Instantly update vitals to reflect pruned/cleared working queue
}
};
const handleLogout = () => {
localStorage.clear();
setUsername(null);
setMessages([]);
setActivePage('console');
};
if (!username) return