From 4476a5e0e260e308b8bf1b9b3fee8dcc6dd33639 Mon Sep 17 00:00:00 2001 From: naturallaw77 Date: Wed, 9 Sep 2026 00:14:07 +0000 Subject: [PATCH] Add boot splash; separate Systems Operational and Security icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loading indicator: - Branded boot splash (Hub logo inside an accent spinner ring, "Starting The Hub") covers the shell while the first services data loads, then fades out once the welcome dashboard has rendered; never blocks longer than 25s and reassures the user after 8s (message about post-reboot delays) - Fire the network and update checks before the first services render so the dashboard cards are current at first paint - Sidebar Update button now adopts the last known update state when built (order-independent), and the welcome dashboard re-renders when the update state changes Icons: - New monochrome g-pulse glyph (activity line) for Systems Operational: welcome card, dialog header, and System Status section — the shield no longer doubles as Security - Tech Support / Security dialog header gets a standard chip; Security shows the shield chip with a plain "Security" title (no emoji), and the shared dialog title now resets correctly when reopening Tech Support after Security --- .../static/css/layout.css | 65 +++++++++++++++++ .../static/js/dashboard.js | 4 +- app/sovran_systemsos_web/static/js/events.js | 4 +- .../static/js/security.js | 7 +- app/sovran_systemsos_web/static/js/support.js | 8 ++ app/sovran_systemsos_web/static/js/tiles.js | 73 +++++++++++-------- app/sovran_systemsos_web/templates/index.html | 47 +++++++++++- 7 files changed, 172 insertions(+), 36 deletions(-) diff --git a/app/sovran_systemsos_web/static/css/layout.css b/app/sovran_systemsos_web/static/css/layout.css index c401fc6..64e3947 100644 --- a/app/sovran_systemsos_web/static/css/layout.css +++ b/app/sovran_systemsos_web/static/css/layout.css @@ -259,6 +259,67 @@ .welcome-inner { animation: none; } } +/* ── Boot splash ────────────────────────────────────────────────── */ +/* Covers the shell while the first services/config data loads; the + inline script in index.html lifts it (window.__liftAppSplash). */ +#app-splash { + position: fixed; + inset: 0; + z-index: 2000; + background: var(--bg); + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.45s ease; +} + +#app-splash.done { opacity: 0; pointer-events: none; } + +.splash-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + padding: 24px; +} + +.splash-ring { + position: relative; + width: 96px; + height: 96px; + display: grid; + place-items: center; + margin-bottom: 8px; +} + +.splash-ring img { width: 60px; height: 60px; opacity: 0.95; } + +.splash-ring-arc { + position: absolute; + inset: 0; + border-radius: 50%; + border: 3px solid rgba(255, 255, 255, 0.10); + border-top-color: var(--accent); + animation: splash-spin 1s linear infinite; +} + +@keyframes splash-spin { to { transform: rotate(360deg); } } + +.splash-title { + font-size: 1.02rem; + font-weight: 700; + color: var(--text); +} + +.splash-sub { + font-size: 0.84rem; + color: var(--text-3); +} + +@media (prefers-reduced-motion: reduce) { + .splash-ring-arc { animation-duration: 2.5s; } +} + .welcome-inner { position: relative; max-width: 1040px; @@ -387,6 +448,10 @@ background: linear-gradient(160deg, #42f39a, #1aa45d); } +.widget-chip.chip-neutral { + background: linear-gradient(160deg, #2e333a, #23272c); +} + .w-body { min-width: 0; } .widget h3 { diff --git a/app/sovran_systemsos_web/static/js/dashboard.js b/app/sovran_systemsos_web/static/js/dashboard.js index ad7448d..02624cc 100644 --- a/app/sovran_systemsos_web/static/js/dashboard.js +++ b/app/sovran_systemsos_web/static/js/dashboard.js @@ -230,7 +230,7 @@ var attentionTitle = c.attention ? "Systems need attention" : "Systems operational"; $wcSystems.innerHTML = '
' + - '
' + icon("g-shield-check") + '
' + + '
' + icon("g-pulse") + '
' + '

' + attentionTitle + '

