import React, { useState, useEffect, useRef, createContext, useContext } from 'react'; import { Brain, Zap, Search, Shield, Clock, Filter, Play, Menu, X, Check, ChevronRight, Coffee, BookOpen, Video, HelpCircle, Mail, Lock, Cpu, Wifi, Activity, Mic, MicOff, AlertTriangle, Radio, CreditCard, BarChart2, TrendingUp, User, LogOut, Sparkles, FileText, Users, Send } from 'lucide-react'; // ============================================ // CONFIGURAZIONE SISTEMA // ============================================ const SYSTEM_CONFIG = { OPENROUTER_KEY: import.meta.env.VITE_OPENROUTER_API_KEY || "", YOUTUBE_KEY: import.meta.env.VITE_YOUTUBE_API_KEY || "", USE_AI: true, FALLBACK_VIDEO_ID: "Y9EjnBmO2Jw", SYSTEM_FAILURE: false, // Flag for error testing SUPABASE_URL: import.meta.env.VITE_SUPABASE_URL, STRIPE_KEY: import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY }; import { supabase } from './lib/supabase'; import { loadStripe } from '@stripe/stripe-js'; import { Elements, PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js'; const stripePromise = SYSTEM_CONFIG.STRIPE_KEY ? loadStripe(SYSTEM_CONFIG.STRIPE_KEY) : null; class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null, errorInfo: null }; } static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, errorInfo) { console.error("Uncaught error:", error, errorInfo); this.setState({ errorInfo }); } render() { if (this.state.hasError) { return (

SYSTEM FAILURE

