flashsync / src /ui_builder.gleam
Dhurgh's picture
Permanently remove build artifacts from history
d0b8e8f
Raw
History Blame Contribute Delete
14.2 kB
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
}