' + sub + '
' + '' + icon("g-chev") + '' + '
'; @@ -337,7 +337,7 @@ /* System status */ html += '
' + - '
' + icon("g-shield-check") + 'System Status
' + + '
' + icon("g-pulse") + 'System Status
' + step(1, "Services running", "", String(c.running)) + step(2, "Needs attention", "", c.attention ? escHtml(c.attentionNames.join(", ")) : "None") + step(3, "Turned off", "", c.off ? escHtml(c.offNames.join(", ")) : "None") + diff --git a/app/sovran_systemsos_web/static/js/events.js b/app/sovran_systemsos_web/static/js/events.js index c940130..b03d861 100644 --- a/app/sovran_systemsos_web/static/js/events.js +++ b/app/sovran_systemsos_web/static/js/events.js @@ -244,9 +244,9 @@ async function init() { if (badge && cfg.role_label) badge.textContent = cfg.role_label; window._roleLabel = cfg.role_label || ""; - await refreshServices(); loadNetwork(); checkUpdates(); + await refreshServices(); setInterval(refreshServices, POLL_INTERVAL_SERVICES); setInterval(checkUpdates, POLL_INTERVAL_UPDATES); @@ -256,9 +256,9 @@ async function init() { } loadAutolaunchToggle(); } catch (_) { - await refreshServices(); loadNetwork(); checkUpdates(); + await refreshServices(); setInterval(refreshServices, POLL_INTERVAL_SERVICES); setInterval(checkUpdates, POLL_INTERVAL_UPDATES); loadAutolaunchToggle(); diff --git a/app/sovran_systemsos_web/static/js/security.js b/app/sovran_systemsos_web/static/js/security.js index 78fa6b6..c6df1e3 100644 --- a/app/sovran_systemsos_web/static/js/security.js +++ b/app/sovran_systemsos_web/static/js/security.js @@ -5,7 +5,12 @@ function openSecurityModal() { if ($supportModal) $supportModal.classList.add("open"); var title = document.getElementById("support-modal-title"); - if (title) title.textContent = "\uD83D\uDEE1 Security"; + if (title) title.textContent = "Security"; + var chip = document.getElementById("support-modal-chip"); + if (chip) { + var use = chip.querySelector("use"); + if (use) use.setAttribute("href", "#g-shield-check"); + } if ($supportBody) { $supportBody.innerHTML = diff --git a/app/sovran_systemsos_web/static/js/support.js b/app/sovran_systemsos_web/static/js/support.js index 9560661..88d4832 100644 --- a/app/sovran_systemsos_web/static/js/support.js +++ b/app/sovran_systemsos_web/static/js/support.js @@ -5,6 +5,14 @@ async function openSupportModal() { if (!$supportModal) return; $supportModal.classList.add("open"); + // The dialog is shared with Security — always restore its identity + var title = document.getElementById("support-modal-title"); + if (title) title.textContent = "Tech Support"; + var chip = document.getElementById("support-modal-chip"); + if (chip) { + var use = chip.querySelector("use"); + if (use) use.setAttribute("href", "#g-lifebuoy"); + } $supportBody.innerHTML = '

Checking support status…

