diff --git a/index.html b/index.html index c108aaa..0140a48 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,6 @@ - @@ -34,7 +33,7 @@ - + @@ -117,71 +116,9 @@
-
-
-

Yuuka Bot

-
- -
-
-
Uptime
-

—

- -
-
-
Disk
-

—

-
-
-
- -
-
-
Load Average
-

—

- 1m / 5m / 15m -
- -
-
Network
-

—

- -
-
-
Processes
-

—

- running -
-
-
Swap
-

—

-
-
-
- -
- -
-
CPU
-

—

-
-
-
-
-
-
Memory
-

—

-
-
-
- -
- -
-
System Info
-
—
-
-
+ +
+
@@ -252,7 +189,6 @@ - diff --git a/js/tabs.js b/js/tabs.js index baf7a60..d938bf0 100644 --- a/js/tabs.js +++ b/js/tabs.js @@ -8,9 +8,10 @@ document.addEventListener('DOMContentLoaded', () => { function pathToTab(path) { const slug = path.replace(/^\/+|\/+$/g, '') || 'about'; - const valid = ['about', 'gallery', 'bucketlist', 'yuukabot', 'calendar', 'services']; + const valid = ['about', 'gallery', 'bucketlist', 'status', 'calendar', 'services']; // old links if (slug === 'ahmet') return 'bucketlist'; + if (slug === 'yuukabot') return 'status'; return valid.includes(slug) ? slug : 'about'; } @@ -24,7 +25,7 @@ document.addEventListener('DOMContentLoaded', () => { const tabVideos = { calendar: calVideo, bucketlist: bucketlistVideo, - yuukabot: botVideo, + status: botVideo, services: botVideo }; @@ -45,6 +46,7 @@ document.addEventListener('DOMContentLoaded', () => { if (topLine) { topLine.classList.toggle('cal-theme', tabId === 'calendar'); topLine.classList.toggle('bucketlist-theme', tabId === 'bucketlist'); + topLine.classList.toggle('status-theme', tabId === 'status'); } } @@ -56,6 +58,12 @@ document.addEventListener('DOMContentLoaded', () => { if (target) target.style.display = 'block'; setBgForTab(tabId); + // VEGA polls every 2s, so only load it once someone opens the tab + const statusFrame = document.getElementById('status-frame'); + if (tabId === 'status' && statusFrame && !statusFrame.src) { + statusFrame.src = 'https://status.userevolt.app/'; + } + // tab name next to the hamburger on mobile const btn = document.getElementById(tabId + '-btn'); if (navCurrent && btn) navCurrent.textContent = btn.textContent; diff --git a/js/yuukabot.js b/js/yuukabot.js deleted file mode 100644 index 94da74d..0000000 --- a/js/yuukabot.js +++ /dev/null @@ -1,97 +0,0 @@ -// bot status dashboard -(() => { - const FIREBASE_CONFIG = { - apiKey: "AIzaSyDuSjEGEKx5FnWYnQq8f_owbpYRBRrl5x0", - authDomain: "esef-514bf.firebaseapp.com", - projectId: "esef-514bf", - storageBucket: "esef-514bf.firebasestorage.app", - messagingSenderId: "353969651290", - appId: "1:353969651290:web:41e3aaf8e54c743702a263" - }; - - // bot updates every 10s, nothing for a minute means it's probably dead - const STALE_MS = 60 * 1000; - - document.addEventListener("DOMContentLoaded", () => { - const dash = document.getElementById("yuukabot"); - if (!dash) return; - - if (!firebase.apps.length) firebase.initializeApp(FIREBASE_CONFIG); - const db = firebase.firestore(); - - const dot = document.getElementById("dash-status-dot"); - let online = false; - let updatedAt = null; - let loaded = false; // dot stays grey until the first data - - function refreshDot() { - if (!loaded) return; - const fresh = updatedAt == null || Date.now() - updatedAt < STALE_MS; - if (dot) dot.className = online && fresh ? "status-dot" : "status-dot offline"; - } - // a crashed bot stops sending updates so keep checking - setInterval(refreshDot, 5000); - - db.collection("sysinfo").doc("server").onSnapshot(doc => { - loaded = true; - const d = doc.exists ? doc.data() : {}; - online = !!d.online; - updatedAt = d.updatedAt ? d.updatedAt.toMillis() : null; - refreshDot(); - if (!doc.exists) return; - - setText("dash-uptime", d.uptime); - setText("dash-bot-uptime", d.botUptime ? "bot up " + d.botUptime : ""); - - setBar("dash-cpu-val", "dash-cpu-bar", d.cpu); - setBar("dash-ram-val", "dash-ram-bar", d.ram); - setBar("dash-disk-val", "dash-disk-bar", d.disk); - setBar("dash-swap-val", "dash-swap-bar", d.swap); - setText("dash-ram-detail", usedOfTotal(d.ramUsed, d.ramTotal)); - setText("dash-disk-detail", usedOfTotal(d.diskUsed, d.diskTotal)); - setText("dash-swap-detail", usedOfTotal(d.swapUsed, d.swapTotal)); - - if (d.load1 != null) { - setText("dash-load", d.load1 + " / " + d.load5 + " / " + d.load15); - } - - if (d.netSent) { - setText("dash-net", "sent " + d.netSent); - setText("dash-net-detail", "recv " + (d.netRecv || "—")); - } - - if (d.processes != null) { - setText("dash-procs", d.processes.toLocaleString()); - } - - setText("dash-fetch", d.fetch); - }, () => { - loaded = true; - online = false; - refreshDot(); - }); - }); - - function usedOfTotal(used, total) { - return used ? used + " / " + total : ""; - } - - function setText(id, value) { - const el = document.getElementById(id); - if (el && value != null) el.textContent = value; - } - - function setBar(valId, barId, value) { - const valEl = document.getElementById(valId); - const barEl = document.getElementById(barId); - if (valEl && value != null) { - valEl.textContent = value; - const unit = document.createElement("span"); - unit.textContent = "%"; - valEl.appendChild(unit); - } - if (barEl) { - barEl.style.width = Math.min(Math.max(value || 0, 0), 100) + "%"; - } - } -})(); diff --git a/style/styles.css b/style/styles.css index dcad211..aef12dc 100644 --- a/style/styles.css +++ b/style/styles.css @@ -91,6 +91,19 @@ html, body { transition: border-color 0.4s ease, color 0.4s ease; } +/* status theme (VEGA colours) */ +#top-line.status-theme { + background-color: #03070b; + border-bottom: 1px solid rgba(75, 187, 232, .28); +} +#top-line.status-theme #nav-tabs button { + border-bottom-color: #4bbbe8; + border-left-color: #4bbbe8; + border-right-color: #4bbbe8; + color: #a6e4ff; + transition: border-color 0.4s ease, color 0.4s ease; +} + h1 { font-size: 3rem; color: #000000; @@ -186,7 +199,8 @@ img { #nav-tabs button, #top-line.cal-theme #nav-tabs button, - #top-line.bucketlist-theme #nav-tabs button { + #top-line.bucketlist-theme #nav-tabs button, + #top-line.status-theme #nav-tabs button { width: 100%; margin: 0; padding: 12px 24px; @@ -205,6 +219,7 @@ img { } #top-line.cal-theme #nav-tabs button:hover { border-left-color: #FBC4CC; } #top-line.bucketlist-theme #nav-tabs button:hover { border-left-color: #B84457; } + #top-line.status-theme #nav-tabs button:hover { border-left-color: #4bbbe8; } /* keep the video centered on tall screens */ #background-video, @@ -221,3 +236,22 @@ img { h1 { font-size: 2.2rem; } p { font-size: 1.2rem; } } + +/* status tab: VEGA page fills everything under the top line */ +#status { padding: 0; } +#status-frame { + position: fixed; + top: 115px; + left: 0; + width: 100%; + height: calc(100% - 115px); + border: 0; + background: #05080c; +} + +@media (max-width: 900px) { + #status-frame { + top: 56px; + height: calc(100% - 56px); + } +} diff --git a/style/yuukabot.css b/style/yuukabot.css deleted file mode 100644 index f5df43d..0000000 --- a/style/yuukabot.css +++ /dev/null @@ -1,155 +0,0 @@ -/* grid settings */ -:root { - --dash-columns: 6; - --dash-gap: 20px; - --dash-min-card-height: 200px; - --dash-max-width: none; -} - -/* tab header */ -.dash-header { - padding: 120px 20px 30px; - text-align: center; -} -.dash-header h1 { - font-size: 2.8rem; - color: #fff; - margin: 0 0 6px; - text-shadow: 0 0 30px rgba(91, 111, 222, 0.5); -} -.dash-header .status-dot { - display: inline-block; - width: 14px; height: 14px; - border-radius: 50%; - background: #22c55e; - margin-right: 8px; - box-shadow: 0 0 12px #22c55e; - animation: pulse 2s infinite; -} -@keyframes pulse { - 0%, 100% { box-shadow: 0 0 8px #22c55e; } - 50% { box-shadow: 0 0 20px #22c55e, 0 0 40px rgba(34,197,94,0.4); } -} -.dash-header .offline { background: #ef4444; box-shadow: 0 0 12px #ef4444; animation: none; } -.dash-header .pending { background: #64748b; box-shadow: none; animation: none; } - -/* card grid */ -.dash-grid { - display: grid; - grid-template-columns: repeat(var(--dash-columns), 1fr); - gap: var(--dash-gap); - max-width: var(--dash-max-width); - margin: 0 auto; - padding: 0 48px 60px; -} - -/* glass card */ -.dash-card { - background: rgba(15, 15, 35, 0.55); - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - border: 1px solid rgba(91, 111, 222, 0.2); - border-radius: 16px; - padding: 28px 30px; - min-height: var(--dash-min-card-height); - display: flex; - flex-direction: column; - justify-content: space-between; - transition: border-color 0.3s, box-shadow 0.3s; -} -.dash-card:hover { - border-color: rgba(91, 111, 222, 0.5); - box-shadow: 0 6px 28px rgba(91, 111, 222, 0.12); -} - -/* card header */ -.card-top { - display: flex; - align-items: center; - gap: 12px; -} -.card-label { - font-size: 0.8rem; - text-transform: uppercase; - letter-spacing: 1.5px; - color: rgba(255, 255, 255, 0.45); - font-family: Arial, Helvetica, sans-serif; -} - -/* card value */ -.card-value { - font-size: 3rem; - font-weight: bold; - color: #fff; - margin: 0; - line-height: 1; -} -.card-value span { - font-size: 1.2rem; - color: rgba(255, 255, 255, 0.4); - font-weight: 400; -} - -/* progress bar */ -.card-bar-wrap { - width: 100%; - height: 8px; - background: rgba(255, 255, 255, 0.08); - border-radius: 4px; - overflow: hidden; - margin-top: 12px; -} -.card-bar-fill { - height: 100%; - border-radius: 4px; - transition: width 0.8s ease; -} -.card-bar-fill.cpu { background: linear-gradient(90deg, #5b6fde, #818cf8); } -.card-bar-fill.ram { background: linear-gradient(90deg, #a78bfa, #c4b5fd); } -.card-bar-fill.disk { background: linear-gradient(90deg, #34d399, #6ee7b7); } -.card-bar-fill.swap { background: linear-gradient(90deg, #f59e0b, #fbbf24); } - -/* detail text */ -.card-detail { - font-size: 0.78rem; - color: rgba(255, 255, 255, 0.3); - font-family: Arial, Helvetica, sans-serif; - margin-top: 6px; -} - -/* fastfetch text block */ -.card-fetch { - margin: 12px 0 0; - font-family: "Cascadia Code", "Fira Code", "JetBrains Mono", Consolas, monospace; - font-size: 0.78rem; - color: rgba(255, 255, 255, 0.7); - white-space: pre-wrap; - line-height: 1.6; - overflow: hidden; -} - -/* card widths */ -.dash-card.span2 { grid-column: span 2; } /* 3 per row */ -.dash-card.span3 { grid-column: span 3; } /* 2 per row */ -.dash-card.span6 { grid-column: span 6; } /* full width */ - -/* mobile */ -/* 2 columns on tablets, 1 on phones */ -@media (max-width: 900px) { - :root { - --dash-columns: 2; - --dash-gap: 12px; - --dash-min-card-height: 130px; - } - .dash-header { padding: 72px 12px 20px; } - .dash-header h1 { font-size: 2rem; } - .dash-grid { padding: 0 4px 30px; } - .dash-card { padding: 20px; } - .dash-card.span2, - .dash-card.span3 { grid-column: span 1; } - .dash-card.span6 { grid-column: 1 / -1; } - .card-value { font-size: 2.2rem; } -} -@media (max-width: 575px) { - :root { --dash-columns: 1; } -}