File size: 14,221 Bytes
d0b8e8f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | import gleam/string
fn active_class(page: String, key: String) -> String {
case page == key || string.starts_with(page, key <> "-") {
True -> " active"
False -> ""
}
}
fn nav_item(page: String, key: String, href: String, icon: String, label: String) -> String {
"<a class='nav-item" <> active_class(page, key) <> "' href='" <> href <> "'><i class='" <> icon <> "'></i><span>" <> label <> "</span></a>"
}
fn nav_html(page: String) -> String {
let sidebar = "<sidebar-nav>
<div class='brand-logo'><i class='fas fa-brain'></i><span>FlashSync</span></div>
<div class='nav-section'><div class='nav-label'>Core</div>"
let sidebar = sidebar <> nav_item(page, "hub", "/hub", "fas fa-home", "Dashboard")
let sidebar = sidebar <> nav_item(page, "flashcards", "/flashcards", "fas fa-cards-blank", "Flashcards")
let sidebar = sidebar <> nav_item(page, "education", "/education", "fas fa-graduation-cap", "Education Portal")
let sidebar = sidebar <> "</div>
<div class='nav-section'><div class='nav-label'>Intelligence</div>"
let sidebar = sidebar <> nav_item(page, "tutor", "/tutor", "fas fa-robot", "AI Tutor")
let sidebar = sidebar <> nav_item(page, "ai-solver", "/ai-solver", "fas fa-wand-magic-sparkles", "AI Solver")
let sidebar = sidebar <> nav_item(page, "study-tools", "/study-tools", "fas fa-toolbox", "Study Tools")
let sidebar = sidebar <> nav_item(page, "heatmap", "/heatmap", "fas fa-fire", "Heatmap")
let sidebar = sidebar <> "</div>
<div class='nav-section'><div class='nav-label'>Analytics</div>"
let sidebar = sidebar <> nav_item(page, "biometric", "/biometric", "fas fa-heart", "Focus Track")
let sidebar = sidebar <> nav_item(page, "exam", "/exams", "fas fa-clipboard-list", "Exams")
let sidebar = sidebar <> nav_item(page, "timetravel", "/timetravel", "fas fa-hourglass-end", "Timeline")
let sidebar = sidebar <> nav_item(page, "analytics", "/analytics", "fas fa-chart-line", "Analytics")
let sidebar = sidebar <> "</div>
<div class='nav-section'><div class='nav-label'>Personalization</div>"
let sidebar = sidebar <> nav_item(page, "achievements", "/achievements", "fas fa-trophy", "Achievements")
let sidebar = sidebar <> nav_item(page, "calendar", "/calendar", "fas fa-calendar", "Calendar")
let sidebar = sidebar <> nav_item(page, "notes", "/notes", "fas fa-sticky-note", "Notes")
let sidebar = sidebar <> nav_item(page, "vocabulary", "/vocabulary", "fas fa-book", "Vocabulary")
let sidebar = sidebar <> nav_item(page, "pomodoro", "/pomodoro", "fas fa-clock", "Pomodoro")
let sidebar = sidebar <> nav_item(page, "formulas", "/formulas", "fas fa-square-root-variable", "Formulas")
let sidebar = sidebar <> nav_item(page, "profile", "/profile", "fas fa-user", "Profile")
let sidebar = sidebar <> nav_item(page, "settings", "/settings", "fas fa-sliders", "Settings")
sidebar <> "</div>
</sidebar-nav>"
}
fn css_style() -> String {
":root { --primary: #00f2ff; --secondary: #bc13fe; --accent: #ff006e; --success: #00ff00; --warning: #ffaa00; --bg-dark: #010103; --bg-darker: #000; --glass: rgba(255,255,255,0.03); --glass-border: rgba(0,242,255,0.1); --page-bg: linear-gradient(135deg, #010103 0%, #0a0a1a 50%, #010103 100%); --text-main: #ffffff; }
html[data-theme='light'] { --primary:#0065a3; --secondary:#7a2fc7; --accent:#cf2a4f; --success:#0b8f45; --warning:#b86a00; --glass: rgba(0,0,0,0.04); --glass-border: rgba(0,101,163,0.15); --page-bg: linear-gradient(135deg, #f4f8ff 0%, #eef3ff 50%, #f8fbff 100%); --text-main:#111827; }
* { margin:0; padding:0; box-sizing:border-box; }
html,body { width:100%; height:100%; background: var(--page-bg); color: var(--text-main); font-family: 'Segoe UI', 'Trebuchet MS', sans-serif; overflow-x: hidden; scroll-behavior: smooth; }
body { display:flex; gap:0; }
sidebar-nav { width:280px; background:linear-gradient(180deg,#0a0a0f 0%,#050508 100%); border-right:2px solid rgba(0,242,255,0.1); padding:30px 20px; height:100vh; overflow-y:auto; position:fixed; left:0; top:0; z-index:1000; }
main-content { flex:1; margin-left:280px; padding:40px; overflow-y:auto; height:100vh; }
.brand-logo { font-size:1.8rem; font-weight:900; color:var(--primary); margin-bottom:40px; display:flex; align-items:center; gap:12px; }
.nav-section { margin-bottom:30px; }
.nav-label { font-size:0.75rem; text-transform:uppercase; color:#666; letter-spacing:2px; margin-bottom:12px; font-weight:700; }
.nav-item { display:flex; align-items:center; gap:12px; padding:14px 16px; margin:6px 0; border-radius:12px; color:#888; text-decoration:none; cursor:pointer; transition:all 0.3s; border-left:3px solid transparent; }
.nav-item:hover { background:rgba(0,242,255,0.08); color:var(--primary); border-left-color:var(--primary); }
.nav-item.active { background:rgba(0,242,255,0.15); color:var(--primary); border-left-color:var(--primary); font-weight:600; }
h1 { font-size:clamp(2rem, 8vw, 3.5rem); font-weight:900; background:linear-gradient(135deg, #fff 0%, var(--primary) 100%); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; margin-bottom:15px; }
h2 { font-size:1.8rem; font-weight:800; margin:20px 0 15px; color:var(--primary); }
h3 { font-size:1.3rem; font-weight:700; margin:15px 0 10px; color:#fff; }
.subtitle { color:#999; font-size:1.1rem; margin-bottom:30px; }
.stat-card { background:var(--glass); border:1px solid rgba(0,242,255,0.15); padding:25px; border-radius:16px; transition:all 0.4s; position:relative; }
.stat-card:hover { border-color:var(--primary); transform:translateY(-4px); box-shadow:0 15px 40px rgba(0,242,255,0.15); }
.grid-2 { display:grid; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)); gap:20px; margin:25px 0; }
.grid-3 { display:grid; grid-template-columns:repeat(auto-fit, minmax(300px, 1fr)); gap:25px; margin:25px 0; }
.grid-4 { display:grid; grid-template-columns:repeat(auto-fit, minmax(220px, 1fr)); gap:20px; margin:25px 0; }
.premium-input { width:100%; background:rgba(0,0,0,0.4); border:2px solid rgba(0,242,255,0.2); padding:16px; border-radius:12px; color:#fff; font-size:1rem; transition:all 0.3s; }
.premium-input:focus { outline:none; border-color:var(--primary); box-shadow:0 0 20px rgba(0,242,255,0.3); }
.premium-button { background:linear-gradient(135deg, var(--primary) 0%, #0099cc 100%); color:#000; border:none; padding:14px 32px; border-radius:50px; font-weight:700; font-size:1rem; cursor:pointer; transition:all 0.3s; display:inline-flex; align-items:center; gap:8px; }
.premium-button:hover { transform:translateY(-3px); box-shadow:0 10px 30px rgba(0,242,255,0.4); }
.accent-button { background:linear-gradient(135deg, var(--secondary) 0%, #9a0099 100%); border:none; padding:14px 32px; border-radius:50px; font-weight:700; font-size:1rem; cursor:pointer; transition:all 0.3s; display:inline-flex; align-items:center; gap:8px; color:#fff; }
.accent-button:hover { transform:translateY(-3px); box-shadow:0 10px 30px rgba(188,19,254,0.4); }
.success-button { background:linear-gradient(135deg, var(--success) 0%, #00cc00 100%); border:none; padding:14px 32px; border-radius:50px; font-weight:700; font-size:1rem; cursor:pointer; transition:all 0.3s; display:inline-flex; align-items:center; gap:8px; color:#000; }
.success-button:hover { transform:translateY(-3px); box-shadow:0 10px 30px rgba(0,255,0,0.4); }
.feature-box { background:linear-gradient(135deg, rgba(0,242,255,0.05) 0%, rgba(188,19,254,0.05) 100%); border:1px solid rgba(0,242,255,0.2); padding:30px; border-radius:20px; margin:20px 0; }
.chat-window { background:rgba(0,0,0,0.5); border:1px solid rgba(0,242,255,0.2); border-radius:20px; height:500px; display:flex; flex-direction:column; overflow:hidden; }
.chat-messages { flex:1; overflow-y:auto; padding:20px; display:flex; flex-direction:column; gap:15px; }
.message { padding:15px 20px; border-radius:15px; max-width:85%; line-height:1.5; }
.message.user { background:linear-gradient(135deg, rgba(0,242,255,0.2), rgba(0,242,255,0.1)); border-left:3px solid var(--primary); margin-left:auto; }
.message.ai { background:linear-gradient(135deg, rgba(188,19,254,0.2), rgba(188,19,254,0.1)); border-left:3px solid var(--secondary); }
.chat-input { padding:20px; border-top:1px solid rgba(0,242,255,0.1); display:flex; gap:10px; }
.chat-input input { flex:1; }
.toast { position:fixed; bottom:30px; right:30px; padding:18px 28px; border-radius:14px; background:rgba(0,0,0,0.9); border:1px solid var(--primary); color:var(--primary); font-weight:600; transform:translateY(100px); opacity:0; transition:all 0.4s; z-index:10000; }
.toast.show { transform:translateY(0); opacity:1; }
.toast.success { border-color:var(--success); color:var(--success); }
.toast.error { border-color:var(--accent); color:var(--accent); }
.modal-overlay { position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.7); z-index:9999; display:flex; align-items:center; justify-content:center; opacity:0; pointer-events:none; transition:opacity 0.3s; }
.modal-overlay.show { opacity:1; pointer-events:auto; }
.modal-box { background:linear-gradient(135deg, #0f1a30, #141f38); border:1px solid rgba(0,242,255,0.2); border-radius:24px; padding:40px; max-width:500px; width:90%; max-height:80vh; overflow-y:auto; transform:scale(0.9); transition:transform 0.3s; }
.modal-overlay.show .modal-box { transform:scale(1); }
.typing-indicator { display:flex; gap:6px; padding:8px 0; }
.typing-indicator span { width:10px; height:10px; border-radius:50%; background:var(--secondary); animation:typing 1.4s infinite; }
.typing-indicator span:nth-child(2) { animation-delay:0.2s; }
.typing-indicator span:nth-child(3) { animation-delay:0.4s; }
@keyframes typing { 0%,100% { transform:translateY(0); opacity:0.4; } 50% { transform:translateY(-10px); opacity:1; } }
.flash-card { background:linear-gradient(135deg, #1c2b4e, #141f38); border:2px solid rgba(0,242,255,0.15); border-radius:20px; padding:40px; margin:20px 0; min-height:250px; display:flex; flex-direction:column; align-items:center; justify-content:center; cursor:pointer; transition:all 0.4s; text-align:center; }
.flash-card:hover { border-color:var(--primary); }
.flash-card .back { display:none; }
.flash-card.flipped .front { display:none; }
.flash-card.flipped .back { display:block; }
.tag { display:inline-block; padding:4px 12px; border-radius:20px; font-size:0.75rem; font-weight:600; margin:3px; }
.tag.primary { background:rgba(0,242,255,0.2); color:var(--primary); }
.tag.success { background:rgba(0,255,0,0.2); color:var(--success); }
.tag.warning { background:rgba(255,170,0,0.2); color:var(--warning); }
.tag.accent { background:rgba(188,19,254,0.2); color:var(--secondary); }
.study-progress-bar { height:8px; background:rgba(0,0,0,0.4); border-radius:4px; margin:15px 0; overflow:hidden; }
.study-progress-fill { height:100%; background:linear-gradient(90deg, var(--primary), var(--secondary)); border-radius:4px; transition:width 0.5s; }
.compact-btn { padding:8px 16px; font-size:0.85rem; border-radius:8px; border:none; cursor:pointer; font-weight:600; transition:all 0.2s; display:inline-flex; align-items:center; gap:6px; }
.compact-btn:hover { transform:translateY(-2px); }
.stats-row { display:flex; gap:20px; flex-wrap:wrap; margin:20px 0; }
.stats-badge { background:rgba(0,242,255,0.08); padding:10px 18px; border-radius:10px; border:1px solid rgba(0,242,255,0.1); font-size:0.9rem; }
.stats-badge strong { color:var(--primary); }
.mode-toggle { display:flex; gap:10px; flex-wrap:wrap; }
.mode-btn,.lang-btn { padding:10px 20px; border-radius:10px; border:1px solid rgba(0,242,255,0.2); background:rgba(0,0,0,0.3); color:#888; cursor:pointer; transition:all 0.3s; font-size:0.9rem; }
.mode-btn:hover,.lang-btn:hover { border-color:var(--primary); color:var(--primary); }
.mode-btn.active,.lang-btn.active { background:rgba(0,242,255,0.2); border-color:var(--primary); color:var(--primary); font-weight:600; }
.language-selector { display:flex; gap:10px; flex-wrap:wrap; margin-top:10px; }
@media (max-width:900px) { sidebar-nav { width:100px; padding:20px 10px; } main-content { margin-left:100px; padding:25px; } .nav-item span:not(:first-child) { display:none; } }
.confetti { position:fixed; top:0; left:0; width:100%; height:100%; pointer-events:none; z-index:99999; overflow:hidden; }
.confetti-piece { position:absolute; width:10px; height:10px; top:-10px; animation:confettiFall 3s ease-in forwards; }
@keyframes confettiFall { 0% { transform:translateY(0) rotate(0deg); opacity:1; } 100% { transform:translateY(100vh) rotate(720deg); opacity:0; } }
.exam-timer { font-size:2rem; font-weight:900; color:var(--primary); text-align:center; padding:20px; }
.exam-timer.warning { color:var(--warning); animation:pulse 1s infinite; }
.exam-timer.critical { color:var(--accent); animation:pulse 0.5s infinite; }
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.5; } }"
}
pub fn build_premium_html(page: String, content: String) -> String {
let head_start = "<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>FlashSync Pro</title>
<link href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css' rel='stylesheet'>
<style>"
let style = css_style()
let head_end = "</style>
</head>
<body>"
let sidebar = nav_html(page)
let modal_toast = "
<div id='toast' class='toast'></div>
<div id='modalOverlay' class='modal-overlay' onclick='closeModal(event)'>
<div class='modal-box'><h2 id='modalTitle'></h2><div id='modalBody'></div>
<button class='premium-button' onclick='closeModal()' style='margin-top:20px;width:100%;'>Close</button>
</div>
</div>
<script src=\"/static/app.js\"></script>
</body>
</html>"
head_start <> style <> head_end <> sidebar <> "<main-content>" <> content <> "</main-content>" <> modal_toast
} |