'; try { var status = await apiFetch("/api/support/status"); diff --git a/app/sovran_systemsos_web/static/js/tiles.js b/app/sovran_systemsos_web/static/js/tiles.js index 787659b..3cd6a01 100644 --- a/app/sovran_systemsos_web/static/js/tiles.js +++ b/app/sovran_systemsos_web/static/js/tiles.js @@ -71,6 +71,8 @@ function renderSidebarSupport(supportServices) { ''; sidebarUpdateBtn.addEventListener("click", function() { openUpdateModal(); }); $sidebarSupport.appendChild(sidebarUpdateBtn); + // checkUpdates may already have run before the sidebar was built + applyUpdateSidebarState(window._lastUpdateCheck); for (var i = 0; i < supportServices.length; i++) { var svc = supportServices[i]; @@ -251,6 +253,10 @@ async function refreshServices() { var services = await apiFetch("/api/services"); if (_firstLoad) { buildTiles(services, _categoryLabels); _firstLoad = false; } else { updateTiles(services); } + // Service data has rendered — the dashboard is ready; lift the boot + // splash (no-op once lifted). Note: dashboardServicesUpdated may also + // fire from checkUpdates before this resolves, so the lift lives here. + if (typeof window.__liftAppSplash === "function") window.__liftAppSplash(); } catch (err) { console.warn("Failed to fetch services:", err); } } @@ -270,39 +276,48 @@ async function loadNetwork() { // ── Update check ────────────────────────────────────────────────── +// Paint the sidebar Update button from the last known update state. +// Called after each check AND when the button is (re)built, so the hint +// is correct regardless of which finishes first at startup. +function applyUpdateSidebarState(data) { + if (!data) return; + var sidebarUpdateBtn = document.getElementById("sidebar-btn-update"); + var sidebarUpdateHint = document.getElementById("sidebar-update-hint"); + if (!sidebarUpdateBtn) return; + var hasUpdates = !!data.available; + var updateStatus = data.status || "idle"; + if (updateStatus === "failed") { + // Last update errored and did not apply — surface it as a persistent + // red banner that re-opens the failed run with a "Retry Update" action. + sidebarUpdateBtn.style.borderColor = "#f66151"; + sidebarUpdateBtn.style.backgroundColor = "rgba(246, 97, 81, 0.10)"; + if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Update failed — click to retry"; + } else if (updateStatus === "reboot_required") { + sidebarUpdateBtn.style.borderColor = "#e9b64a"; + sidebarUpdateBtn.style.backgroundColor = "rgba(233, 182, 74, 0.10)"; + if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Restart required"; + } else if (updateStatus === "running") { + sidebarUpdateBtn.style.borderColor = "#78aeed"; + sidebarUpdateBtn.style.backgroundColor = "rgba(120, 174, 237, 0.10)"; + if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Update in progress…"; + } else if (hasUpdates) { + sidebarUpdateBtn.style.borderColor = "#3ecf8e"; + sidebarUpdateBtn.style.backgroundColor = "rgba(62, 207, 142, 0.10)"; + if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Updates available!"; + } else { + sidebarUpdateBtn.style.borderColor = ""; + sidebarUpdateBtn.style.backgroundColor = ""; + if (sidebarUpdateHint) sidebarUpdateHint.textContent = "System is up to date"; + } +} + async function checkUpdates() { try { var data = await apiFetch("/api/updates/check"); window._lastUpdateCheck = data; if (typeof markUpdateChecked === "function") markUpdateChecked(); - var hasUpdates = !!data.available; - var updateStatus = data.status || "idle"; - var sidebarUpdateBtn = document.getElementById("sidebar-btn-update"); - var sidebarUpdateHint = document.getElementById("sidebar-update-hint"); - if (sidebarUpdateBtn) { - if (updateStatus === "failed") { - // Last update errored and did not apply — surface it as a persistent - // red banner that re-opens the failed run with a "Retry Update" action. - sidebarUpdateBtn.style.borderColor = "#f66151"; - sidebarUpdateBtn.style.backgroundColor = "rgba(246, 97, 81, 0.10)"; - if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Update failed — click to retry"; - } else if (updateStatus === "reboot_required") { - sidebarUpdateBtn.style.borderColor = "#e9b64a"; - sidebarUpdateBtn.style.backgroundColor = "rgba(233, 182, 74, 0.10)"; - if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Restart required"; - } else if (updateStatus === "running") { - sidebarUpdateBtn.style.borderColor = "#78aeed"; - sidebarUpdateBtn.style.backgroundColor = "rgba(120, 174, 237, 0.10)"; - if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Update in progress…"; - } else if (hasUpdates) { - sidebarUpdateBtn.style.borderColor = "#3ecf8e"; - sidebarUpdateBtn.style.backgroundColor = "rgba(62, 207, 142, 0.10)"; - if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Updates available!"; - } else { - sidebarUpdateBtn.style.borderColor = ""; - sidebarUpdateBtn.style.backgroundColor = ""; - if (sidebarUpdateHint) sidebarUpdateHint.textContent = "System is up to date"; - } - } + applyUpdateSidebarState(data); + // The welcome dashboard's updates card reads this state + if (typeof window.dashboardServicesUpdated === "function") window.dashboardServicesUpdated(); } catch (_) {} } diff --git a/app/sovran_systemsos_web/templates/index.html b/app/sovran_systemsos_web/templates/index.html index e9b0e0b..4aa6e24 100644 --- a/app/sovran_systemsos_web/templates/index.html +++ b/app/sovran_systemsos_web/templates/index.html @@ -111,6 +111,11 @@ + + + + @@ -165,6 +170,41 @@ + +
+
+
+ + +
+
Starting The Hub
+
Gathering your services…
+
+
+ +
@@ -250,7 +290,7 @@
- + Systems Operational @@ -317,7 +357,10 @@