| 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>" |
| } |