{this.state.error && this.state.error.toString()}
{this.state.errorInfo && this.state.errorInfo.componentStack}
); } return this.props.children; } } // ============================================ // USER CONTEXT (Gestione Protocolli con localStorage) // ============================================ const UserContext = createContext(); const STORAGE_KEY = 'synapse_user_data'; const getStoredData = () => { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored) return JSON.parse(stored); } catch (e) { } return null; }; const UserProvider = ({ children }) => { const stored = getStoredData(); const [protocol, setProtocol] = useState(stored?.protocol || 'GUEST'); const [credits, setCredits] = useState(stored?.credits ?? 5); const [showPayment, setShowPayment] = useState(false); const [currentView, setCurrentView] = useState(stored?.protocol === 'PRO' ? 'PRO_DASHBOARD' : 'LANDING'); const [userData, setUserData] = useState(stored?.userData || { name: 'Ospite', xp: 0, hours: 0, streak: 0, email: '' }); // Persist to localStorage useEffect(() => { localStorage.setItem(STORAGE_KEY, JSON.stringify({ protocol, credits, userData })); }, [protocol, credits, userData]); // Daily credit reset for Core useEffect(() => { const lastReset = localStorage.getItem('synapse_credit_reset'); const today = new Date().toDateString(); if (protocol === 'CORE' && lastReset !== today) { setCredits(5); localStorage.setItem('synapse_credit_reset', today); } }, [protocol]); const [session, setSession] = useState(null); // Supabase Auth Sync useEffect(() => { if (!supabase) return; const checkUser = (session) => { // Check for Dev Bypass const isBypass = localStorage.getItem('synapse_dev_bypass') === 'true'; if (isBypass) { setProtocol('PRO'); setUserData(p => ({ ...p, name: 'Sviluppatore (Force)', email: 'dev@synapse.os' })); return; } if (session) { if (session.user.email === 'chridipi04@gmail.com') { setProtocol('PRO'); setUserData(p => ({ ...p, name: 'Sviluppatore', email: session.user.email })); } else { setProtocol('CORE'); setUserData(p => ({ ...p, name: session.user.email.split('@')[0], email: session.user.email })); } } else { setProtocol('GUEST'); } }; supabase.auth.getSession().then(({ data: { session } }) => { setSession(session); checkUser(session); }); const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { setSession(session); checkUser(session); }); return () => subscription.unsubscribe(); }, []); const upgradeToCore = () => { // Now handled by Auth setShowAuth(true); // Trigger Auth Modal }; const upgradeToPro = () => { if (!session) { setShowAuth(true); return; } // In real app, this waits for Stripe webhook. Here we simulate optimistic update after successful payment logic. setProtocol('PRO'); setUserData(prev => ({ ...prev, name: 'Scholar Pro', hours: prev.hours || 0 })); setShowPayment(false); setCurrentView('PRO_DASHBOARD'); }; const logout = async () => { if (supabase) await supabase.auth.signOut(); localStorage.removeItem(STORAGE_KEY); localStorage.removeItem('synapse_dev_bypass'); // Clear the bypass flag setProtocol('GUEST'); setSession(null); setCurrentView('LANDING'); window.location.reload(); // Force reload to ensure clean state }; const [showAuth, setShowAuth] = useState(false); const addXP = (amount) => setUserData(prev => ({ ...prev, xp: (prev.xp || 0) + amount })); const addHours = (amount) => setUserData(prev => ({ ...prev, hours: (prev.hours || 0) + amount })); return ( {children} ); }; const useUser = () => useContext(UserContext); // ============================================ // VFX LAYER // ============================================ const VFXLayer = () => ( <>
); // ============================================ // COMPONENTI UI BASE // ============================================ const GlitchText = ({ children, className = '' }) => ( {children} ); const HUDCard = ({ children, className = '', hoverGlow = true, locked = false, style = {} }) => (
{locked && (
Accesso Limitato RICHIESTO UPGRADE PROTOCOLLO
)} {children}
); const MagneticButton = ({ children, className = '', onClick, disabled, ...props }) => { const buttonRef = useRef(null); const [offset, setOffset] = useState({ x: 0, y: 0 }); const handleMouseMove = (e) => { if (!buttonRef.current || disabled) return; const rect = buttonRef.current.getBoundingClientRect(); setOffset({ x: (e.clientX - rect.left - rect.width / 2) * 0.2, y: (e.clientY - rect.top - rect.height / 2) * 0.2 }); }; return ( ); }; // ============================================ // NEURAL VOID (Background) // ============================================ const NeuralVoid = () => (
); // ============================================ // HEADER (Landing) // ============================================ const Header = () => { const { setShowAuth, protocol, userData, logout, setCurrentView } = useUser(); return (
SYNAPSE
{protocol === 'GUEST' ? ( ) : (
BENTORNATO
{userData?.name || 'Utente'}
{protocol === 'PRO' && ( )}
)}
); }; // ============================================ // NEURAL CANVAS // ============================================ const NeuralCanvas = () => { const canvasRef = useRef(null); const mouseRef = useRef({ x: 0, y: 0 }); const nodesRef = useRef([]); const animationRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas) return; const ctx = canvas.getContext('2d'); let width = window.innerWidth, height = window.innerHeight; const resize = () => { width = window.innerWidth; height = window.innerHeight; canvas.width = width; canvas.height = height; initNodes(); }; const initNodes = () => { const count = Math.floor((width * height) / 12000); nodesRef.current = Array.from({ length: count }, () => ({ x: Math.random() * width, y: Math.random() * height, baseX: Math.random() * width, baseY: Math.random() * height, vx: (Math.random() - 0.5) * 0.4, vy: (Math.random() - 0.5) * 0.4, radius: Math.random() * 2 + 1.5, opacity: 0.3 + Math.random() * 0.5, pulsePhase: Math.random() * Math.PI * 2, pulseSpeed: 0.01 + Math.random() * 0.02, })); }; const handleMouseMove = (e) => { mouseRef.current = { x: e.clientX, y: e.clientY }; }; const animate = () => { ctx.fillStyle = '#020617'; ctx.fillRect(0, 0, width, height); const nodes = nodesRef.current, mouse = mouseRef.current; nodes.forEach((node, i) => { node.pulsePhase += node.pulseSpeed; node.opacity = 0.3 + (Math.sin(node.pulsePhase) + 1) * 0.25; const dx = mouse.x - node.x, dy = mouse.y - node.y, dist = Math.sqrt(dx * dx + dy * dy); if (dist < 150 && dist > 0) { const force = (150 - dist) / 150; node.vx += (dx / dist) * force * 0.08; node.vy += (dy / dist) * force * 0.08; } node.vx += (node.baseX - node.x) * 0.002; node.vy += (node.baseY - node.y) * 0.002; node.x += node.vx; node.y += node.vy; node.vx *= 0.96; node.vy *= 0.96; if (node.x < 0 || node.x > width) node.vx *= -0.5; if (node.y < 0 || node.y > height) node.vy *= -0.5; node.x = Math.max(0, Math.min(width, node.x)); node.y = Math.max(0, Math.min(height, node.y)); for (let j = i + 1; j < nodes.length; j++) { const other = nodes[j], distance = Math.sqrt((node.x - other.x) ** 2 + (node.y - other.y) ** 2); if (distance < 140) { const nearMouse = Math.sqrt((mouse.x - node.x) ** 2 + (mouse.y - node.y) ** 2) < 150 || Math.sqrt((mouse.x - other.x) ** 2 + (mouse.y - other.y) ** 2) < 150; ctx.beginPath(); ctx.moveTo(node.x, node.y); ctx.lineTo(other.x, other.y); ctx.strokeStyle = nearMouse ? '#06b6d4' : '#8b5cf6'; ctx.lineWidth = nearMouse ? 2 : 1; ctx.globalAlpha = (1 - distance / 140) * (nearMouse ? 0.8 : 0.3); ctx.stroke(); } } ctx.globalAlpha = node.opacity; ctx.shadowBlur = 15; ctx.shadowColor = '#8b5cf6'; ctx.beginPath(); ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2); ctx.fillStyle = '#8b5cf6'; ctx.fill(); ctx.shadowBlur = 0; }); ctx.globalAlpha = 1; animationRef.current = requestAnimationFrame(animate); }; resize(); window.addEventListener('resize', resize); window.addEventListener('mousemove', handleMouseMove); animate(); return () => { window.removeEventListener('resize', resize); window.removeEventListener('mousemove', handleMouseMove); if (animationRef.current) cancelAnimationFrame(animationRef.current); }; }, []); return ; }; // ============================================ // LIVE TICKER // ============================================ const LiveTicker = () => { const messages = [ '[LIVE] User_X99: Sessione "Fisica" completata (+200XP)', '[LIVE] Sistema: Database aggiornato con 400 nuovi video', '[LIVE] User_Anna: Ha sbloccato "Memory Lock Pro"', '[LIVE] User_Marco: Quiz Storia completato (98%)', '[LIVE] User_Sara: 5 ore di Deep Work questa settimana', ]; return (
{[...messages, ...messages].map((msg, i) => ( {msg} ))}
); }; // ============================================ // SYSTEM HEADER // ============================================ const SystemHeader = () => { const { protocol, userData, setShowPayment, currentView, setCurrentView, logout } = useUser(); const [isScrolled, setIsScrolled] = useState(false); const [latency, setLatency] = useState(12); useEffect(() => { const handleScroll = () => setIsScrolled(window.scrollY > 50); window.addEventListener('scroll', handleScroll); const interval = setInterval(() => setLatency(Math.floor(8 + Math.random() * 15)), 2000); return () => { window.removeEventListener('scroll', handleScroll); clearInterval(interval); }; }, []); return (
SYNAPSE [OS]
RETE: SICURA
LATENZA: {latency}ms
{protocol === 'GUEST' ? ( document.getElementById('pricing')?.scrollIntoView({ behavior: 'smooth' })} className="px-4 py-2 bg-[#8b5cf6] text-white font-bold text-xs rounded hover:bg-[#7c3aed]"> ACCEDI ) : (
{protocol === 'PRO' && ( <> PRO )} {protocol === 'CORE' && CORE}
{userData.name}
{protocol === 'CORE' && }
)}
); }; // ============================================ // PAYMENT MODAL // ============================================ // ============================================ // AUTH MODAL (Login/Signup) // ============================================ const AuthModal = () => { const { showAuth, setShowAuth, setProtocol, setUserData } = useUser(); const [isLogin, setIsLogin] = useState(false); // Default to Signup as requested "Iscriviti la prima volta" const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); if (!showAuth) return null; const handleAuth = async (e) => { e.preventDefault(); setLoading(true); setError(''); try { if (!supabase) throw new Error("Supabase non configurato (Mancano API Keys)"); if (isLogin) { const { data, error } = await supabase.auth.signInWithPassword({ email, password }); if (error) throw error; console.log("Login Success", data); } else { const { data, error } = await supabase.auth.signUp({ email, password }); if (error) throw error; if (data?.user && !data?.session) { setError("Controlla la tua email per confermare l'iscrizione!"); return; } console.log("Signup Success", data); } setShowAuth(false); } catch (err) { console.error("Auth Error", err); setError(err.message); } finally { setLoading(false); } }; return (
setShowAuth(false)} />

