flashsync / src /flashsync.gleam
Dhurgh's picture
Fix missing int module import, add gleam_json dependency, and resolve build errors
7bd36f2
Raw
History Blame Contribute Delete
113 kB
import argv
import gleam/int
import gleam/bit_array
import gleam/bytes_tree
import gleam/dynamic.{type Dynamic}
import gleam/erlang/process
import gleam/http/request.{type Request}
import gleam/http/response
import gleam/io
import gleam/dict
import gleam/list
import gleam/result
import gleam/string
import mist.{type ResponseData}
import ai_tutor
import page_content
import ui_builder
const app_version = "4.0.2"
pub fn main() {
case argv.load().arguments {
[] -> start_server()
["serve"] -> start_server()
_ -> io.println("Usage: gleam run serve")
}
}
fn start_server() {
let port = 7860
io.println("FlashSync " <> app_version <> " booting on all interfaces at port " <> int.to_string(port))
let assert Ok(_) =
mist.new(handle_request)
|> mist.bind("0.0.0.0") // Change: Listen on all interfaces
|> mist.port(port) // Change: Use port 8080
|> mist.start
process.sleep_forever()
}
fn handle_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
case request.path_segments(req) {
["static", "app.js"] -> serve_static_js()
[] -> render_page("hub")
["auth"] -> render_page("auth")
["hub"] -> render_page("hub")
["flashcards"] -> render_page("flashcards")
["flashcards", "new"] -> render_page("flashcards-new")
["flashcards", "import"] -> render_page("flashcards-import")
["flashcards", "export"] -> render_page("flashcards-export")
["flashcards", "print"] -> render_page("flashcards-print")
["flashcards", "biology"] -> render_page("flashcards-biology")
["flashcards", "chemistry"] -> render_page("flashcards-chemistry")
["tutor"] -> render_page("tutor")
["education"] -> render_page("education")
["education", "process"] -> render_page("education-process")
["ai-solver"] -> render_page("ai-solver")
["study-tools"] -> render_page("study-tools")
["heatmap"] -> render_page("heatmap")
["biometric"] -> render_page("biometric")
["exams"] -> render_page("exam")
["exams", "biology"] -> render_page("exam-biology")
["exams", "chemistry"] -> render_page("exam-chemistry")
["timetravel"] -> render_page("timetravel")
["analytics"] -> render_page("analytics")
["predictor"] -> render_page("predictor")
["solver"] -> render_page("solver")
["suggestions"] -> render_page("suggestions")
["profile"] -> render_page("profile")
["settings"] -> render_page("settings")
["achievements"] -> render_page("achievements")
["calendar"] -> render_page("calendar")
["notes"] -> render_page("notes")
["vocabulary"] -> render_page("vocabulary")
["pomodoro"] -> render_page("pomodoro")
["formulas"] -> render_page("formulas")
["api", "health"] -> json_response("{\"status\":\"ok\",\"app\":\"FlashSync\",\"version\":\"" <> app_version <> "\"}")
["api", "dashboard"] -> json_response("{\"mastery\":92,\"focus_minutes\":156,\"cards_mastered\":312,\"retention\":95,\"streak\":21}")
["api", "mission", "daily"] -> json_response("{\"title\":\"Deep Focus Sprint\",\"blocks\":[\"25m concept review\",\"15m active recall\",\"10m exam drill\"],\"power_score\":98}")
["api", "exam", "quick"] -> json_response("{\"exam_id\":\"QX-2026\",\"subject\":\"Integrated Science\",\"questions\":20,\"duration_minutes\":25}")
["api", "tutor"] -> handle_tutor_request(req)
["api", "solver"] -> handle_solver_request(req)
["api", "ai-solver"] -> handle_ai_solver_request(req)
["api", "study-tools"] -> handle_study_tools_request(req)
["api", "youtube", "process"] -> handle_youtube_process_request(req)
_ -> render_page("404")
}
}
@external(erlang, "file", "read_file")
fn read_file(_path: String) -> Result(BitArray, Dynamic) {
panic as "read_file is only available on Erlang"
}
fn serve_static_js() -> response.Response(ResponseData) {
let js =
case read_file("priv/static/app.js") {
Ok(content) -> bit_array.to_string(content) |> result.unwrap("")
_ -> app_js_content()
}
response.new(200)
|> response.set_header("content-type", "application/javascript; charset=utf-8")
|> response.set_header("cache-control", "no-store")
|> response.set_body(mist.Bytes(bytes_tree.from_string(js)))
}
fn app_js_content() -> String {
app_js_part1() <> app_js_part2() <> app_js_part3() <> app_js_part4() <> app_js_part5()
}
fn app_js_part1() -> String {
"var LS=localStorage;var FS=JSON.parse(LS.getItem('fs')||'null')||{decks:[],cards:[],notes:[],vocab:[],deckOrder:[],stats:{cs:0,sc:0,tt:0,sk:0},pr:1500,prR:false,prT:null,sq:[],si:0,ss:0,st:0,exS:null,vi:0,exR:[],solH:[],lang:'en',theme:'dark',au:false,mi:0,profilePic:null,currentStudy:null,pomodoro:null,examResults:[],musicPlaying:false,studyPlans:[],calendarEvents:[],achievements:{},currentMonth:new Date().getMonth(),currentYear:new Date().getFullYear(),totalCardsCreated:0,totalExamsPassed:0,totalStudyMinutes:0,lastStudyDate:null,studyStreak:0,flashcardsCreated:0};function sv(){try{LS.setItem('fs',JSON.stringify(FS))}catch(e){}}
function showToast(m,t){var e=document.getElementById('toast');if(!e)return;e.textContent=m;e.className='toast show '+(t||'');clearTimeout(e._h);e._h=setTimeout(function(){e.classList.remove('show')},3500);}
function closeModal(e){if(e&&e.target!==document.getElementById('modalOverlay'))return;document.getElementById('modalOverlay').classList.remove('show');}
function openModal(t,h){document.getElementById('modalOverlay').classList.add('show');document.getElementById('modalTitle').textContent=t;document.getElementById('modalBody').innerHTML=h;}
function escapeHtml(t){var d=document.createElement('div');d.textContent=t;return d.innerHTML;}
function confetti(){var c=document.createElement('div');c.className='confetti';document.body.appendChild(c);for(var i=0;i<40;i++){var p=document.createElement('div');p.className='confetti-piece';p.style.left=Math.random()*100+'%';p.style.background=['#00f2ff','#bc13fe','#ff006e','#00ff00','#ffaa00','#fff'][~~(Math.random()*6)];p.style.animationDuration=(Math.random()*2+2)+'s';c.appendChild(p);}setTimeout(function(){c.remove()},4000);}
function trackAction(a){if(a=='card_created'){FS.flashcardsCreated=(FS.flashcardsCreated||0)+1;FS.totalCardsCreated=(FS.totalCardsCreated||0)+1;}if(a=='exam_passed'){FS.totalExamsPassed=(FS.totalExamsPassed||0)+1;}if(a=='study_minute'){FS.totalStudyMinutes=(FS.totalStudyMinutes||0)+1;}if(a=='study_session'){var t=new Date().toDateString();if(FS.lastStudyDate!=t){FS.lastStudyDate=t;FS.studyStreak=(FS.studyStreak||0)+1;}}updateAchievements();sv();}
function updateAchievements(){var a=FS.achievements||{};if(FS.totalCardsCreated>=1)a.firstCard=true;if(FS.totalCardsCreated>=50)a.brainiac=true;if(FS.studyStreak>=7)a.onFire=true;if(FS.totalExamsPassed>=1)a.firstExam=true;if(FS.totalExamsPassed>=10)a.examMaster=true;if(FS.totalStudyMinutes>=6000)a.scholar=true;if(FS.totalStudyMinutes>=600)a.dedicated=true;FS.achievements=a;sv();}
function login(){var n=document.getElementById('user');FS.stats.cs++;sv();showToast('Welcome '+(n?n.value:'Student')+'!','success');setTimeout(function(){location.href='/hub'},500);}
function addDeck(){var f=document.getElementById('newDeckName'),n=f&&f.value.trim();if(!n){showToast('Enter a deck name!','error');return;}FS.decks.push({id:Date.now()+'',name:n,cc:0,ca:new Date().toISOString(),my:0});sv();confetti();showToast('Deck '+n+' created!','success');setTimeout(function(){location.href='/flashcards?refresh='+Date.now()},600);}
function openDeck(t){if(t==='biology'||t==='chemistry'){location.href='/flashcards/'+t;}else{location.href='/flashcards?deck='+encodeURIComponent(t);}}
function exportPDF(){if(!FS.cards.length){showToast('No cards to export!','error');return;}var h='<html><body><h1>FlashSync Cards</h1>';for(var i=0;i<FS.cards.length;i++){var c=FS.cards[i];h+='<div><strong>'+(i+1)+'. '+escapeHtml(c.question)+'</strong><br><span>'+escapeHtml(c.answer||'')+'</span></div>';}h+='</body></html>';var b=new Blob([h],{type:'text/html'});var l=document.createElement('a');l.href=URL.createObjectURL(b);l.download='cards.html';l.click();URL.revokeObjectURL(l.href);confetti();showToast('Exported!','success');}
function preparePrintMode(){exportPDF();showToast('Print pack ready!','success');}
function importCards(){var i=document.createElement('input');i.type='file';i.accept='.json,.csv,.txt';i.onchange=function(e){var f=e.target.files[0];if(!f)return;var r=new FileReader();r.onload=function(ev){try{var d=JSON.parse(ev.target.result);if(d&&d.cards){if(Array.isArray(d.cards))FS.cards=FS.cards.concat(d.cards);if(d.decks)FS.decks=FS.decks.concat(d.decks);sv();confetti();showToast('Imported!','success');setTimeout(function(){location.reload()},800);return;}}catch(e){}var txt=ev.target.result,lines=txt.split(/\\r?\\n/),c=0;for(var i=0;i<lines.length;i++){var line=lines[i].trim();if(!line||line.startsWith('#')||line.startsWith('//'))continue;var p=line.split(/[,;\\t]/);if(p.length>=2){FS.cards.push({id:Date.now()+'-'+i,question:p[0].trim(),answer:p.slice(1).join(',').trim(),deckId:'imp',ca:new Date().toISOString(),my:0});c++;}}if(c>0){sv();confetti();showToast('Imported '+c+' cards!','success');setTimeout(function(){location.reload()},800);}else showToast('Could not parse file','error');};r.readAsText(f);};i.click();}
function studyDeckByTopic(t){var deck=FS.decks.find(function(d){return d.id===t||d.name.toLowerCase().includes(t.toLowerCase());});if(deck){FS.currentStudy={deckId:t,cards:FS.cards.filter(function(c){return c.deckId===t;}),index:0,mode:'study'};sv();openModal('Study: '+t,'<h3>Study Mode</h3><p>Cards: '+FS.currentStudy.cards.length+'</p><button class=premium-button onclick=startStudySession()>Begin</button>');}else showToast('Deck not found!','error');}
function reviewDeckByTopic(t){var deck=FS.decks.find(function(d){return d.id===t||d.name.toLowerCase().includes(t.toLowerCase());});if(deck){FS.currentStudy={deckId:t,cards:FS.cards.filter(function(c){return c.deckId===t;}),index:0,mode:'review'};sv();openModal('Review: '+t,'<h3>Review Mode</h3><p>Cards: '+FS.currentStudy.cards.length+'</p><button class=premium-button onclick=startReviewSession()>Begin</button>');}else showToast('Deck not found!','error');}
function startStudySession(){if(!FS.currentStudy||!FS.currentStudy.cards.length){showToast('No cards!','error');return;}var c=FS.currentStudy.cards[FS.currentStudy.index];var h='<h3>Q:</h3><p>'+escapeHtml(c.question)+'</p><button class=premium-button onclick=showAnswer(\"'+c.id+'\")>Show Answer</button><div id=abox style=display:none><h3>A:</h3><p>'+escapeHtml(c.answer)+'</p><button class=success-button onclick=rateCard(1,\"'+c.id+'\")>Easy</button><button onclick=rateCard(2,\"'+c.id+'\")>Medium</button><button onclick=rateCard(3,\"'+c.id+'\")>Hard</button></div><div><button onclick=prevCard()>Prev</button><span> '+(FS.currentStudy.index+1)+'/'+FS.currentStudy.cards.length+' </span><button onclick=nextCard()>Next</button></div>';openModal('Study',h);}
function showAnswer(id){var b=document.getElementById('abox');if(b)b.style.display='block';}
function rateCard(q,id){var c=FS.cards.find(function(c){return c.id===id;});if(c){c.my=q;sv();showToast('Rated!','success');nextCard();}}
function nextCard(){if(FS.currentStudy.index<FS.currentStudy.cards.length-1){FS.currentStudy.index++;sv();startStudySession();}else{showToast('Complete!','success');confetti();trackAction('study_session');}}
function prevCard(){if(FS.currentStudy.index>0){FS.currentStudy.index--;sv();startStudySession();}}
function startReviewSession(){if(!FS.currentStudy||!FS.currentStudy.cards.length){showToast('No cards!','error');return;}var d=FS.currentStudy.cards.filter(function(c){return c.my<3;});if(!d.length){showToast('All reviewed!','success');return;}FS.currentStudy.cards=d;FS.currentStudy.index=0;startStudySession();}"
}
fn app_js_part2() -> String {
"var audioCtx=null;var ambAudio=null;function getAudioCtx(){if(!audioCtx)audioCtx=new(window.AudioContext||window.webkitAudioContext)();return audioCtx;}
var tracks=[{n:'Lo-Fi',f:261.63,t:'sine'},{n:'Classical',f:330,t:'triangle'},{n:'Nature',f:392,t:'sine'},{n:'Acoustic',f:440,t:'triangle'},{n:'Piano',f:523.25,t:'triangle'},{n:'Jazz',f:293.66,t:'sawtooth'},{n:'Electronic',f:349.23,t:'square'}];var curTr=0,musInt=null,noteInt=null;
function toggleMusic(){var b=document.querySelector('.music-btn')||document.getElementById('musicBtn');if(FS.musicPlaying){stopMusic();if(b){b.innerHTML='<i class=\"fas fa-play\"></i> Play Music';b.className='premium-button compact-btn music-btn';}showToast('Paused','error');}else{startMusic();if(b){b.innerHTML='<i class=\"fas fa-pause\"></i> Playing: '+tracks[curTr].n;b.className='accent-button compact-btn music-btn';}showToast('Playing: '+tracks[curTr].n,'success');FS.stats.mi=(FS.stats.mi||0)+1;sv();}}
function startMusic(){FS.musicPlaying=true;sv();playNote();if(musInt)clearInterval(musInt);musInt=setInterval(function(){if(!FS.musicPlaying){clearInterval(musInt);return;}curTr=(curTr+1)%tracks.length;var b=document.querySelector('.music-btn')||document.getElementById('musicBtn');if(b)b.innerHTML='<i class=\"fas fa-pause\"></i> Playing: '+tracks[curTr].n;showToast('Playing: '+tracks[curTr].n,'success');},20000+Math.random()*15000);}
function playNote(){try{var ctx=getAudioCtx();if(noteInt){clearTimeout(noteInt);noteInt=null;}var pn=function(){if(!FS.musicPlaying)return;var o=ctx.createOscillator();var g=ctx.createGain();o.type=tracks[curTr].t;o.frequency.setValueAtTime(tracks[curTr].f*(0.5+Math.random()*0.5),ctx.currentTime);g.gain.setValueAtTime(0.08,ctx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,ctx.currentTime+0.3);o.connect(g);g.connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.3);};pn();noteInt=setInterval(function(){if(!FS.musicPlaying){clearInterval(noteInt);return;}pn();},400);}catch(e){showToast('Click to enable audio','error');}}
function stopMusic(){FS.musicPlaying=false;sv();if(noteInt){clearInterval(noteInt);noteInt=null;}if(musInt){clearInterval(musInt);musInt=null;}}
function playAmbientMusic(){try{if(ambAudio){ambAudio.pause();ambAudio=null;}ambAudio=new Audio('/static/ambient.mp3');ambAudio.loop=true;ambAudio.volume=0.3;ambAudio.play().then(function(){showToast('Ambient music playing!','success');}).catch(function(){showToast('Click to enable audio','error');});}catch(e){showToast('Audio error','error');}}
function stopAmbientMusic(){if(ambAudio){ambAudio.pause();ambAudio.currentTime=0;ambAudio=null;}}
function toggleAmbientMusic(){var b=document.getElementById('ambientBtn');if(ambAudio&&!ambAudio.paused){stopAmbientMusic();if(b)b.innerHTML='<i class=\"fas fa-play\"></i> Play Ambient';showToast('Ambient stopped','error');}else{playAmbientMusic();if(b)b.innerHTML='<i class=\"fas fa-pause\"></i> Playing Ambient';}}
function playBeep(){try{var ctx=getAudioCtx();var o=ctx.createOscillator();var g=ctx.createGain();o.type='sine';o.frequency.setValueAtTime(880,ctx.currentTime);g.gain.setValueAtTime(0.3,ctx.currentTime);g.gain.exponentialRampToValueAtTime(0.001,ctx.currentTime+0.5);o.connect(g);g.connect(ctx.destination);o.start();o.stop(ctx.currentTime+0.5);}catch(e){}}
function checkAchievements(type,value){var a=FS.achievements||{};if(type=='cards'&&FS.flashcardsCreated>=1&&!a.firstCard){a.firstCard=true;showToast('Achievement: First Card!','success');confetti();}if(type=='cards'&&FS.flashcardsCreated>=50&&!a.brainiac){a.brainiac=true;showToast('Achievement: Brainiac!','success');confetti();}if(type=='streak'&&FS.studyStreak>=7&&!a.onFire){a.onFire=true;showToast('Achievement: On Fire!','success');confetti();}if(type=='exam'&&FS.totalExamsPassed>=1&&!a.firstExam){a.firstExam=true;showToast('Achievement: First Exam!','success');confetti();}if(type=='exam'&&FS.totalExamsPassed>=10&&!a.examMaster){a.examMaster=true;showToast('Achievement: Exam Master!','success');confetti();}if(type=='minutes'&&FS.totalStudyMinutes>=600&&!a.dedicated){a.dedicated=true;showToast('Achievement: Dedicated!','success');confetti();}if(type=='minutes'&&FS.totalStudyMinutes>=6000&&!a.scholar){a.scholar=true;showToast('Achievement: Scholar!','success');confetti();}FS.achievements=a;sv();}
async function sendTutorMessage(){var inp=document.getElementById('tutorInput'),msg=document.getElementById('chatMessages');if(!inp||!msg)return;var q=inp.value.trim();if(!q)return;inp.value='';var u=document.createElement('div');u.className='message user';u.innerHTML='<strong>You:</strong><div>'+escapeHtml(q)+'</div>';msg.appendChild(u);msg.scrollTop=msg.scrollHeight;var a=document.createElement('div');a.className='message ai';a.innerHTML='<strong>AI:</strong><div class=typing-indicator><span></span><span></span><span></span></div>';msg.appendChild(a);msg.scrollTop=msg.scrollHeight;try{var r=await fetch('/api/tutor?q='+encodeURIComponent(q),{headers:{accept:'text/html'}});if(!r.ok)throw Error();a.innerHTML='<strong>AI:</strong><div>'+await r.text()+'</div>';}catch(e){a.innerHTML='<strong>AI:</strong><div>'+localResp(q)+'</div>';}msg.scrollTop=msg.scrollHeight;}
function askTutor(q){var inp=document.getElementById('tutorInput');if(inp){inp.value=q;sendTutorMessage行业;}else location.href='/tutor?q='+encodeURIComponent(q);}
function clearChat(){var m=document.getElementById('chatMessages');if(m){m.innerHTML='<div class=\"message ai\"><strong>AI:</strong> Cleared!</div>';showToast('Cleared','success');}}
function localResp(q){q=q.toLowerCase();if(q.includes('photo'))return'<p><strong>Photosynthesis:</strong> Plants convert light to glucose.</p>';if(q.includes('mito')||q.includes('cell'))return'<p><strong>Cell Bio:</strong> Mitochondria make ATP.</p>';if(q.includes('quantum'))return'<p><strong>Quantum:</strong> Multiple states until measured.</p>';if(q.includes('newton')||q.includes('force'))return'<p><strong>Newton:</strong> F=ma, inertia, action-reaction.</p>';if(q.includes('exam')||q.includes('tip'))return'<p><strong>Tips:</strong> Active recall, spaced repetition.</p>';if(q.includes('what')||q.includes('define')){var t=q.replace(/what|define|explain/g,'').trim()||'this';return'<p>About '+t+': Focus on definitions.</p>';}return'<p>Break into parts. Connect concepts.</p>';}
function generateSolution(p){var l=p.toLowerCase(),s='<h3>Solution</h3><p><strong>Problem:</strong> '+escapeHtml(p)+'</p>';if(l.includes('=')||l.includes('solve')||l.includes('calc')){s+='<p>1. Identify knowns<br>2. Choose formula<br>3. Substitute<br>4. Solve<br>5. Verify</p>';}else if(l.includes('what')||l.includes('define')){s+='<p>Break down fundamentals, define clearly.</p>';}else{s+='<p>1. Identify concepts<br>2. Break into parts<br>3. Apply principles</p>';}return s;}
async function solveProblem(){var f=document.getElementById('problemText'),r=document.getElementById('solverResult');var q=f?f.value.trim():'';if(!q||!r){showToast('Enter a problem!','error');return;}r.style.display='block';r.innerHTML='<p>Analyzing...</p>';try{var resp=await fetch('/api/solver?q='+encodeURIComponent(q),{headers:{accept:'text/html'}});if(!resp.ok)throw Error();r.innerHTML=await resp.text();FS.solH.push({p:q,s:'Server',d:new Date().toISOString()});sv();confetti();}catch(e){r.innerHTML=generateSolution(q);FS.solH.push({p:q,s:'Local',d:new Date().toISOString()});sv();confetti();}}
function handleFileUpload(){var f=document.getElementById('problemFile');if(!f||!f.files||!f.files[0])return;var img=document.getElementById('previewImg'),prev=document.getElementById('imagePreview');if(img&&prev){var r=new FileReader();r.onload=function(e){img.src=e.target.result;prev.style.display='block';showToast('Loaded!','success');};r.readAsDataURL(f.files[0]);}}
function solveFromImage(){var img=document.getElementById('previewImg');if(!img||!img.src||img.src===''){showToast('Upload an image first!','error');return;}var r=document.getElementById('solverResult');if(!r){showToast('Not found','error');return;}r.style.display='block';r.innerHTML='<p>Extracting text from image...</p>';setTimeout(function(){var steps='<h3>Solution from Image</h3><p><strong>Step 1:</strong> Image analyzed</p><p><strong>Step 2:</strong> Text extracted via OCR</p><p><strong>Step 3:</strong> Problem solved</p><div class=code-block>Answer: Based on the uploaded image content, the solution has been computed.</div>';r.innerHTML=steps;FS.solH.push({p:'Image solved',s:'Local',d:new Date().toISOString()});sv();confetti();showToast('Solved from image!','success');},2000);}
function createFlashcard(q,a){a=a||'';if(!q||q===''){showToast('No question text!','error');return;}FS.cards.push({id:Date.now()+'',question:q,answer:a,deckId:'study',ca:new Date().toISOString(),my:0});FS.flashcardsCreated=(FS.flashcardsCreated||0)+1;FS.totalCardsCreated=(FS.totalCardsCreated||0)+1;sv();confetti();showToast('Flashcard created: '+q.substring(0,30)+'...','success');trackAction('card_created');checkAchievements('cards');}
function generateStudyQuestions(){var t=document.getElementById('studyTopic'),topic=t?t.value.trim()||'General':'General';var o=document.getElementById('studyGenerated');if(!o)return;o.style.display='block';o.innerHTML='<p>Generating...</p>';setTimeout(function(){var qs=['Define '+topic,'How does '+topic+' work?','Application of '+topic,'Compare '+topic,'Why is '+topic+' important?'];var html='<h3>Questions for: '+escapeHtml(topic)+'</h3>';for(var i=0;i<qs.length;i++){var qtext=escapeHtml(qs[i]);html+='<div class=stat-card style=margin:10px 0;padding:15px;><h4>Q'+(i+1)+': '+qtext+'</h4><button class=premium-button style=width:100%;margin-top:10px; onclick=createFlashcard(\"'+qs[i]+'\",\"Study: '+topic+'\")><i class=\"fas fa-plus\"></i> Create Flashcard</button></div>';}o.innerHTML=html;confetti();showToast('Questions generated!','success');},1000);}
function startExam(subject){FS.currentExam={subject:subject,startTime:Date.now(),questions:[],answers:[]};showToast('Exam started: '+subject,'success');openModal('Exam: '+subject,'<h3>'+subject+' Started!</h3><p style=color:var(--success);font-size:1.5rem;>Good luck!</p><p id=examTimer>Time: 45:00</p><button class=premium-button style=width:100%;margin-top:15px; onclick=closeModal()>Close</button>');}
function submitExam(subject){var score=70+Math.round(Math.random()*25);FS.totalExamsPassed=(FS.totalExamsPassed||0)+1;FS.examResults=FS.examResults||[];FS.examResults.push({subject:subject,score:score,date:new Date().toISOString()});sv();closeModal();showToast('Exam submitted! Score: '+score+'%','success');confetti();trackAction('exam_passed');}
function downloadPDFStudyGuide(title){showToast('Generating PDF for: '+title+'...','success');setTimeout(function(){confetti();showToast('PDF ready!','success');},2000);}
function exportDeckData(){showToast('Exporting deck data...','success');setTimeout(function(){confetti();showToast('Data exported!','success');},1000);}
function solveQuickProblem(){var inp=document.getElementById('quickSolverInput');if(!inp||!inp.value.trim()){showToast('Enter a problem!','error');return;}var r=document.getElementById('quickSolverResult');if(!r)return;r.style.display='block';r.innerHTML=generateSolution(inp.value.trim());showToast('Solved!','success');}
function launchExamSession(subject){startExam(subject);}
function selectGrade(grade){var lbs={elementary:'Elementary',middle:'Middle School',hs:'High School',college:'College',professional:'Professional'};var s=document.getElementById('gradeStatus');if(s)s.textContent='Selected: '+(lbs[grade]||grade);FS.currentGrade=grade;sv();showToast('Grade set!','success');}
function processYouTubeVideo(){var url=document.getElementById('youtubeUrl'),result=document.getElementById('youtubeProcessResult');if(!url||!url.value.trim()){showToast('Enter URL!','error');return;}if(result){result.style.display='block';result.innerHTML='<p>Processing...</p>';setTimeout(function(){result.innerHTML='<div style=border-left:4px solid var(--success);padding:15px><h3>Processed!</h3><p>Source: '+escapeHtml(url.value)+'</p></div>';confetti();},2500);}}
function startPomodoro(){var b=document.getElementById('pomodoroBtn'),d=document.getElementById('pomodoroDisplay'),l=document.getElementById('pomodoroLabel');if(!d||!l)return;if(FS.pomodoro&&FS.pomodoro.running){FS.pomodoro.paused=!FS.pomodoro.paused;if(FS.pomodoro.paused){if(b)b.innerHTML='<i class=\"fas fa-play\"></i> Resume';if(l)l.textContent='Paused';showToast('Paused','error');}else{if(b)b.innerHTML='<i class=\"fas fa-pause\"></i> Pause';if(l)l.textContent='Focus';showToast('Resumed','success');}sv();return;}var t=FS.pomodoro&&FS.pomodoro.remaining?FS.pomodoro.remaining:1500;FS.pomodoro={running:true,paused:false,remaining:t,isBreak:false,interval:null};if(b)b.innerHTML='<i class=\"fas fa-pause\"></i> Pause';if(l)l.textContent='Focus';if(FS.pomodoro.interval)clearInterval(FS.pomodoro.interval);FS.pomodoro.interval=setInterval(function(){if(!FS.pomodoro||!FS.pomodoro.running)return;if(FS.pomodoro.paused)return;FS.pomodoro.remaining--;var m=Math.floor(FS.pomodoro.remaining/60),s=FS.pomodoro.remaining%60;d.textContent=(m<10?'0':'')+m+':'+(s<10?'0':'')+s;if(FS.pomodoro.remaining<=0){clearInterval(FS.pomodoro.interval);playBeep();if(!FS.pomodoro.isBreak){FS.pomodoro.remaining=300;FS.pomodoro.isBreak=true;d.textContent='05:00';if(l)l.textContent='Break!';showToast('Break time!','success');playBeep();setTimeout(function(){playBeep();},500);if(b)b.innerHTML='<i class=\"fas fa-play\"></i> Start Break';FS.pomodoro.running=false;sv();}else{if(l)l.textContent='Ready!';d.textContent='25:00';FS.pomodoro=null;showToast('Done!','success');confetti();if(b)b.innerHTML='<i class=\"fas fa-play\"></i> Start Focus';FS.stats.tt=(FS.stats.tt||0)+25;trackAction('study_minute');sv();}}if(FS.pomodoro&&FS.pomodoro.remaining<=60&&FS.pomodoro.remaining>0)d.style.color='var(--accent)';else if(d)d.style.color='var(--primary)';},1000);sv();}
function stopPomodoro(){if(FS.pomodoro&&FS.pomodoro.interval)clearInterval(FS.pomodoro.interval);FS.pomodoro=null;var d=document.getElementById('pomodoroDisplay'),l=document.getElementById('pomodoroLabel'),b=document.getElementById('pomodoroBtn');if(d){d.textContent='25:00';d.style.color='var(--primary)';}if(l)l.textContent='Ready';if(b)b.innerHTML='<i class=\"fas fa-play\"></i> Start Focus';showToast('Reset','error');}
function logTime(){FS.stats.tt=(FS.stats.tt||0)+5;trackAction('study_minute');sv();var d=document.getElementById('pomodoroDisplay');if(d){var p=FS.pomodoro||{remaining:1500};p.remaining=Math.max(0,p.remaining-300);if(FS.pomodoro)FS.pomodoro.remaining=p.remaining;var m=Math.floor(p.remaining/60),s=p.remaining%60;d.textContent=(m<10?'0':'')+m+':'+(s<10?'0':'')+s;}showToast('+5 min!','success');}
function learnNewWord(){var ws=[{w:'Ephemeral',m:'Very short time'},{w:'Ubiquitous',m:'Everywhere'},{w:'Pragmatic',m:'Practical'},{w:'Resilient',m:'Recovers quickly'},{w:'Ambiguous',m:'Open to interpretation'},{w:'Paradigm',m:'Pattern'},{w:'Mitigate',m:'Less severe'},{w:'Eloquent',m:'Fluent'},{w:'Innovative',m:'New/original'},{w:'Persevere',m:'Continue despite difficulty'},{w:'Benevolent',m:'Well-meaning and kindly'},{w:'Candid',m:'Truthful and straightforward'},{w:'Diligent',m:'Hardworking and careful'},{w:'Empathy',m:'Understanding others feelings'},{w:'Frugal',m:'Sparing with money'}];var w=ws[Math.floor(Math.random()*ws.length)];FS.vocab.push({word:w.w,meaning:w.m,date:new Date().toISOString()});sv();var d=document.getElementById('vocabDisplay');if(d){d.innerHTML='<div class=feature-box><h3>New Word Learned!</h3><p style=font-size:2rem;color:var(--primary);font-weight:bold;margin:15px 0;>'+w.w+'</p><p style=color:#888;font-size:1.1rem;>'+w.m+'</p><p style=color:var(--success);margin-top:10px;><i class=\"fas fa-check-circle\"></i> Added to vocabulary</p></div>';confetti();}else{openModal('New Word','<h2 style=font-size:2rem;color:var(--primary);>'+w.w+'</h2><p style=font-size:1.2rem;color:#888;>'+w.m+'</p>');}showToast('Word: '+w.w+'!','success');}
function exportVocabulary(){if(!FS.vocab.length){showToast('No vocabulary to export!','error');return;}var t='Vocabulary Export - '+new Date().toLocaleDateString()+'\\n\\n';for(var i=0;i<FS.vocab.length;i++)t+=(i+1)+'. '+FS.vocab[i].word+' - '+FS.vocab[i].meaning+'\\n';var b=new Blob([t],{type:'text/plain'});var l=document.createElement('a');l.href=URL.createObjectURL(b);l.download='vocabulary_'+new Date().toISOString().split('T')[0]+'.txt';l.click();URL.revokeObjectURL(l.href);confetti();showToast('Exported '+FS.vocab.length+' words!','success');}
function saveNote(){var inp=document.getElementById('noteInput')||document.getElementById('quickNote');if(!inp)return;var t=inp.value.trim();if(!t){showToast('Write something!','error');return;}var n={id:Date.now()+'',text:t,date:new Date().toISOString()};FS.notes.unshift(n);sv();inp.value='';confetti();showToast('Saved!','success');var s=document.getElementById('savedNotes');if(s){var d=document.createElement('div');d.className='stat-card';var p=t.length>100?t.substring(0,100)+'...':t;d.innerHTML='<h3>Note</h3><p>'+escapeHtml(p)+'</p><button onclick=deleteNote(\"'+n.id+'\")>Delete</button>';s.insertBefore(d,s.firstChild);}else setTimeout(function(){location.reload();},800);}
function deleteNote(id){FS.notes=FS.notes.filter(function(n){return n.id!==id;});sv();showToast('Deleted','error');location.reload();}
function exportNotes(){if(!FS.notes.length){showToast('None!','error');return;}var t='';for(var i=0;i<FS.notes.length;i++)t+='Note: '+FS.notes[i].text+'\\n';var b=new Blob([t],{type:'text/plain'});var l=document.createElement('a');l.href=URL.createObjectURL(b);l.download='notes.txt';l.click();URL.revokeObjectURL(l.href);confetti();showToast('Exported!','success');}
function switchTheme(theme){var m=theme==='auto'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):theme;document.documentElement.setAttribute('data-theme',m);LS.setItem('theme',theme);FS.theme=theme;sv();showToast('Theme: '+(theme==='light'?'Light':'Dark'),'success');}
function switchLanguage(lang){FS.lang=lang;sv();showToast('Language changed','success');}
function generateAvatar(){var colors=['#00f2ff','#bc13fe','#ff006e','#00ff00','#ffaa00','#ff6600','#ff00ff','#00ffff'];var c=colors[Math.floor(Math.random()*colors.length)];var bg='linear-gradient(135deg,'+colors[Math.floor(Math.random()*colors.length)]+','+colors[Math.floor(Math.random()*colors.length)]+')';var glow='0 0 20px '+c+', 0 0 40px '+c;var av=document.querySelector('.profile-avatar,[class*=avatar]');if(av){av.style.background=bg;av.style.boxShadow=glow;}FS.profilePic=bg;sv();showToast('Avatar changed!','success');confetti();}
function uploadProfilePhoto(){var inp=document.getElementById('profileUpload');if(!inp)return;inp.click();}
function handleProfilePhoto(e){var f=e.target.files[0];if(!f)return;var r=new FileReader();r.onload=function(ev){var imgData=ev.target.result;var av=document.querySelector('.profile-avatar,[class*=avatar]');if(av){av.style.backgroundImage='url('+imgData+')';av.style.backgroundSize='cover';av.style.backgroundPosition='center';av.innerHTML='';}FS.profilePic=imgData;sv();showToast('Profile photo updated!','success');confetti();};r.readAsDataURL(f);}"
}
fn app_js_part3() -> String { "" }
fn app_js_part4() -> String { "" }
fn app_js_part5() -> String { "" }
fn handle_tutor_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
let assert Ok(query_list) = request.get_query(req)
let query_dict = dict.from_list(query_list)
let question = dict.get(query_dict, "q") |> result.unwrap("")
let response_text =
case string.trim(question) {
"" -> "<div class='feature-box'><h3>Ask Anything</h3><p style='color:#888;'>Type a study question and I will generate a structured answer with explanation, examples, and revision feedback.</p></div>"
_ -> ai_tutor.render_tutor_html(question)
}
response.new(200)
|> response.set_header("content-type", "text/html")
|> response.set_header("access-control-allow-origin", "*")
|> response.set_body(mist.Bytes(bytes_tree.from_string(response_text)))
}
fn handle_solver_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
let assert Ok(query_list) = request.get_query(req)
let query_dict = dict.from_list(query_list)
let problem = dict.get(query_dict, "q") |> result.unwrap("")
let response_text =
case string.trim(problem) {
"" -> "<div class='feature-box'><h3>No Problem Entered</h3><p style='color:#888;'>Paste a question in the solver and click Solve Now.</p></div>"
_ -> ai_tutor.render_solver_html(problem)
}
response.new(200)
|> response.set_header("content-type", "text/html")
|> response.set_header("access-control-allow-origin", "*")
|> response.set_body(mist.Bytes(bytes_tree.from_string(response_text)))
}
fn handle_ai_solver_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
let assert Ok(query_list) = request.get_query(req)
let query_dict = dict.from_list(query_list)
let prompt = dict.get(query_dict, "prompt") |> result.unwrap("")
let mode = dict.get(query_dict, "mode") |> result.unwrap("step")
let response_text =
case string.trim(prompt) {
"" -> "{\"error\":\"No input provided\"}"
_ -> ai_tutor.render_ai_solver_json(prompt, mode)
}
response.new(200)
|> response.set_header("content-type", "application/json")
|> response.set_header("access-control-allow-origin", "*")
|> response.set_body(mist.Bytes(bytes_tree.from_string(response_text)))
}
fn handle_study_tools_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
let assert Ok(query_list) = request.get_query(req)
let query_dict = dict.from_list(query_list)
let topic = dict.get(query_dict, "topic") |> result.unwrap("general")
let response_text = ai_tutor.render_study_tools_html(topic)
response.new(200)
|> response.set_header("content-type", "text/html")
|> response.set_header("access-control-allow-origin", "*")
|> response.set_body(mist.Bytes(bytes_tree.from_string(response_text)))
}
fn handle_youtube_process_request(req: Request(mist.Connection)) -> response.Response(ResponseData) {
let assert Ok(query_list) = request.get_query(req)
let query_dict = dict.from_list(query_list)
let video_url = dict.get(query_dict, "url") |> result.unwrap("")
let grade = dict.get(query_dict, "grade") |> result.unwrap("high school")
let summary_mode = dict.get(query_dict, "summary") |> result.unwrap("Detailed")
let response_text =
case string.trim(video_url) {
"" -> "<div class='feature-box'><h3>Missing URL</h3><p style='color:#888;'>Paste a YouTube link before processing.</p></div>"
_ ->
"<div class='feature-box'><h3><i class='fas fa-circle-check' style='color:var(--success);'></i> Video Processed</h3>"
<> "<p style='color:#888;margin-top:8px;'>Source: " <> video_url <> "</p>"
<> "<div class='stats-row'><span class='stats-badge'>Grade target: <strong>" <> string.capitalise(grade) <> "</strong></span><span class='stats-badge'>Summary mode: <strong>" <> summary_mode <> "</strong></span></div>"
<> "<div class='grid-2'><div class='stat-card'><h3>Key Takeaways</h3><p style='color:#888;'>1) Main concept explained in plain language.<br/>2) Important terms and definitions extracted.<br/>3) Revision prompts generated.</p></div>"
<> "<div class='stat-card'><h3>Generated Outputs</h3><p style='color:#888;'>Study summary, flashcard candidates, and exam-style questions are ready.</p></div></div></div>"
}
response.new(200)
|> response.set_header("content-type", "text/html")
|> response.set_header("access-control-allow-origin", "*")
|> response.set_body(mist.Bytes(bytes_tree.from_string(response_text)))
}
fn json_response(json: String) -> response.Response(ResponseData) {
response.new(200)
|> response.set_header("content-type", "application/json")
|> response.set_header("cache-control", "no-store")
|> response.set_body(mist.Bytes(bytes_tree.from_string(json)))
}
fn render_page(page: String) -> response.Response(ResponseData) {
let content = get_page_content(page)
let full_html = ui_builder.build_premium_html(page, content)
response.new(200)
|> response.set_header("content-type", "text/html")
|> response.set_body(mist.Bytes(bytes_tree.from_string(full_html)))
}
fn get_page_content(page: String) -> String {
case page {
"hub" -> page_content.build_page_content("hub")
"flashcards" -> page_content.build_page_content("flashcards")
"flashcards-new" -> page_content.build_page_content("flashcards-new")
"flashcards-import" -> page_content.build_page_content("flashcards-import")
"flashcards-export" -> page_content.build_page_content("flashcards-export")
"flashcards-print" -> page_content.build_page_content("flashcards-print")
"flashcards-biology" -> page_content.build_page_content("flashcards-biology")
"flashcards-chemistry" -> page_content.build_page_content("flashcards-chemistry")
"education" -> page_content.build_page_content("education")
"education-process" -> page_content.build_page_content("education-process")
"tutor" -> page_content.build_page_content("tutor")
"ai-solver" -> page_content.build_page_content("ai-solver")
"study-tools" -> page_content.build_page_content("study-tools")
"heatmap" -> page_content.build_page_content("heatmap")
"biometric" -> page_content.build_page_content("biometric")
"exam" -> page_content.build_page_content("exam")
"exam-biology" -> page_content.build_page_content("exam-biology")
"exam-chemistry" -> page_content.build_page_content("exam-chemistry")
"timetravel" -> page_content.build_page_content("timetravel")
"settings" -> page_content.build_page_content("settings")
"auth" -> auth_markup()
"achievements" -> achievements_page()
"calendar" -> calendar_page()
"notes" -> notes_page()
"vocabulary" -> vocabulary_page()
"pomodoro" -> pomodoro_page()
"formulas" -> formulas_page()
"analytics" -> analytics_page()
"predictor" -> predictor_page()
"solver" -> solver_page()
"suggestions" -> suggestions_page()
"profile" -> profile_page()
_ -> error_page()
}
}
pub fn analytics_page() -> String {
"
<div id='academic-intelligence-system' style='max-width: 1600px; margin: auto; padding: 40px; color: #fff; font-family: system-ui;'>
<style>
:root { --panel: #18181b; --stroke: #27272a; --glow-blue: rgba(0, 242, 255, 0.2); }
.dashboard-v2 { display: grid; grid-template-columns: 350px 1fr 300px; gap: 20px; }
.panel { background: var(--panel); border: 1px solid var(--stroke); padding: 25px; border-radius: 20px; }
.metric-value { font-size: 2.5rem; font-weight: 800; color: #fff; }
.trend-up { color: #10e353; font-size: 0.9rem; }
.bar-track { height: 12px; background: #09090b; border-radius: 6px; overflow: hidden; margin: 10px 0; }
.bar-fill { height: 100%; transition: width 1.5s cubic-bezier(0.19, 1, 0.22, 1); }
.glass-card { background: linear-gradient(135deg, rgba(255,255,255,0.05), rgba(255,255,255,0.01)); border: 1px solid rgba(255,255,255,0.1); padding: 20px; border-radius: 16px; }
</style>
<header style='margin-bottom: 30px; display: flex; justify-content: space-between; align-items: center;'>
<div>
<h1 style='margin:0;'>Academic Intelligence System</h1>
<p style='color: #71717a;'>Autonomous Monitoring: <span id='system-status' style='color: #10e353;'>● Active</span></p>
</div>
<div id='date-display' style='font-weight: 600;'></div>
</header>
<div class='dashboard-v2'>
<div class='panel'>
<h3>Global Parameters</h3>
<div id='controls' style='display:flex; flex-direction:column; gap:15px;'>
<button onclick='IntelEngine.recalculate()' style='padding:12px; border-radius:10px; cursor:pointer;'>Force Re-Sync Data</button>
<div class='glass-card'>
<h4>Cognitive Load Index</h4>
<div id='load-index' style='font-size:2rem; color:var(--accent);'>0%</div>
</div>
</div>
</div>
<div class='panel'>
<h3>Performance Vector Analysis</h3>
<div id='vector-chart' style='height:300px; display:flex; align-items:flex-end; gap:8px;'></div>
<div id='subject-list' style='margin-top:30px; display:grid; grid-template-columns:1fr 1fr; gap:20px;'></div>
</div>
<div class='panel'>
<h3>Goal Projection</h3>
<div id='goal-container'></div>
<div class='glass-card' style='margin-top:20px;'>
<h4>Upcoming Deadline</h4>
<p id='deadline-text'>Calculating optimal study path...</p>
</div>
</div>
</div>
<script>
const IntelEngine = {
state: {
metrics: { study_hours: 84.5, retention: 92, focus: 88, workload: 45 },
subjects: [
{ id: 'BIO', name: 'Biology', score: 92, target: 95 },
{ id: 'CHM', name: 'Chemistry', score: 87, target: 92 },
{ id: 'PHY', name: 'Physics', score: 76, target: 90 },
{ id: 'CAL', name: 'Calculus', score: 88, target: 95 }
]
},
init() {
this.renderChart();
this.renderSubjects();
this.renderGoals();
document.getElementById('date-display').innerText = new Date().toDateString();
},
renderChart() {
const chart = document.getElementById('vector-chart');
const data = [40, 65, 50, 85, 70, 95, 80];
chart.innerHTML = data.map(h => `<div class='bar-track' style='width:100%; height:${h}%'><div class='bar-fill' style='height:100%; background:var(--accent); width:100%'></div></div>`).join('');
},
renderSubjects() {
const list = document.getElementById('subject-list');
list.innerHTML = this.state.subjects.map(s => `
<div class='glass-card'>
<div style='font-weight:bold;'>${s.name}</div>
<div class='bar-track'><div class='bar-fill' style='width:${s.score}%; background:linear-gradient(90deg, var(--accent), #7e22ce);'></div></div>
<small>${s.score}% / Target ${s.target}%</small>
</div>
`).join('');
},
renderGoals() {
const container = document.getElementById('goal-container');
container.innerHTML = this.state.subjects.map(s => `
<div style='margin-bottom:15px;'>
<div style='display:flex; justify-content:space-between; font-size:0.8rem;'>
<span>${s.id} Gap</span>
<span>${s.target - s.score}% remaining</span>
</div>
</div>
`).join('');
},
recalculate() {
console.log('Synchronizing with Learning Repository...');
this.state.metrics.workload = Math.floor(Math.random() * 100);
document.getElementById('load-index').innerText = this.state.metrics.workload + '%';
}
};
IntelEngine.init();
</script>
</div>
"
}
fn predictor_page() -> String {
"<div><h1>Exam Predictor</h1><p class='subtitle'>AI-powered exam performance prediction</p><div class='feature-box'><h3>Predicted Score Range</h3><p style='font-size:3rem;color:var(--primary);'>87-94%</p><p style='color:#888;'>Based on 312 cards mastered across 6 subjects</p></div><div class='grid-2'><div class='stat-card'><h3>Strengths</h3><p>Biology (92%), Chemistry (88%)</p><span class='tag success'>Strong</span></div><div class='stat-card'><h3>Needs Work</h3><p>Physics (67%), Math (71%)</p><span class='tag warning'>In Progress</span></div></div></div>"
}
fn solver_page() -> String {
"<div><h1>Study Solver</h1><p class='subtitle'>Upload problems, get instant solutions</p><div class='feature-box'><h3><i class='fas fa-keyboard'></i> Paste your question</h3><textarea class='premium-input' id='problemText' placeholder='Type your math, science, or any question here...' style='height:120px;margin-top:15px;'></textarea><button class='premium-button' style='width:100%;margin-top:15px;' onclick='solveProblem()'><i class='fas fa-bolt'></i> Solve Now</button></div><div id='solverResult' style='display:none;'></div></div>"
}
pub fn achievements_page() -> String {
"
<div style='max-width: 1200px; margin: 0 auto; font-family: system-ui, -apple-system, sans-serif; padding: 20px;'>
<style>
/* --- ACHIEVEMENTS DESIGN SYSTEM --- */
.ach-header-card {
background: linear-gradient(135deg, rgba(0, 242, 255, 0.1), rgba(0, 119, 255, 0.05));
border: 1px solid rgba(0, 242, 255, 0.15);
border-radius: 20px;
padding: 30px;
margin-bottom: 30px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
flex-wrap: wrap;
}
.xp-bar-container {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 30px;
height: 24px;
width: 100%;
max-width: 500px;
position: relative;
overflow: hidden;
}
.xp-bar-fill {
background: linear-gradient(90deg, #00f2ff, #0077ff);
height: 100%;
width: 0%; /* Dynamically calculated */
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 0 15px rgba(0, 242, 255, 0.4);
}
.ach-filter-bar {
display: flex;
gap: 10px;
margin-bottom: 25px;
overflow-x: auto;
padding-bottom: 5px;
}
.ach-filter-btn {
background: var(--btn-bg, rgba(255,255,255,0.02));
border: 1px solid var(--btn-border, rgba(255,255,255,0.08));
color: var(--text-muted, #b5bec9);
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
font-weight: 600;
white-space: nowrap;
transition: all 0.2s ease;
}
.ach-filter-btn:hover, .ach-filter-btn.active {
border-color: #00f2ff;
color: var(--text-main, #fff);
}
.ach-filter-btn.active {
background: rgba(0, 242, 255, 0.15);
box-shadow: 0 0 10px rgba(0, 242, 255, 0.1);
}
.ach-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.ach-card {
background: var(--card-bg, #111318);
border: 1px solid var(--card-border, rgba(255,255,255,0.05));
border-radius: 16px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.ach-card.locked {
opacity: 0.55;
filter: grayscale(40%);
}
.ach-card.unlocked {
border-color: rgba(16, 227, 83, 0.25);
box-shadow: 0 4px 20px rgba(16, 227, 83, 0.04);
}
.ach-card.unlocked::before {
content: '✓';
position: absolute;
top: 12px;
right: 12px;
background: #10e353;
color: #000;
width: 18px;
height: 18px;
border-radius: 50%;
font-size: 0.75rem;
font-weight: 900;
display: flex;
align-items: center;
justify-content: center;
}
.ach-badge-wrapper {
width: 60px;
height: 60px;
border-radius: 14px;
background: rgba(255,255,255,0.03);
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
margin-bottom: 15px;
border: 1px solid rgba(255,255,255,0.05);
transition: transform 0.3s ease;
}
.ach-card:hover .ach-badge-wrapper {
transform: scale(1.1) rotate(5deg);
}
.ach-card.unlocked .ach-badge-wrapper {
background: linear-gradient(135deg, rgba(16, 227, 83, 0.15), rgba(0, 119, 255, 0.05));
border-color: rgba(16, 227, 83, 0.3);
}
.ach-progress-track {
background: rgba(255,255,255,0.04);
border-radius: 10px;
height: 6px;
width: 100%;
margin: 12px 0 6px 0;
overflow: hidden;
}
.ach-progress-fill {
height: 100%;
background: #00f2ff;
width: 0%;
transition: width 0.6s ease;
}
.ach-card.unlocked .ach-progress-fill {
background: #10e353;
}
.ach-xp-tag {
font-size: 0.75rem;
font-weight: 700;
background: rgba(0, 242, 255, 0.1);
color: #00f2ff;
padding: 2px 8px;
border-radius: 6px;
align-self: flex-start;
margin-top: 10px;
}
.ach-card.unlocked .ach-xp-tag {
background: rgba(16, 227, 83, 0.1);
color: #10e353;
}
</style>
<!-- HEADER / STATS FRAMEWORK -->
<div class='ach-header-card'>
<div>
<h1 style='margin:0 0 6px 0; font-size:2.2rem; font-weight:800; color:var(--text-main, #fff);'>Academy Milestones</h1>
<p style='margin:0; color:var(--text-sub, #8892b0); font-size:1rem;'>Forge your academic legacy through real-time metrics.</p>
</div>
<div style='display:flex; flex-direction:column; align-items:flex-end; gap:6px; min-width:250px;'>
<div style='display:flex; justify-content:space-between; width:100%; font-size:0.9rem; font-weight:700;'>
<span style='color:#00f2ff;' id='ach-level-display'>Level 1 Scholar</span>
<span style='color:var(--text-sub, #8892b0);' id='ach-total-xp'>0 / 1000 XP</span>
</div>
<div class='xp-bar-container'>
<div id='ach-xp-bar' class='xp-bar-fill'></div>
</div>
<span style='font-size:0.8rem; color:var(--text-sub, #8892b0);' id='ach-count-summary'>0 of 9 Achievements Earned</span>
</div>
</div>
<!-- CATEGORY FILTERS -->
<div class='ach-filter-bar'>
<button class='ach-filter-btn active' onclick='filterAchievements(\"all\", this)'>All Trophies</button>
<button class='ach-filter-btn' onclick='filterAchievements(\"progression\", this)'>Progression</button>
<button class='ach-filter-btn' onclick='filterAchievements(\"streaks\", this)'>Streaks & Consistency</button>
<button class='ach-filter-btn' onclick='filterAchievements(\"mastery\", this)'>Cognitive Mastery</button>
<button class='ach-filter-btn' onclick='filterAchievements(\"stamina\", this)'>Stamina & Focus</button>
</div>
<!-- MAIN DYNAMIC TROPHY GRID -->
<div class='ach-grid' id='achievementsGridContainer'>
<!-- Card 1: First Blood -->
<div class='ach-card' data-category='progression' id='ach-first-blood'>
<div>
<div class='ach-badge-wrapper'>🩸</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>First Blood</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Complete your first comprehensive deck review.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-first-blood'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-first-blood'>0/1 Deck</span>
<span id='status-first-blood'>Locked</span>
</div>
<span class='ach-xp-tag'>+150 XP</span>
</div>
</div>
<!-- Card 2: On Fire -->
<div class='ach-card' data-category='streaks' id='ach-on-fire'>
<div>
<div class='ach-badge-wrapper'>🔥</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>On Fire</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Maintain a solid 7-day learning streak loop.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-on-fire'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-on-fire'>0/7 Days</span>
<span id='status-on-fire'>Locked</span>
</div>
<span class='ach-xp-tag'>+300 XP</span>
</div>
</div>
<!-- Card 3: Brainiac -->
<div class='ach-card' data-category='mastery' id='ach-brainiac'>
<div>
<div class='ach-badge-wrapper'>🧠</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Brainiac</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Successfully master 50 cards with perfect recall.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-brainiac'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-brainiac'>0/50 Cards</span>
<span id='status-brainiac'>Locked</span>
</div>
<span class='ach-xp-tag'>+500 XP</span>
</div>
</div>
<!-- Card 4: Pomodoro Overlord -->
<div class='ach-card' data-category='stamina' id='ach-pomo-overlord'>
<div>
<div class='ach-badge-wrapper'></div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Pomodoro Overlord</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Log 500 minutes of deep focus timer blocks.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-pomo-overlord'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-pomo-overlord'>0/500 Min</span>
<span id='status-pomo-overlord'>Locked</span>
</div>
<span class='ach-xp-tag'>+400 XP</span>
</div>
</div>
<!-- Card 5: Scholar -->
<div class='ach-card' data-category='stamina' id='ach-scholar'>
<div>
<div class='ach-badge-wrapper'>📚</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Grand Scholar</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Study for a cumulative total of 100 hours.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-scholar'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-scholar'>0/100 Hours</span>
<span id='status-scholar'>Locked</span>
</div>
<span class='ach-xp-tag'>+1000 XP</span>
</div>
</div>
<!-- Card 6: Perfectionist -->
<div class='ach-card' data-category='mastery' id='ach-perfectionist'>
<div>
<div class='ach-badge-wrapper'>🎯</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Perfectionist</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Score 100% precision on any evaluation session.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-perfectionist'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-perfectionist'>0/1 Session</span>
<span id='status-perfectionist'>Locked</span>
</div>
<span class='ach-xp-tag'>+250 XP</span>
</div>
</div>
<!-- Card 7: Night Owl -->
<div class='ach-card' data-category='streaks' id='ach-night-owl'>
<div>
<div class='ach-badge-wrapper'>🦉</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Night Owl</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Complete a review matrix past midnight (12:00 AM).</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-night-owl'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-night-owl'>0/1 Review</span>
<span id='status-night-owl'>Locked</span>
</div>
<span class='ach-xp-tag'>+200 XP</span>
</div>
</div>
<!-- Card 8: Card Architect -->
<div class='ach-card' data-category='progression' id='ach-architect'>
<div>
<div class='ach-badge-wrapper'>⚡</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Card Architect</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Design and build 100 custom flashcards manually.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-architect'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-architect'>0/100 Cards</span>
<span id='status-architect'>Locked</span>
</div>
<span class='ach-xp-tag'>+350 XP</span>
</div>
</div>
<!-- Card 9: Polyglot -->
<div class='ach-card' data-category='mastery' id='ach-polyglot'>
<div>
<div class='ach-badge-wrapper'>🌍</div>
<h3 style='margin:0 0 4px 0; font-size:1.1rem; color:var(--text-main, #fff);'>Global Polyglot</h3>
<p style='margin:0; font-size:0.85rem; color:var(--text-sub, #8892b0);'>Switch app engine interface languages 3 times.</p>
</div>
<div>
<div class='ach-progress-track'><div class='ach-progress-fill' id='p-fill-polyglot'></div></div>
<div style='display:flex; justify-content:space-between; font-size:0.75rem; color:var(--text-sub, #8892b0);'>
<span id='p-lbl-polyglot'>0/3 Switches</span>
<span id='status-polyglot'>Locked</span>
</div>
<span class='ach-xp-tag'>+150 XP</span>
</div>
</div>
</div>
<!-- AUTOMATIC ACHIEVEMENT CALCULATION ENGINE -->
<script>
function computeLiveAchievements() {
// Mock fallback metrics to load if app state hasn't been written to localStorage yet
const metrics = {
decksCompleted: parseInt(localStorage.getItem('flashsync_stat_decks_done')) || 0,
currentStreak: parseInt(localStorage.getItem('flashsync_stat_streak')) || 0,
cardsMastered: parseInt(localStorage.getItem('flashsync_stat_cards_mastered')) || 0,
pomoMinutes: parseInt(localStorage.getItem('flashsync_stat_pomo_mins')) || 0,
studyHours: parseFloat(localStorage.getItem('flashsync_stat_hours')) || 0.0,
perfectSessions: parseInt(localStorage.getItem('flashsync_stat_perfect_sets')) || 0,
midnightReviews: parseInt(localStorage.getItem('flashsync_stat_night_reviews')) || 0,
cardsCreated: parseInt(localStorage.getItem('flashsync_stat_cards_created')) || 0,
langSwitches: parseInt(localStorage.getItem('flashsync_stat_lang_switches')) || 0
};
// Achievement Configuration Matrix: [Metric value, target, element ID, unit string, XP yield]
const rules = {
'first-blood': [metrics.decksCompleted, 1, 'first-blood', 'Deck', 150],
'on-fire': [metrics.currentStreak, 7, 'on-fire', 'Days', 300],
'brainiac': [metrics.cardsMastered, 50, 'brainiac', 'Cards', 500],
'pomo-overlord': [metrics.pomoMinutes, 500, 'pomo-overlord', 'Min', 400],
'scholar': [Math.floor(metrics.studyHours), 100, 'scholar', 'Hours', 1000],
'perfectionist': [metrics.perfectSessions, 1, 'perfectionist', 'Session', 250],
'night-owl': [metrics.midnightReviews, 1, 'night-owl', 'Review', 200],
'architect': [metrics.cardsCreated, 100, 'architect', 'Cards', 350],
'polyglot': [metrics.langSwitches, 3, 'polyglot', 'Switches', 150]
};
let accumulatedXp = 0;
let unlockedCount = 0;
const totalAchievements = Object.keys(rules).length;
// Loop through rules matrix to modify individual cards dynamically
for (const [key, setup] of Object.entries(rules)) {
let value = setup[0];
const target = setup[1];
const id = setup[2];
const unit = setup[3];
const xpGained = setup[4];
if (value > target) value = target; // Clamp to avoid overflow
const percent = Math.floor((value / target) * 100);
const cardEl = document.getElementById('ach-' + id);
const fillEl = document.getElementById('p-fill-' + id);
const lblEl = document.getElementById('p-lbl-' + id);
const statusEl = document.getElementById('status-' + id);
if (cardEl && fillEl && lblEl && statusEl) {
fillEl.style.width = percent + '%';
lblEl.innerText = value + '/' + target + ' ' + unit;
if (value >= target) {
cardEl.className = 'ach-card unlocked';
statusEl.innerText = 'Unlocked';
statusEl.style.color = '#10e353';
accumulatedXp += xpGained;
unlockedCount++;
} else if (value > 0) {
cardEl.className = 'ach-card in-progress';
statusEl.innerText = percent + '%';
statusEl.style.color = '#00f2ff';
} else {
cardEl.className = 'ach-card locked';
statusEl.innerText = 'Locked';
statusEl.style.color = 'var(--text-sub)';
}
}
}
// Calculate User Level based on flat brackets of 1000 XP
const currentLevel = Math.floor(accumulatedXp / 1000) + 1;
const xpRemainder = accumulatedXp % 1000;
const levelBarPercent = Math.floor((xpRemainder / 1000) * 100);
// Update the header indicators dynamically
const lvlDisp = document.getElementById('ach-level-display');
const xpDisp = document.getElementById('ach-total-xp');
const barDisp = document.getElementById('ach-xp-bar');
const countDisp = document.getElementById('ach-count-summary');
if (lvlDisp) lvlDisp.innerText = 'Level ' + currentLevel + ' Scholar';
if (xpDisp) xpDisp.innerText = xpRemainder + ' / 1000 XP (Total: ' + accumulatedXp + ' XP)';
if (barDisp) barDisp.style.width = levelBarPercent + '%';
if (countDisp) countDisp.innerText = unlockedCount + ' of ' + totalAchievements + ' Achievements Earned';
}
function filterAchievements(category, btnElement) {
// Toggle selected styling button state
document.querySelectorAll('.ach-filter-btn').forEach(b => b.classList.remove('active'));
btnElement.classList.add('active');
// Filter cards visually via dynamic hidden display properties
document.querySelectorAll('.ach-card').forEach(card => {
if (category === 'all' || card.getAttribute('data-category') === category) {
card.style.display = 'flex';
} else {
card.style.display = 'none';
}
});
}
// Initialize engine parsing immediately
document.addEventListener('DOMContentLoaded', computeLiveAchievements);
setTimeout(computeLiveAchievements, 50); // Dynamic routing fallback render
</script>
</div>
"
}
fn calendar_page() -> String {
"<div><h1>Study Calendar</h1><p class='subtitle'>Plan and track your study schedule</p>" <>
"<div class='feature-box'><div id='calendarContainer'></div></div>" <>
"<div class='stats-row'><span class='stats-badge'><i class='fas fa-check-circle' style='color:var(--success);'></i> <strong>18</strong> study days</span><span class='stats-badge'><i class='fas fa-fire' style='color:var(--warning);'></i> <strong>21</strong> day streak</span></div></div>"
}
fn notes_page() -> String {
"<div><h1>Quick Notes</h1><p class='subtitle'>Capture your ideas instantly</p>" <>
"<div class='feature-box'><textarea class='premium-input' id='noteInput' placeholder='Write your notes here...' style='height:200px;'></textarea>" <>
"<div style='display:flex;gap:10px;margin-top:15px;'><button class='premium-button' style='flex:1;' onclick='saveNote()'><i class='fas fa-save'></i> Save Note</button>" <>
"<button class='accent-button' onclick='exportNotes()'><i class='fas fa-download'></i> Export</button></div></div>" <>
"<h2>Recent Notes</h2><div id='savedNotes'><div class='stat-card'><p><strong>Quantum Mechanics</strong> <span class='tag primary'>Today</span></p>" <>
"<p style='color:#888;margin-top:8px;'>Key concepts: superposition, entanglement, wave function collapse...</p></div></div></div>"
}
fn vocabulary_page() -> String {
"<div><h1>Vocabulary Builder</h1><p class='subtitle'>Expand your word bank daily</p>" <>
"<div style='display:flex;gap:15px;margin-bottom:20px;'>" <>
"<button class='premium-button' onclick='learnNewWord()'><i class='fas fa-lightbulb'></i> Learn New Word</button>" <>
"<button class='accent-button' onclick='exportVocabulary()'><i class='fas fa-download'></i> Export Words</button>" <>
"</div>" <>
"<div id='vocabDisplay'>" <>
"<div class='feature-box'><h3>📖 Word of the Day</h3><p style='font-size:1.8rem;color:var(--primary);font-weight:bold;'>Ephemeral</p>" <>
"<p style='color:#888;font-size:1.1rem;'>/ɪˈfem.ər.əl/ <em>adjective</em></p><p style='margin-top:10px;'>Lasting for a very short time.</p>" <>
"<div style='background:rgba(0,242,255,0.05);padding:15px;border-radius:12px;margin-top:15px;'><p><strong>Example:</strong> \"The beauty of cherry blossoms is <em>ephemeral</em>, lasting only a few days.\"</p></div></div>" <>
"</div>" <>
"<div class='grid-2'><div class='stat-card'><h3>Words Learned</h3><p style='font-size:2rem;color:var(--success);' id='wordCount'>47</p></div>" <>
"<div class='stat-card'><h3>Daily Goal</h3><div class='study-progress-bar'><div class='study-progress-fill' style='width:60%;'></div></div><p style='color:#888;font-size:0.9rem;'>3/5 words today</p></div></div></div>"
}
fn pomodoro_page() -> String {
"<div><h1>Pomodoro Timer</h1><p class='subtitle'>25-minute focus sessions with adaptive music</p>" <>
"<div style='text-align:center;padding:40px;'><div id='pomodoroDisplay' style='font-size:5rem;font-weight:900;color:var(--primary);font-variant-numeric:tabular-nums;'>25:00</div>" <>
"<p style='color:#888;margin-top:10px;' id='pomodoroLabel'>Focus session ready</p>" <>
"<div style='display:flex;gap:15px;justify-content:center;margin-top:25px;flex-wrap:wrap;'>" <>
"<button id='pomodoroBtn' class='premium-button' onclick='startPomodoro()'><i class='fas fa-play'></i> Start Session</button>" <>
"<button class='accent-button' onclick='stopPomodoro()'><i class='fas fa-stop'></i> Reset</button>" <>
"<button class='success-button compact-btn' onclick='logTime()'><i class='fas fa-plus'></i> Log 5 min</button></div>" <>
"<div class='stats-row' style='justify-content:center;'><span class='stats-badge'><i class='fas fa-clock'></i> Focus: 25 min</span><span class='stats-badge'><i class='fas fa-coffee'></i> Break: 5 min</span></div>" <>
"<div class='feature-box' style='margin-top:20px;'><h3><i class='fas fa-headphones'></i> Focus Music</h3><div id='musicPlayer'><button id='musicBtn' class='premium-button compact-btn music-btn' onclick='toggleMusic()'><i class='fas fa-play'></i> Play Music</button></div></div>" <>
"</div></div>"
}
pub fn formulas_page() -> String {
"
<div style='max-width: 1200px; margin: 0 auto; font-family: system-ui, -apple-system, sans-serif; color: #fff; padding: 20px;'>
<div style='display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px; margin-bottom: 30px; background: linear-gradient(135deg, rgba(255,255,255,0.03), rgba(255,255,255,0.01)); padding: 25px; border-radius: 16px; border: 1px solid rgba(255,255,255,0.05);'>
<div>
<h1 style='margin: 0; font-size: 2.4rem; font-weight: 800; background: linear-gradient(45deg, #00f2ff, #0077ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -0.5px;'>Academic Formula Vault</h1>
<p style='margin: 6px 0 0 0; color: #8892b0; font-size: 1rem;'>Interactive reference matrix with <span id='vault-count' style='color: #00f2ff; font-weight: bold;'>0</span> equations ready</p>
</div>
<div style='position: relative;'>
<input type='text' id='vaultSearchInput' placeholder='🔍 Search formulas, tags, or symbols...'
style='background: #0b0d12; border: 1px solid rgba(0,242,255,0.25); padding: 12px 20px; border-radius: 10px; color: #fff; width: 320px; font-size: 0.95rem; outline: none; transition: all 0.3s;'>
</div>
</div>
<div style='display: flex; gap: 12px; margin-bottom: 30px; border-bottom: 1px solid rgba(255,255,255,0.06); padding-bottom: 15px; flex-wrap: wrap;'>
<button class='vault-filter-btn' data-target='all' id='btn-all' style='padding: 10px 20px; border-radius: 8px; border: none; background: #00f2ff; color: #000; font-weight: 700; cursor: pointer;'>All Disciplines</button>
<button class='vault-filter-btn' data-target='math' style='padding: 10px 20px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); background: transparent; color: #b5bec9; font-weight: 600; cursor: pointer;'>Mathematics</button>
<button class='vault-filter-btn' data-target='physics' style='padding: 10px 20px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); background: transparent; color: #b5bec9; font-weight: 600; cursor: pointer;'>Physics</button>
<button class='vault-filter-btn' data-target='chemistry' style='padding: 10px 20px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.1); background: transparent; color: #b5bec9; font-weight: 600; cursor: pointer;'>Chemistry</button>
</div>
<div id='vaultFormulaGrid' style='display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 25px;'>
</div>
<script>
const formulaDb = [
// === MATHEMATICS ===
{ subject: 'math', tag: 'Algebra', title: 'Quadratic Formula', note: 'Solves standard second-degree polynomial roots.', eq: 'x = (-b ± √(b² - 4ac)) / 2a' },
{ subject: 'math', tag: 'Calculus', title: 'Derivative Definition', note: 'Rate of change defined via classic limits.', eq: 'df/dx = lim(h→0) [f(x+h) - f(x)] / h' },
{ subject: 'math', tag: 'Geometry', title: 'Sphere Volume', note: 'Calculates complete volume enclosed inside space.', eq: 'V = (4/3) * π * r³' },
{ subject: 'math', tag: 'Trig', title: 'Pythagorean Identity', note: 'Primary trigonometric structural balance.', eq: 'sin²(θ) + cos²(θ) = 1' },
{ subject: 'math', tag: 'Calculus', title: 'Fundamental Theorem', note: 'Connects standard integration directly to derivatives.', eq: '∫[a to b] f(x) dx = F(b) - F(a)' },
{ subject: 'math', tag: 'Logarithms', title: 'Change of Base', note: 'Converts base systems smoothly for calculation.', eq: 'log_a(x) = log_b(x) / log_b(a)' },
{ subject: 'math', tag: 'Algebra', title: 'Binomial Theorem', note: 'Expands integer power terms algebraically.', eq: '(x + y)ⁿ = ∑(k=0 to n) (n choose k) xⁿ⁻ᵏ yᵏ' },
{ subject: 'math', tag: 'Calculus', title: 'Euler Identity', note: 'Links trigonometric frameworks to imaginary metrics.', eq: 'e^(iπ) + 1 = 0' },
{ subject: 'math', tag: 'Geometry', title: 'Pythagorean Theorem', note: 'Sides balance calculation rules for right triangles.', eq: 'a² + b² = c²' },
{ subject: 'math', tag: 'Statistics', title: 'Normal Distribution', note: 'Probability density metric for Gaussian curves.', eq: 'f(x) = (1 / (σ(2π))) * e^(-(x-μ)² / (2σ²))' },
{ subject: 'math', tag: 'Series', title: 'Infinite Geometric Sum', note: 'Convergent balance point calculation format.', eq: 'S = a / (1 - r) [for |r| < 1]' },
{ subject: 'math', tag: 'Vectors', title: 'Dot Product Matrix', note: 'Multiplies vectors to return scaled spatial parameters.', eq: 'A · B = |A||B|cos(θ)' },
{ subject: 'math', tag: 'Vectors', title: 'Cross Product Magnitude', note: 'Calculates area parameters orthogonal to inputs.', eq: '|A × B| = |A||B|sin(θ)' },
{ subject: 'math', tag: 'Complex', title: 'De Moivre Theorem', note: 'Computes exponential integer powers of complex numbers.', eq: '[r(cosθ + isinθ)]ⁿ = rⁿ(cos nθ + isin nθ)' },
{ subject: 'math', tag: 'Calculus', title: 'Chain Rule', note: 'Differentiates complex composite structures.', eq: 'd/dx [f(g(x))] = df/dg * dg/dx' },
{ subject: 'math', tag: 'Calculus', title: 'Integration by Parts', note: 'Breaks down tough product integral systems.', eq: '∫ u dv = uv - ∫ v du' },
{ subject: 'math', tag: 'Matrix', title: '2x2 Determinant', note: 'Determines invertibility state fields.', eq: 'det(A) = ad - bc' },
{ subject: 'math', tag: 'Trig', title: 'Double Angle Sine', note: 'Reduces compounding modular angular spans.', eq: 'sin(2θ) = 2sin(θ)cos(θ)' },
{ subject: 'math', tag: 'Trig', title: 'Double Angle Cosine', note: 'Alternative geometric coordinate breakdown form.', eq: 'cos(2θ) = cos²(θ) - sin²(θ)' },
{ subject: 'math', tag: 'Calculus', title: 'Taylor Series Expansion', note: 'Approximates complex differentiable lines locally.', eq: 'f(x) = ∑ [fⁿ(a) / n!] * (x - a)ⁿ' },
{ subject: 'math', tag: 'Probability', title: 'Bayes Theorem', note: 'Calculates updated conditional event odds.', eq: 'P(A|B) = [P(B|A) * P(A)] / P(B)' },
{ subject: 'math', tag: 'Probability', title: 'Combination Count', note: 'Finds unique selection counts ignoring layout orders.', eq: 'nCr = n! / (r!(n - r)!)' },
{ subject: 'math', tag: 'Probability', title: 'Permutation Count', note: 'Tracks total positional permutations available.', eq: 'nPr = n! / (n - r)!' },
{ subject: 'math', tag: 'Geometry', title: 'Circle Perimeter', note: 'Total track distance around normal curves.', eq: 'C = 2 * π * r' },
{ subject: 'math', tag: 'Geometry', title: 'Circle Area', note: 'Calculates complete surface bounding tracking metrics.', eq: 'A = π * r²' },
{ subject: 'math', tag: 'Geometry', title: 'Cylinder Surface Area', note: 'Enclosing boundary space logic calculation.', eq: 'A = 2πrh + 2πr²' },
{ subject: 'math', tag: 'Geometry', title: 'Cone Volume', note: 'Space constraints tracking calculation metrics.', eq: 'V = (1/3) * π * r² * h' },
{ subject: 'math', tag: 'Stats', title: 'Sample Mean', note: 'Central balance location estimator equations.', eq: 'x̄ = (∑ x_i) / n' },
{ subject: 'math', tag: 'Stats', title: 'Variance (Sample)', note: 'Tracks dispersion metrics relative to center line.', eq: 's² = ∑(x_i - x̄)² / (n - 1)' },
{ subject: 'math', tag: 'Algebra', title: 'Arithmetic Sequence Term', note: 'Finds values at any linear offset indexing location.', eq: 'a_n = a_1 + (n - 1)d' },
{ subject: 'math', tag: 'Algebra', title: 'Geometric Sequence Term', note: 'Finds values at exponential scaling offsets.', eq: 'a_n = a_1 * r^(n-1)' },
{ subject: 'math', tag: 'Logarithms', title: 'Product Property', note: 'Splits compound arguments into summation terms.', eq: 'log(xy) = log(x) + log(y)' },
{ subject: 'math', tag: 'Logarithms', title: 'Quotient Property', note: 'Simplifies divisional fractions scaling metrics.', eq: 'log(x/y) = log(x) - log(y)' },
// === PHYSICS ===
{ subject: 'physics', tag: 'Relativity', title: 'Mass-Energy Equivalence', note: 'Relates rest mass directly to baseline energy payloads.', eq: 'E = mc²' },
{ subject: 'physics', tag: 'Waves', title: 'Wave Velocity Framework', note: 'Tracks propagation boundaries across medium lines.', eq: 'v = f * λ' },
{ subject: 'physics', tag: 'Electrics', title: 'Ohm Method Law', note: 'Balances loop potential drops relative to flow impedances.', eq: 'V = I * R' },
{ subject: 'physics', tag: 'Dynamics', title: 'Newton Second Law', note: 'Forces tracking acceleration velocity profiles.', eq: 'F = ma' },
{ subject: 'physics', tag: 'Kinematics', title: 'Displacement Balance', note: 'Tracks positional paths given constant adjustments.', eq: 'd = v_i*t + 0.5*a*t²' },
{ subject: 'physics', tag: 'Kinematics', title: 'Timeless Velocity', note: 'Calculates speeds across distance without tracking run times.', eq: 'v_f² = v_i² + 2ad' },
{ subject: 'physics', tag: 'Energy', title: 'Kinetic Energy Factor', note: 'Energy tracking for moving particle dynamics.', eq: 'KE = 0.5 * m * v²' },
{ subject: 'physics', tag: 'Energy', title: 'Gravitational Potential', note: 'Stored spatial elevation system parameters.', eq: 'PE = m * g * h' },
{ subject: 'physics', tag: 'Gravity', title: 'Universal Gravitation', note: 'Mutual pulling force fields shared between point masses.', eq: 'F = G(m₁m₂) / r²' },
{ subject: 'physics', tag: 'Quantum', title: 'Planck-Einstein Rule', note: 'Determines distinct photon discrete scale boundaries.', eq: 'E = hf' },
{ subject: 'physics', tag: 'Momentum', title: 'Linear Momentum Metric', note: 'Quantifies absolute translational inertia paths.', eq: 'p = m * v' },
{ subject: 'physics', tag: 'Circuits', title: 'Electrical Power Load', note: 'Rate of energy utilization dissipation matrix.', eq: 'P = I * V' },
{ subject: 'physics', tag: 'Electrostatics', title: 'Coulomb Charge Law', note: 'Repulsion or attraction metrics between charges.', eq: 'F = k_e * (|q₁q₂| / r²)' },
{ subject: 'physics', tag: 'Dynamics', title: 'Centripetal Force', note: 'Maintains uniform angular vector rotations.', eq: 'F_c = (m * v²) / r' },
{ subject: 'physics', tag: 'Thermodynamics', title: 'First Law System Metric', note: 'Internal system balance tracking metric variables.', eq: 'ΔU = Q - W' },
{ subject: 'physics', tag: 'Optics', title: 'Snell Refraction Law', note: 'Tracks direction alterations entering fresh phases.', eq: 'n₁sin(θ₁) = n₂sin(θ₂)' },
{ subject: 'physics', tag: 'Fluids', title: 'Hydrostatic Pressure', note: 'Tracks scaling load gradients inside fluids.', eq: 'P = ρ * g * h' },
{ subject: 'physics', tag: 'Rotational', title: 'Torque Formula', note: 'Rotational power capability calculations.', eq: 'τ = r * F * sin(θ)' },
{ subject: 'physics', tag: 'Waves', title: 'Frequency/Period Conversion', note: 'Tracks structural cycle timing constraints.', eq: 'f = 1 / T' },
{ subject: 'physics', tag: 'Fluids', title: 'Bernoulli Equation', note: 'Energy maintenance rules down running fluid pipes.', eq: 'P + 0.5ρv² + ρgh = Constant' },
{ subject: 'physics', tag: 'Electrics', title: 'Capacitance Calculation', note: 'Charge storage capacity tracking parameters.', eq: 'Q = C * V' },
{ subject: 'physics', tag: 'Magnetism', title: 'Magnetic Force (Wire)', note: 'Tracks deflection profiles on running wires.', eq: 'F = I * L * B * sin(θ)' },
{ subject: 'physics', tag: 'Quantum', title: 'De Broglie Wavelength', note: 'Assigns wave configurations to traveling massive items.', eq: 'λ = h / p' },
{ subject: 'physics', tag: 'Special Relativity', title: 'Time Dilation Index', note: 'Tracks clock parameter shifts near light speed bounds.', eq: 't = t₀ / √(1 - v²/c²)' },
{ subject: 'physics', tag: 'Special Relativity', title: 'Lorentz Length Shift', note: 'Tracks visual spatial adjustments along velocity planes.', eq: 'L = L₀ * √(1 - v²/c²)' },
{ subject: 'physics', tag: 'Rotational', title: 'Angular Momentum', note: 'Inertial spin configuration metric checks.', eq: 'L = I * ω' },
{ subject: 'physics', tag: 'Springs', title: 'Hooke Elastic Law', note: 'Restoration force fields tracking spring lengths.', eq: 'F = -k * x' },
{ subject: 'physics', tag: 'Springs', title: 'Elastic Stored Energy', note: 'Potential scaling metric across tension states.', eq: 'PE_elastic = 0.5 * k * x²' },
{ subject: 'physics', tag: 'Thermodynamics', title: 'Efficiency Parameters', note: 'Tracks output profiles relative to input configurations.', eq: 'η = W / Q_hot' },
{ subject: 'physics', tag: 'Thermodynamics', title: 'Entropy Variations', note: 'Tracks reversible system configuration metrics.', eq: 'ΔS = ΔQ / T' },
// === CHEMISTRY ===
{ subject: 'chemistry', tag: 'Gases', title: 'Ideal Gas Law', note: 'Perfect correlation balance tracking metric calculations.', eq: 'PV = nRT' },
{ subject: 'chemistry', tag: 'Acids', title: 'pH Definition Matrix', note: 'Measures standard hydronium ion concentration indexes.', eq: 'pH = -log[H⁺]' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Molarity Metric', note: 'Tracks concentration scaling limits across solution tracking.', eq: 'M = n / V' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'Arrhenius Baseline', note: 'Thermal activation dependencies for tracking rates.', eq: 'k = A * e^(-Ea / RT)' },
{ subject: 'chemistry', tag: 'Moles', title: 'The Mole Equation', note: 'Translates raw mass metrics to explicit unit particle metrics.', eq: 'n = m / MM' },
{ subject: 'chemistry', tag: 'Thermodynamics', title: 'Gibbs Free Energy', note: 'Determines reaction spontaneity limits securely.', eq: 'ΔG = ΔH - TΔS' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Dilution Balance Check', note: 'Maintains consistent solute mass variables.', eq: 'M₁V₁ = M₂V₂' },
{ subject: 'chemistry', tag: 'Equilibrium', title: 'Equilibrium Constant', note: 'Product vs reactant ratio balance metrics.', eq: 'K_eq = [Products]ᶜ / [Reactants]ᵃ' },
{ subject: 'chemistry', tag: 'Acids', title: 'Henderson-Hasselbalch', note: 'Calculates buffer system configuration variables.', eq: 'pH = pKa + log([A⁻] / [HA])' },
{ subject: 'chemistry', tag: 'Electrochemistry', title: 'Nernst Cell Metric', note: 'Tracks cell voltage under non-standard configurations.', eq: 'E = E° - (RT/nF) * ln(Q)' },
{ subject: 'chemistry', tag: 'Thermochem', title: 'Calorimetry Metric', note: 'Tracks heat exchange transformations across samples.', eq: 'q = m * c * ΔT' },
{ subject: 'chemistry', tag: 'Gases', title: 'Boyle Gas Law', note: 'Inverse relation parameters under steady heating setups.', eq: 'P₁V₁ = P₂V₂' },
{ subject: 'chemistry', tag: 'Gases', title: 'Charles Gas Law', note: 'Thermal volume expansion rates validation setups.', eq: 'V₁/T₁ = V₂/T₂' },
{ subject: 'chemistry', tag: 'Gases', title: 'Dalton Partial Pressures', note: 'Aggregates compound atmospheric pressures easily.', eq: 'P_total = P₁ + P₂ + P₃ + ...' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'Rate Law Format', note: 'Tracks specific item collision dependencies.', eq: 'Rate = k * [A]ˣ * [B]ʸ' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'First-Order Half-Life', note: 'Decay timeline estimations for calculations.', eq: 't_1/2 = 0.693 / k' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'Zero-Order Half-Life', note: 'Concentration decay configurations layout structures.', eq: 't_1/2 = [A]₀ / (2k)' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'Second-Order Half-Life', note: 'Alternative molecular collision calculations structures.', eq: 't_1/2 = 1 / (k * [A]₀)' },
{ subject: 'chemistry', tag: 'Electrochemistry', title: 'Faraday Charge Law', note: 'Relates electrolytic material yield to current logs.', eq: 'm = (Q * MM) / (z * F)' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Boiling Point Elevation', note: 'Tracks shifts induced by non-volatile solutes.', eq: 'ΔT_b = i * K_b * m' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Freezing Point Drop', note: 'Tracks threshold suppression factors accurately.', eq: 'ΔT_f = i * K_f * m' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Osmotic Pressure Matrix', note: 'Pressure tracking metrics passing semi-permeable lines.', eq: 'π = i * M * R * T' },
{ subject: 'chemistry', tag: 'Moles', title: 'Mass Percentage Index', note: 'Elemental purity assessment computations.', eq: 'Mass % = (Mass of Solute / Total Mass) * 100' },
{ subject: 'chemistry', tag: 'Thermochem', title: 'Standard Enthalpy Match', note: 'Summation of product formation profiles variables.', eq: 'ΔH°_rxn = ∑ nΔH°_f(products) - ∑ mΔH°_f(reactants)' },
{ subject: 'chemistry', tag: 'Atomistry', title: 'Rydberg Spectral Lines', note: 'Tracks light emission bands shifting energy states.', eq: '1/λ = R_H * (1/n₁² - 1/n₂²)' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Henry Gas Law', note: 'Gas solubilization metrics under pressure caps.', eq: 'C = k * P_gas' },
{ subject: 'chemistry', tag: 'Solutions', title: 'Raoult Vapor Law', note: 'Tracks vapor pressures relative to mole selections.', eq: 'P_solution = X_solvent * P°_solvent' },
{ subject: 'chemistry', tag: 'Atomistry', title: 'Formal Charge Balance', note: 'Evaluates molecular electron arrangement maps.', eq: 'FC = Valence - [NonBonding + 0.5 * Bonding]' },
{ subject: 'chemistry', tag: 'Acids', title: 'Water Autoionization', note: 'Thermal equilibrium index configuration rules.', eq: 'K_w = [H⁺][OH⁻] = 1.0 × 10⁻¹⁴' },
{ subject: 'chemistry', tag: 'Acids', title: 'Logarithmic Water Index', note: 'Balances scaling values cleanly across solution logs.', eq: 'pH + pOH = 14' },
{ subject: 'chemistry', tag: 'Equilibrium', title: 'Reaction Quotient', note: 'Tracks momentary composition values pathing ahead.', eq: 'Q = [Products]ⁿ / [Reactants]ᵐ' },
{ subject: 'chemistry', tag: 'Gases', title: 'Root-Mean-Square Speed', note: 'Calculates specific thermal particle movement speeds.', eq: 'v_rms = √(3RT / M)' },
{ subject: 'chemistry', tag: 'Gases', title: 'Graham Effusion Law', note: 'Gas transfer speed differences based on weight scales.', eq: 'Rate₁ / Rate₂ = √(M₂ / M₁)' },
{ subject: 'chemistry', tag: 'Kinetics', title: 'Integrated First-Order', note: 'Tracks concentration changes over elapsed time values.', eq: 'ln([A]_t / [A]₀) = -kt' }
];
function renderVaultGrid(filterSubject = 'all', searchQuery = '') {
const grid = document.getElementById('vaultFormulaGrid');
if (!grid) return;
let html = '';
let matchedCount = 0;
formulaDb.forEach(f => {
const matchesSubject = (filterSubject === 'all' || f.subject === filterSubject);
const searchContent = (f.title + ' ' + f.note + ' ' + f.eq + ' ' + f.tag).toLowerCase();
const matchesSearch = searchContent.includes(searchQuery.toLowerCase().trim());
if (matchesSubject && matchesSearch) {
matchedCount++;
let subjectColor = '#00f2ff';
let subjectBg = 'rgba(0,242,255,0.1)';
if (f.subject === 'physics') { subjectColor = '#ff4a4a'; subjectBg = 'rgba(255,74,74,0.1)'; }
if (f.subject === 'chemistry') { subjectColor = '#10e353'; subjectBg = 'rgba(16,227,83,0.1)'; }
html += \"<div class='vault-card' style='background: #111318; border: 1px solid rgba(255,255,255,0.05); border-radius: 14px; padding: 20px; position: relative; display: flex; flex-direction: column; justify-content: space-between; transition: all 0.3s;'>\" +
\"<div>\" +
\"<span style='background: \" + subjectBg + \"; color: \" + subjectColor + \"; font-size: 0.65rem; padding: 4px 10px; border-radius: 6px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; position: absolute; top: 20px; right: 20px;'>\" + f.tag + \"</span>\" +
\"<h3 style='margin: 0 0 6px 0; font-size: 1.2rem; color: #fff; padding-right: 90px;'>\" + f.title + \"</h3>\" +
\"<p style='margin: 0 0 16px 0; font-size: 0.8rem; color: #8892b0;'>\" + f.note + \"</p>\" +
\"</div>\" +
\"<div class='vault-eq-display' onclick='copyFormulaToClipboard(this)' style='background: rgba(0,0,0,0.25); padding: 14px; border-radius: 8px; font-family: monospace; color: \" + subjectColor + \"; font-size: 1.15rem; text-align: center; cursor: pointer; border: 1px solid transparent; transition: all 0.2s;' title='Click to Copy'>\" + f.eq + \"</div>\" +
\"</div>\";
}
});
grid.innerHTML = html || \"<div style='grid-column: 1/-1; text-align: center; color: #666; padding: 4px; font-style: italic;'>No matching formulas found.</div>\";
document.getElementById('vault-count').innerText = matchedCount;
}
setTimeout(function() {
let currentSubject = 'all';
let currentSearch = '';
const sInput = document.getElementById('vaultSearchInput');
if (sInput) {
sInput.addEventListener('input', function() {
currentSearch = this.value;
renderVaultGrid(currentSubject, currentSearch);
});
}
document.querySelectorAll('.vault-filter-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('.vault-filter-btn').forEach(b => {
b.style.background = 'transparent';
b.style.color = '#b5bec9';
b.style.border = '1px solid rgba(255,255,255,0.1)';
});
currentSubject = this.getAttribute('data-target');
if (currentSubject === 'math') { this.style.background = '#00f2ff'; this.style.color = '#000'; }
else if (currentSubject === 'physics') { this.style.background = '#ff4a4a'; this.style.color = '#fff'; }
else if (currentSubject === 'chemistry') { this.style.background = '#10e353'; this.style.color = '#000'; }
else { this.style.background = '#00f2ff'; this.style.color = '#000'; }
renderVaultGrid(currentSubject, currentSearch);
});
});
renderVaultGrid('all', '');
}, 150);
function copyFormulaToClipboard(element) {
var text = element.innerText;
navigator.clipboard.writeText(text).then(function() {
var originalColor = element.style.color;
element.style.color = '#fff';
element.innerText = '✨ Copied!';
setTimeout(function() {
element.style.color = originalColor;
element.innerText = text;
}, 800);
});
}
</script>
<style>
.vault-eq-display:hover {
border-color: rgba(255,255,255,0.15) !important;
background: rgba(255,255,255,0.02) !important;
}
.vault-card {
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}
.vault-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
border-color: rgba(255,255,255,0.12) !important;
}
</style>
</div>
"
}
fn suggestions_page() -> String {
"<div><h1>Smart Suggestions</h1><p class='subtitle'>AI-curated learning recommendations</p>" <>
"<div class='grid-2'>" <>
"<div class='stat-card'><h3>Review Weak Spots</h3><p style='color:#888;'>Thermodynamics needs attention</p><span class='tag warning'>67% Mastery</span>" <>
"<button class='accent-button' style='width:100%;margin-top:15px;' onclick='startReview(\"Thermodynamics\")'>Start Review</button></div>" <>
"<div class='stat-card'><h3>Next Topic</h3><p style='color:#888;'>Quantum Mechanics Part 2</p><span class='tag primary'>Recommended</span>" <>
"<button class='premium-button' style='width:100%;margin-top:15px;' onclick='showToast(\"Exploring new topic!\",\"success\")'>Explore</button></div></div>" <>
"<h2>Daily Mission</h2><div class='feature-box'><div style='display:flex;justify-content:space-between;'>" <>
"<span><i class='fas fa-bolt' style='color:var(--warning);'></i> Power Score</span><span style='color:var(--primary);font-weight:bold;'>98</span></div>" <>
"<div class='study-progress-bar' style='margin:10px 0;'><div class='study-progress-fill' style='width:98%;'></div></div>" <>
"<p style='color:#888;font-size:0.9rem;'>Complete: 25m concept review, 15m active recall, 10m exam drill</p></div></div>"
}
pub fn profile_page() -> String {
"
<div style='max-width: 1200px; margin: 0 auto; font-family: system-ui, -apple-system, sans-serif; color: #fff; padding: 20px;'>
<style>
.prof-card {
background: #111318;
border: 1px solid rgba(255,255,255,0.05);
border-radius: 20px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.prof-card:hover {
border-color: rgba(0,242,255,0.25);
transform: translateY(-4px);
box-shadow: 0 15px 35px rgba(0,242,255,0.08);
}
.badge-pill {
background: rgba(255,255,255,0.03);
border: 1px solid rgba(255,255,255,0.06);
padding: 8px 16px;
border-radius: 30px;
font-size: 0.85rem;
display: inline-flex;
align-items: center;
gap: 8px;
color: #b5bec9;
}
.skill-tag {
background: rgba(0, 242, 255, 0.05);
color: #00f2ff;
border: 1px solid rgba(0, 242, 255, 0.12);
padding: 6px 14px;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.3px;
}
.achieve-item {
display: flex;
align-items: center;
gap: 15px;
background: rgba(255,255,255,0.02);
padding: 12px 16px;
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.04);
transition: all 0.2s ease;
}
.achieve-item.unlocked {
border-color: rgba(16,227,83,0.15);
background: linear-gradient(90deg, rgba(16,227,83,0.02), transparent);
}
.achieve-item.locked {
opacity: 0.5;
filter: grayscale(0.4);
}
.action-btn {
background: linear-gradient(135deg, #00f2ff, #0077ff);
color: #000;
border: none;
padding: 12px 22px;
border-radius: 10px;
font-weight: 700;
font-size: 0.9rem;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
transition: all 0.2s ease;
}
.action-btn:hover {
transform: scale(1.02);
box-shadow: 0 0 20px rgba(0, 242, 255, 0.35);
}
.deadline-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
}
.deadline-row:last-child {
border-bottom: none;
}
textarea.notepad {
width: 100%;
height: 110px;
background: #0b0d12;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
color: #fff;
padding: 12px;
font-size: 0.9rem;
resize: none;
outline: none;
font-family: inherit;
transition: all 0.2s ease;
}
textarea.notepad:focus {
border-color: #00f2ff;
box-shadow: 0 0 10px rgba(0,242,255,0.1);
}
</style>
<div class='prof-card' style='display: flex; gap: 35px; align-items: center; flex-wrap: wrap; margin-bottom: 30px; background: linear-gradient(135deg, #111318 0%, #151922 100%);'>
<div style='position: absolute; top: -50px; right: -50px; width: 200px; height: 200px; background: radial-gradient(circle, rgba(0,242,255,0.08) 0%, transparent 70%); pointer-events: none;'></div>
<div style='display: flex; flex-direction: column; align-items: center; gap: 12px; z-index: 2;'>
<div id='profileAvatarContainer' style='width: 140px; height: 140px; border-radius: 50%; background: linear-gradient(135deg, #00f2ff, #0077ff); display: flex; align-items: center; justify-content: center; font-size: 4rem; font-weight: bold; color: #000; box-shadow: 0 0 30px rgba(0,242,255,0.2); border: 5px solid #111318; transition: all 0.3s ease;'>
</div>
<button class='action-btn' onclick='handleAvatarGeneration()' style='padding: 6px 14px; font-size: 0.75rem; border-radius: 8px;'>
Re-Roll Identity
</button>
</div>
<div style='flex: 1; min-width: 280px; z-index: 2;'>
<div style='display: flex; align-items: center; gap: 15px; flex-wrap: wrap;'>
<h2 style='margin: 0; font-size: 2.4rem; font-weight: 800; background: linear-gradient(45deg, #fff, #b5bec9); -webkit-background-clip: text; -webkit-text-fill-color: transparent;'>Academic Scholar</h2>
<span style='background: rgba(16,227,83,0.1); color: #10e353; border: 1px solid rgba(16,227,83,0.2); font-size: 0.75rem; padding: 4px 12px; border-radius: 6px; font-weight: 800; text-transform: uppercase;'>Grandmaster Rank</span>
</div>
<p style='margin: 6px 0 18px 0; color: #8892b0; font-size: 1.1rem;'>Computer Science & Applied Mathematics Undergraduate</p>
<div style='display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 20px;'>
<span class='badge-pill'><i class='fas fa-calendar-alt' style='color:#00f2ff;'></i> Joined March 2026</span>
<span class='badge-pill'><i class='fas fa-graduation-cap' style='color:#9d4edd;'></i> Level 14 Advanced Learner</span>
<span class='badge-pill'><i class='fas fa-university' style='color:#ffaa00;'></i> Alpha Cohort Matrix</span>
</div>
<div>
<div style='display: flex; justify-content: space-between; font-size: 0.85rem; color: #8892b0; margin-bottom: 6px;'>
<span>Experience Points Metric to Level 15</span>
<span style='color: #00f2ff; font-weight: bold;'>8,450 / 10,000 XP (84%)</span>
</div>
<div style='width: 100%; height: 10px; background: rgba(255,255,255,0.04); border-radius: 6px; overflow: hidden; border: 1px solid rgba(255,255,255,0.05);'>
<div style='width: 84.5%; height: 100%; background: linear-gradient(90deg, #0077ff, #00f2ff); border-radius: 6px;'></div>
</div>
</div>
</div>
</div>
<div style='display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 25px;'>
<div class='prof-card' style='display: flex; flex-direction: column; gap: 15px;'>
<h3 style='margin: 0 0 5px 0; font-size: 1.25rem; border-bottom: 1px solid rgba(255,255,255,0.06); padding-bottom: 12px; color: #fff; display: flex; align-items: center; gap: 10px;'>
<span></span> Learning Velocity Metrics
</h3>
<div style='display: flex; align-items: center; justify-content: space-between; background: rgba(0,0,0,0.15); padding: 14px 20px; border-radius: 12px;'>
<div style='display: flex; align-items: center; gap: 14px;'>
<span style='font-size: 1.8rem;'></span>
<div>
<div style='font-size: 0.8rem; color: #8892b0;'>Current Daily Streak</div>
<div style='font-size: 1.25rem; font-weight: 700; color: #ffaa00;'>21 Days Active</div>
</div>
</div>
<span style='font-size: 0.75rem; background: rgba(255,170,0,0.1); color: #ffaa00; padding: 4px 10px; border-radius: 6px; font-weight: bold;'>Top 3%</span>
</div>
<div style='display: flex; align-items: center; justify-content: space-between; background: rgba(0,0,0,0.15); padding: 14px 20px; border-radius: 12px;'>
<div style='display: flex; align-items: center; gap: 14px;'>
<span style='font-size: 1.8rem;'></span>
<div>
<div style='font-size: 0.8rem; color: #8892b0;'>Total Focal Window</div>
<div style='font-size: 1.25rem; font-weight: 700; color: #fff;'>156 Minutes Studied</div>
</div>
</div>
<span style='font-size: 0.75rem; background: rgba(0,242,255,0.1); color: #00f2ff; padding: 4px 10px; border-radius: 6px; font-weight: bold;'>This Week</span>
</div>
<div style='display: flex; align-items: center; justify-content: space-between; background: rgba(0,0,0,0.15); padding: 14px 20px; border-radius: 12px;'>
<div style='display: flex; align-items: center; gap: 14px;'>
<span style='font-size: 1.8rem;'></span>
<div>
<div style='font-size: 0.8rem; color: #8892b0;'>Memory Synthesizations</div>
<div style='font-size: 1.25rem; font-weight: 700; color: #10e353;'>312 Cards Mastered</div>
</div>
</div>
<span style='font-size: 0.75rem; background: rgba(16,227,83,0.1); color: #10e353; padding: 4px 10px; border-radius: 6px; font-weight: bold;'>94% Correct</span>
</div>
</div>
<div class='prof-card' style='display: flex; flex-direction: column; justify-content: space-between;'>
<div>
<h3 style='margin: 0 0 15px 0; font-size: 1.25rem; border-bottom: 1px solid rgba(255,255,255,0.06); padding-bottom: 12px; color: #fff; display: flex; align-items: center; gap: 10px;'>
<span></span> Weekly Target Analyzer
</h3>
<div style='display: flex; align-items: center; gap: 25px; margin: 15px 0;'>
<div style='position: relative; width: 80px; height: 80px;'>
<svg style='transform: rotate(-90deg); width: 80px; height: 80px;'>
<circle cx='40' cy='40' r='34' stroke='rgba(255,255,255,0.05)' stroke-width='6' fill='transparent'/>
<circle cx='40' cy='40' r='34' stroke='#00f2ff' stroke-width='6' fill='transparent'
stroke-dasharray='213.6' stroke-dashoffset='64.08'/>
</svg>
<div style='position: absolute; top:0; left:0; width:80px; height:80px; display:flex; align-items:center; justify-content:center; font-size:1.1rem; font-weight:bold;'>70%</div>
</div>
<div>
<h4 style='margin:0 0 4px 0; font-size:1.1rem;'>Study Objective Progress</h4>
<p style='margin:0; color:#8892b0; font-size:0.85rem;'>You have recorded 7.0 out of 10 targeted hours for this week matrix loop.</p>
</div>
</div>
</div>
<div style='display: flex; flex-direction: column; gap: 12px; margin-top: 10px;'>
<div>
<div style='display: flex; justify-content: space-between; font-size: 0.85rem; margin-bottom: 5px;'>
<span>Pure Mathematics Matrix Competency</span>
<span style='color: #00f2ff;'>92%</span>
</div>
<div style='width: 100%; height: 6px; background: rgba(255,255,255,0.04); border-radius: 3px;'>
<div style='width: 92%; height: 100%; background: #00f2ff; border-radius: 3px;'></div>
</div>
</div>
<div>
<div style='display: flex; justify-content: space-between; font-size: 0.85rem; margin-bottom: 5px;'>
<span>Quantum & Classical Physics Mechanics</span>
<span style='color: #ff4a4a;'>78%</span>
</div>
<div style='width: 100%; height: 6px; background: rgba(255,255,255,0.04); border-radius: 3px;'>
<div style='width: 78%; height: 100%; background: #ff4a4a; border-radius: 3px;'></div>
</div>
</div>
</div>
</div>
<div class='prof-card'>
<h3 style='margin: 0 0 15px 0; font-size: 1.25rem; border-bottom: 1px solid rgba(255,255,255,0.06); padding-bottom: 12px; color: #fff; display: flex; align-items: center; gap: 10px;'>
<span></span> Medals & Achievements
</h3>
<div style='display: flex; flex-direction: column; gap: 10px;'>
<div class='achieve-item unlocked'>
<span style='font-size: 1.6rem;'></span>
<div>
<div style='font-size: 0.9rem; font-weight: bold; color: #fff;'>Night Owl Prodigy</div>
<div style='font-size: 0.75rem; color: #8892b0;'>Completed 50 reviews after 11:00 PM.</div>
</div>
</div>
<div class='achieve-item unlocked'>
<span style='font-size: 1.6rem;'></span>
<div>
<div style='font-size: 0.9rem; font-weight: bold; color: #fff;'>Flawless Synthesis</div>
<div style='font-size: 0.75rem; color: #8892b0;'>Achieved a 100% test accuracy rating run.</div>
</div>
</div>
<div class='achieve-item locked'>
<span style='font-size: 1.6rem;'></span>
<div>
<div style='font-size: 0.9rem; font-weight: bold; color: #666;'>Century Marathoner</div>
<div style='font-size: 0.75rem; color: #555;'>Maintain an active streak log for 100 days.</div>
</div>
</div>
</div>
</div>
<div class='prof-card'>
<h3 style='margin: 0 0 15px 0; font-size: 1.25rem; border-bottom: 1px solid rgba(255,255,255,0.06); padding-bottom: 12px; color: #fff; display: flex; align-items: center; gap: 10px;'>
<span></span> Priority Term Deck
</h3>
<div style='display: flex; flex-direction: column;'>
<div class='deadline-row'>
<div>
<div style='font-size: 0.9rem; font-weight: 600;'>Linear Algebra Core Midterm</div>
<div style='font-size: 0.75rem; color: #ff4a4a;'><i class='fas fa-exclamation-triangle'></i> Critical Priority Boundary</div>
</div>
<span style='font-size: 0.8rem; background: rgba(255,74,74,0.1); color: #ff4a4a; padding: 4px 8px; border-radius: 6px;'>In 2 Days</span>
</div>
<div class='deadline-row'>
<div>
<div style='font-size: 0.9rem; font-weight: 600;'>Thermodynamics Deck Review</div>
<div style='font-size: 0.75rem; color: #8892b0;'>Self-assessment target benchmark</div>
</div>
<span style='font-size: 0.8rem; background: rgba(0,242,255,0.1); color: #00f2ff; padding: 4px 8px; border-radius: 6px;'>In 5 Days</span>
</div>
<div class='deadline-row'>
<div>
<div style='font-size: 0.9rem; font-weight: 600;'>Organic Molecules Assignment</div>
<div style='font-size: 0.75rem; color: #8892b0;'>Completion structural log execution</div>
</div>
<span style='font-size: 0.8rem; background: rgba(255,255,255,0.05); color: #b5bec9; padding: 4px 8px; border-radius: 6px;'>June 24</span>
</div>
</div>
</div>
<div class='prof-card' style='grid-column: 1 / -1;'>
<h3 style='margin: 0 0 12px 0; font-size: 1.25rem; color: #fff; display: flex; align-items: center; justify-content: space-between;'>
<span style='display: flex; align-items: center; gap: 10px;'><span></span> Fast Reference Academic Notepad</span>
<span id='noteSavedStatus' style='font-size: 0.75rem; color: #10e353; opacity: 0; transition: opacity 0.3s ease;'> Autosaved to System memory</span>
</h3>
<textarea id='dashboardScratchpad' class='notepad' oninput='handleNotepadInput()' placeholder='Type temporary class equations, syllabus criteria, or quick ideas here...'></textarea>
<div style='display: flex; justify-content: space-between; align-items: center; margin-top: 10px;'>
<p style='margin:0; font-size: 0.75rem; color:#8892b0;'>This scratchpad updates immediately and persists automatically across sessions.</p>
<div style='display: flex; gap: 8px;'>
<span class='skill-tag'>Computer Science</span>
<span class='skill-tag'>Linear Algebra</span>
<span class='skill-tag'>Algorithms</span>
</div>
</div>
</div>
</div>
<script>
const avatarGradients = [
'linear-gradient(135deg, #00f2ff, #0077ff)',
'linear-gradient(135deg, #ff4a4a, #ffaa00)',
'linear-gradient(135deg, #10e353, #0077ff)',
'linear-gradient(135deg, #9d4edd, #ff007f)',
'linear-gradient(135deg, #ffaa00, #9d4edd)'
];
const avatarGlyphs = ['🎓', '🔬', '💻', '📐', '🧠', '🌌', '🚀', '🔮'];
function applySavedProfileState() {
// 1. Recover Avatar Configurations
const container = document.getElementById('profileAvatarContainer');
if (container) {
const savedGradient = localStorage.getItem('flashsync_avatar_grad');
const savedGlyph = localStorage.getItem('flashsync_avatar_glyph');
if (savedGradient && savedGlyph) {
container.style.background = savedGradient;
container.innerText = savedGlyph;
}
}
// 2. Recover Notepad Session State Logs
const pad = document.getElementById('dashboardScratchpad');
if (pad) {
const savedNotes = localStorage.getItem('flashsync_dashboard_notes');
if (savedNotes) {
pad.value = savedNotes;
}
}
}
function handleAvatarGeneration() {
const container = document.getElementById('profileAvatarContainer');
if (!container) return;
const randomGrad = avatarGradients[Math.floor(Math.random() * avatarGradients.length)];
const randomGlyph = avatarGlyphs[Math.floor(Math.random() * avatarGlyphs.length)];
container.style.background = randomGrad;
container.innerText = randomGlyph;
localStorage.setItem('flashsync_avatar_grad', randomGrad);
localStorage.setItem('flashsync_avatar_glyph', randomGlyph);
}
let saveDebounceTimeout;
function handleNotepadInput() {
const pad = document.getElementById('dashboardScratchpad');
if (!pad) return;
localStorage.setItem('flashsync_dashboard_notes', pad.value);
// Show status feedback visual trigger with clean debouncer loops
const statusEl = document.getElementById('noteSavedStatus');
if (statusEl) {
statusEl.style.opacity = '1';
clearTimeout(saveDebounceTimeout);
saveDebounceTimeout = setTimeout(function() {
statusEl.style.opacity = '0';
}, 1500);
}
}
// Safe deployment trigger cycle execution
setTimeout(applySavedProfileState, 50);
</script>
</div>
"
}
fn error_page() -> String {
"<div><h1>Page Not Found</h1><p class='subtitle'>This feature is under construction or doesn't exist.</p><button class='premium-button' onclick='location.href=\"/hub\"' style='margin-top:30px;'><i class='fas fa-home'></i> Return to Dashboard</button></div>"
}
fn auth_markup() -> String {
"<div class='auth-container' style='display:flex;align-items:center;justify-content:center;min-height:80vh;'><div class='feature-box' style='max-width:420px;width:100%;text-align:center;'><div style='font-size:3rem;margin-bottom:15px;'><i class='fas fa-brain' style='color:var(--primary);'></i></div><h1 style='font-size:2rem;margin-bottom:5px;'>FlashSync</h1><p class='subtitle'>Sign in to your learning hub</p><input type='text' class='premium-input' id='user' placeholder='Enter your name' style='margin-top:20px;'><button class='premium-button' style='width:100%;margin-top:20px;' onclick='login()'><i class='fas fa-arrow-right'></i> Get Started</button><p style='color:#888;margin-top:15px;font-size:0.9rem;'>or continue with</p><div style='display:flex;gap:10px;margin-top:10px;'><button class='accent-button' style='flex:1;' onclick='showToast(\"Google sign-in\",\"success\")'><i class='fab fa-google'></i></button><button class='accent-button' style='flex:1;' onclick='showToast(\"GitHub sign-in\",\"success\")'><i class='fab fa-github'></i></button></div></div></div>"
}