{isLogin ? 'ACCESSO NEURALE' : 'CREA IDENTITÀ'}

{isLogin ? 'Bentornato nel sistema, Operatore.' : 'Registrati per avviare il protocollo Synapse.'}

{error &&
{error}
}
setEmail(e.target.value)} className="w-full bg-slate-800 border border-slate-700 rounded p-3 text-white focus:border-[#06b6d4] outline-none" required />
setPassword(e.target.value)} className="w-full bg-slate-800 border border-slate-700 rounded p-3 text-white focus:border-[#06b6d4] outline-none" required />
); }; // ============================================ // STRIPE CHECKOUT UTILS // ============================================ const StripeCheckout = ({ clientSecret }) => { const stripe = useStripe(); const elements = useElements(); const { upgradeToPro } = useUser(); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const handleSubmit = async (event) => { event.preventDefault(); if (!stripe || !elements) return; setLoading(true); const { error: submitError } = await elements.submit(); if (submitError) { setError(submitError.message); setLoading(false); return; } // Confirm the payment const { error } = await stripe.confirmPayment({ elements, clientSecret, confirmParams: { return_url: 'http://localhost:5173', // Redirects here after payment }, redirect: 'if_required' }); if (error) { setError(error.message); setLoading(false); } else { // Payment succeeded upgradeToPro(); } }; return (
{error &&
{error}
}
Totale€9.99
); }; // ============================================ // PAYMENT MODAL (Stripe Aware) // ============================================ const PaymentModal = () => { const { showPayment, setShowPayment, upgradeToPro } = useUser(); const [clientSecret, setClientSecret] = useState(null); useEffect(() => { if (showPayment && SYSTEM_CONFIG.STRIPE_KEY) { // Fetch fetch client secret from our local node server console.log("Fetching Payment Intent..."); fetch('http://localhost:4242/create-payment-intent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, }) .then(res => { if (!res.ok) throw new Error("Server Error"); return res.json(); }) .then(data => { console.log("Secret received"); setClientSecret(data.clientSecret); }) .catch(err => { console.error("Payment Fetch Error", err); // Fallback for demo if server is dead // alert("Server Pagamenti Offline. Controlla che 'node server.js' sia attivo."); }); } }, [showPayment]); if (!showPayment) return null; return (
setShowPayment(false)} />

UPGRADE SCHOLAR PRO

{stripePromise && clientSecret ? ( ) : (

Inizializzazione Stripe Sicuro...

)}
); }; // ============================================ // TYPEWRITER HOOK // ============================================ const useTypewriter = (lines, speed = 40, pauseBetween = 500) => { const [currentLine, setCurrentLine] = useState(0); const [currentChar, setCurrentChar] = useState(0); const [displayLines, setDisplayLines] = useState([]); const [isComplete, setIsComplete] = useState(false); useEffect(() => { if (currentLine >= lines.length) { setIsComplete(true); return; } const line = lines[currentLine]; if (currentChar < line.length) { const timeout = setTimeout(() => { setDisplayLines((prev) => { const newLines = [...prev]; newLines[currentLine] = line.slice(0, currentChar + 1); return newLines; }); setCurrentChar(currentChar + 1); }, speed); return () => clearTimeout(timeout); } else { const timeout = setTimeout(() => { setCurrentLine(currentLine + 1); setCurrentChar(0); }, pauseBetween); return () => clearTimeout(timeout); } }, [currentLine, currentChar, lines, speed, pauseBetween]); return { displayLines, isComplete }; }; // ============================================ // HERO UNIT // ============================================ const HeroUnit = () => { const { displayLines, isComplete } = useTypewriter([ '> Inizializzazione Neural Uplink...', '> Ottimizzazione Protocolli di Memoria...', '> Sistema Pronto.', ], 30, 400); return (

POTENZIA LA TUA MENTE.

{displayLines.map((line, i) => (
{line}{i === displayLines.length - 1 && !isComplete && }
))} {displayLines.length === 0 && }
document.getElementById('simulator')?.scrollIntoView({ behavior: 'smooth' })} className="group px-10 py-5 bg-gradient-to-r from-[#8b5cf6] to-[#7c3aed] text-white font-bold text-lg rounded-xl hover:scale-105 shadow-[0_0_40px_rgba(139,92,246,0.5)] relative overflow-hidden"> AVVIA
); }; // ============================================ // VIDEO DATABASE & SIMULATION LOGIC // ============================================ // ID Verificati & Quiz & Riassunti (Curati da Gemini 2 Flash) const VIDEO_DATABASE = { 'fisica': { id: 'Y9EjnBmO2Jw', title: 'I Principi della Dinamica (Hub Scuola)', summary: "STUDIO: La dinamica è la parte della fisica che studia come si muovono i corpi per effetto delle forze. \n1. Principio d'Inerzia: Se nessuna forza agisce su un corpo, esso mantiene il suo stato (quiete o moto rettilineo uniforme). \n2. Legge Fondamentale (F=ma): La forza è il prodotto tra massa e accelerazione. \n3. Azione e Reazione: A ogni forza corrisponde una forza uguale e contraria.", quiz: { question: "Quale principio afferma che F = m * a?", options: [{ text: "Primo Principio", correct: false }, { text: "Secondo Principio", correct: true }, { text: "Terzo Principio", correct: false }], hint: "È la legge fondamentale della dinamica che collega causa ed effetto." } }, 'napoleone': { id: '2U_YdZD5kkM', title: 'Napoleone Bonaparte - Sintesi Completa', summary: "BIOGRAFIA: Generale e Imperatore francese (1769-1821). \nASCESA: Sfruttò il caos post-rivoluzione. Famoso per le campagne d'Italia e d'Egitto. Autoproclamato Imperatore nel 1804. \nRIFORME: Introdusse il Codice Civile (basi diritto moderno). \nCADUTA: Disastrosa campagna di Russia (1812), sconfitto a Lipsia e Waterloo (1815). Esiliato a Sant'Elena.", quiz: { question: "In quale isola morì Napoleone in esilio?", options: [{ text: "Isola d'Elba", correct: false }, { text: "Sant'Elena", correct: true }, { text: "Corsica", correct: false }], hint: "Un'isola remota nell'Oceano Atlantico meridionale." } }, 'storia': { id: 'd_kS3x0lJ4k', title: 'La Prima Guerra Mondiale (In 5 minuti)', summary: "GRANDE GUERRA (1914-1918): Scatenata dall'attentato di Sarajevo. \nSCHIERAMENTI: Triplice Intesa (Francia, UK, Russia, poi Italia/USA) vs Imperi Centrali (Austria, Germania). \nCARATTERISTICHE: Guerra di trincea, logoramento, nuove armi (gas, aerei, carri). \nESITO: Crollo di 4 imperi, nascita di nuovi stati, riassetto dell'Europa con Versailles.", quiz: { question: "Quale evento fece scoppiare la guerra?", options: [{ text: "Invasione della Polonia", correct: false }, { text: "Attentato di Sarajevo", correct: true }, { text: "Presa della Bastiglia", correct: false }], hint: "L'assassinio dell'Arciduca Francesco Ferdinando." } }, 'chimica': { id: '?listType=search&list=Tavola+Periodica+Spiegazione+Semplice', title: 'La Tavola Periodica degli Elementi', summary: "STRUTTURA: Organizza gli elementi chimici ordinati per numero atomico (Z). \nGRUPPI E PERIODI: Le colonne (gruppi) hanno proprietà simili; le righe (periodi) indicano il livello energetico. \nCLASSIFICAZIONE: Metalli (sinistra), Non metalli (destra), Gas Nobili (ultima colonna, stabili). Fondamentale per prevedere le reazioni chimiche.", quiz: { question: "Come sono ordinati gli elementi nella tavola?", options: [{ text: "Per data di scoperta", correct: false }, { text: "Per numero atomico crescente", correct: true }, { text: "Alfabeticamente", correct: false }], hint: "Il numero di protoni nel nucleo decide la posizione." } }, 'matematica': { id: '?listType=search&list=Equazioni+Primo+Grado+Spiegazione', title: 'Equazioni Lineari (Algebra)', summary: "CONCETTO: Uguaglianza tra due espressioni verificata solo per certi valori (soluzioni). \nRISOLUZIONE: L'obiettivo è isolare la 'x'. \nPRINCIPI: \n1. Sommando/sottraendo la stessa quantità a entrambi i membri, il risultato non cambia. \n2. Moltiplicando/dividendo entrambi i membri per uno stesso numero (diverso da 0), l'equazione resta equivalente.", quiz: { question: "Qual è il primo passaggio per risolvere 2x + 5 = 15?", options: [{ text: "Dividere tutto per 2", correct: false }, { text: "Sottrarre 5 da entrambi i lati", correct: true }, { text: "Moltiplicare per x", correct: false }], hint: "Devi isolare il termine con la x spostando i numeri." } }, 'italiano': { id: 'fESdidM5j7s', title: 'La Divina Commedia in 10 minuti', summary: "OPERA: Poema allegorico in terzine incatenate. \nVIAGGIO: Dante attraversa i tre regni ultraterreni. \nINFERNO: Voragine a imbuto, pena del contrappasso. \nPURGATORIO: Montagna dove le anime espiano i peccati. \nPARADISO: Cieli concentrici di pura luce e beatitudine. \nGUIDE: Virgilio (Ragione), Beatrice (Teologia/Grazia).", quiz: { question: "Chi guida Dante attraverso l'Inferno?", options: [{ text: "Beatrice", correct: false }, { text: "Virgilio", correct: true }, { text: "San Pietro", correct: false }], hint: "Il sommo poeta latino autore dell'Eneide." } }, 'inglese': { id: 'M2K-kM2i_tQ', title: 'Inglese: Basi Fondamentali', summary: "GRAMMATICA BASE: \n1. Verbo To Be (Essere): I am, You are, He is. \n2. Ordine parole: Subject + Verb + Object (SVO). \n3. Present Simple: Per abitudini e verità generali (Add 's' for he/she/it). \nCONSIGLIO: La pratica dell'ascolto (listening) è cruciale quanto la grammatica.", quiz: { question: "Qual è la forma corretta?", options: [{ text: "He go to school", correct: false }, { text: "He goes to school", correct: true }, { text: "He going to school", correct: false }], hint: "Terza persona singolare richiede la 's' o 'es'." } }, }; const getTopicVideo = (topic) => { const query = topic.toLowerCase().trim(); const searchKey = Object.keys(VIDEO_DATABASE).find(key => query.includes(key)); if (searchKey) return VIDEO_DATABASE[searchKey]; // FALLBACK INTELLIGENTE: YouTube Search Embed // Genera una ricerca dinamica se non abbiamo un ID specifico return { id: `?listType=search&list=${encodeURIComponent(topic + ' spiegazione scuola')}`, title: `Ricerca approfondita: ${topic}`, summary: `Generazione sintesi automatica per: ${topic}. L'argomento richiede un'analisi approfondita delle fonti video correlate. Consulta il video per i dettagli specifici e prendi appunti sui concetti chiave.`, quiz: { question: `Qual è il concetto principale riguardo "${topic}"?`, options: [ { text: "Concetto A (Vedi Video)", correct: true }, { text: "Concetto B", correct: false }, { text: "Concetto C", correct: false } ], hint: "Guarda i primi 2 minuti del video per la risposta." } }; }; const getSimulatedResult = (topic) => { try { const video = getTopicVideo(topic); return { videoId: video.id, summary: video.summary || `Riassunto generato per ${topic}.`, notes: [ `Concetti chiave su: ${topic || 'Argomento'}`, `Analisi approfondita di: ${video.title}`, 'Sintesi dei punti fondamentali', 'Collegamenti interdisciplinari rilevati' ], planner: [ { time: '15:00', task: `Studio Intensivo: ${topic || 'Concetti Base'}`, details: 'Lettura e sottolineatura testo', duration: '45m' }, { time: '15:45', task: 'Active Recall & Quiz', details: 'Test di autovalutazione', duration: '15m' }, { time: '16:00', task: 'Analisi Video & Sintesi', details: 'Visione materiale multimediale', duration: '30m' }, ], quiz: Array.isArray(video.quiz) ? video.quiz : [video.quiz] // Normalize to array }; } catch (e) { console.error("Error generating simulated result:", e); return { videoId: 'Y9EjnBmO2Jw', // Fallback sicuro (Fisica) summary: "Errore durante il recupero dei dati. Riprova.", notes: ['Errore generazione note', 'Riprova più tardi'], planner: [], quiz: [{ question: 'Errore di sistema', options: [], hint: 'Riprova' }] }; } }; // ============================================ // CORE SIMULATOR (CUORE DEL SITO) // ============================================ const CoreSimulator = () => { const { protocol, credits, setCredits, setShowPayment, addXP } = useUser(); const [state, setState] = useState('INPUT'); const [query, setQuery] = useState(''); const [progress, setProgress] = useState(0); const [logs, setLogs] = useState([]); const [resultData, setResultData] = useState(getSimulatedResult('Introduzione')); const [isListening, setIsListening] = useState(false); const [quizAnswer, setQuizAnswer] = useState(null); const [showXP, setShowXP] = useState(false); const [apiError, setApiError] = useState(false); const startListening = () => { if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) { const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const recognition = new SpeechRecognition(); recognition.lang = 'it-IT'; recognition.onstart = () => setIsListening(true); recognition.onend = () => setIsListening(false); recognition.onresult = (event) => { const transcript = event.results[0][0].transcript; setQuery(transcript); setTimeout(() => handleSubmit(null, transcript), 500); }; recognition.start(); } else { alert("Riconoscimento vocale non supportato in questo browser."); } }; const handleSubmit = async (e, voiceQuery = null) => { e?.preventDefault(); if (protocol === 'GUEST') { document.getElementById('pricing')?.scrollIntoView({ behavior: 'smooth' }); return; } if (protocol === 'CORE' && credits <= 0) { setShowPayment(true); return; } if (protocol === 'CORE') setCredits(p => p - 1); const effectiveQuery = voiceQuery || query; if (voiceQuery) setQuery(voiceQuery); // Ensure state is synced setState('PROCESSING'); setProgress(0); setLogs([]); setApiError(false); }; // AI SIMULATION EFFECT useEffect(() => { if (state !== 'PROCESSING') return; const searchQuery = query; let isMounted = true; // Fake Progress Logs const logMessages = ['[INFO] Scansione 40.000 Fonti Accademiche...', '[INFO] Filtraggio Contenuti a Bassa Densità...', '[INFO] Generazione Mappe Neurali...']; let logIndex = 0; const logInt = setInterval(() => { if (logIndex < logMessages.length) { setLogs(p => [...p, logMessages[logIndex]]); logIndex++; } // Random system logs if (Math.random() > 0.7) { setLogs(prev => [...prev.slice(-4), ` [SYSTEM] ${['Analyzing patterns...', 'Decoding vector space...', 'Retrieving archives...', 'Synthesizing layout...'][Math.floor(Math.random() * 4)]}`]); } }, 600); const progInt = setInterval(() => { setProgress(p => { if (p >= 90) return p; return p + Math.floor(Math.random() * 5); }); }, 200); const runSimulation = async () => { // 1. Check Static Database First (Fast Cache) but allow AI override if key is present // Actually, we want AI to take over unless it fails. // But for specific static IDs (Fisica/Dante), users might prefer the "Perfect" curated content? // Let's Check: User said "Use Gemini for EVERY search". So we prefer AI. // But we can fallback to static if AI fails or for speed? // Let's try AI first. try { if (!SYSTEM_CONFIG.OPENROUTER_KEY) throw new Error("No API Key"); const response = await fetch("https://openrouter.ai/api/v1/chat/completions", { method: "POST", headers: { "Authorization": `Bearer ${SYSTEM_CONFIG.OPENROUTER_KEY}`, "Content-Type": "application/json", "HTTP-Referer": "http://localhost:5176", "X-Title": "Synapse OS" }, body: JSON.stringify({ "model": "google/gemini-2.0-flash-001", "messages": [ { "role": "system", "content": "You are Synapse OS, an advanced education interface. Analyze the user topic and return a STRICT JSON object (no markdown). Structure: { \"summary\": \"Comprehensive summary (8-10 sentences).\", \"notes\": [\"Point 1\", \"Point 2\", \"Point 3\"], \"videoSearchQuery\": \"${topic} documentario scuola\", \"quiz\": [ { \"question\": \"Q1?\", \"options\": [{\"text\":\"A\",\"correct\":false}, {\"text\":\"B\",\"correct\":true}, {\"text\":\"C\",\"correct\":false}], \"hint\": \"H1\" }, { \"question\": \"Q2?\", \"options\": [...], \"hint\": \"...\" }, { \"question\": \"Q3?\", \"options\": [...], \"hint\": \"...\" }, { \"question\": \"Q4?\", \"options\": [...], \"hint\": \"...\" }, { \"question\": \"Q5?\", \"options\": [...], \"hint\": \"...\" } ], \"planner\": [{\"time\":\"15:00\",\"task\":\"Deep Work\",\"details\":\"Study concept X and Y\",\"duration\":\"45m\"},{\"time\":\"16:00\",\"task\":\"Review\",\"details\":\"Test knowledge\",\"duration\":\"30m\"}] }. Notes: Quiz MUST have exactly 5 questions. Each question MUST have 3 options. Language: ITALIAN." }, { "role": "user", "content": `Topic: ${searchQuery}` } ] }) }); const data = await response.json(); if (!data.choices) throw new Error("Invalid API Response"); // Clean potential markdown code blocks ```json ... ``` let cleanContent = data.choices[0].message.content; if (cleanContent.startsWith('```')) { cleanContent = cleanContent.replace(/^```json\s*/, '').replace(/^```\s*/, '').replace(/\s*```$/, ''); } const aiContent = JSON.parse(cleanContent); let finalVideoId = `?listType=search&list=${encodeURIComponent(aiContent.videoSearchQuery || searchQuery)}`; // Try YouTube API if Key is present if (SYSTEM_CONFIG.YOUTUBE_KEY) { try { const ytRes = await fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=${encodeURIComponent(aiContent.videoSearchQuery || searchQuery)}&type=video&key=${SYSTEM_CONFIG.YOUTUBE_KEY}`); const ytData = await ytRes.json(); if (ytData.items && ytData.items.length > 0) { finalVideoId = ytData.items[0].id.videoId; } } catch (ytErr) { console.error("YouTube API Failed:", ytErr); } } if (isMounted) { setResultData({ videoId: finalVideoId, summary: aiContent.summary, notes: aiContent.notes, planner: aiContent.planner, quiz: Array.isArray(aiContent.quiz) ? aiContent.quiz : [aiContent.quiz] }); } } catch (e) { console.error("AI GENERATION FAILED:", e); if (isMounted) { setLogs(prev => [...prev, " [ERROR] NEURAL LINK SEVERED", " [WARN] SWITCHING TO LOCAL CACHE..."]); // Fallback to static setResultData(getSimulatedResult(searchQuery)); } } finally { if (isMounted) { setProgress(100); setTimeout(() => { setLogs(prev => [...prev, " [SUCCESS] DATA STREAM ESTABLISHED"]); setTimeout(() => setState('DASHBOARD'), 500); }, 500); } } }; runSimulation(); return () => { isMounted = false; clearInterval(logInt); clearInterval(progInt); }; }, [state, query]); const [quizIndex, setQuizIndex] = useState(0); const handleQuizAnswer = (isCorrect) => { setQuizAnswer(isCorrect ? 'correct' : 'wrong'); if (isCorrect) { setShowXP(true); addXP(200); // Add real XP setTimeout(() => setShowXP(false), 2000); // Next question delay setTimeout(() => { if (resultData?.quiz && quizIndex < resultData.quiz.length - 1) { setQuizIndex(p => p + 1); setQuizAnswer(null); } }, 1500); } }; return (

CORE SIMULATOR

INTERFACCIA ELABORAZIONE NEURALE v2.0

{protocol === 'CORE' &&
CREDITI GIORNALIERI: {credits}
}
{state === 'INPUT' && (
setQuery(e.target.value)} placeholder={protocol === 'GUEST' ? "Seleziona un Protocollo..." : "Es. Storia..."} className="flex-1 px-4 py-4 bg-transparent text-white placeholder:text-slate-500 focus:outline-none font-mono" disabled={protocol === 'GUEST'} />
{protocol === 'GUEST' &&
SELEZIONE PROTOCOLLO RICHIESTA
}
)} {state === 'PROCESSING' && (
ELABORAZIONE NEURALE...{progress}%
{logs.map((l, i) =>
{l}
)}
)} {state === 'DASHBOARD' && resultData && (
{/* Planner */}

PLANNER

{resultData.planner?.map((x, i) => (
{x.time}
{x.task}
{x.details &&
"{x.details}"
}
{x.duration}
))}
{/* Summary Card (NEW) */}

RIASSUNTO

{resultData.summary}
INTEL. ARTIFICIALE: 98% PRECISIONE
{/* Video Stream */}