Retire l'ecran de chargement plein ecran au demarrage d'ObsiGate #53
+1
-66
@@ -47,33 +47,6 @@
|
|||||||
href="/static/icons/icon-192x192.svg"
|
href="/static/icons/icon-192x192.svg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Boot splash CSS — inline so it applies BEFORE /static/style.css is
|
|
||||||
available (desktop: embedded page served from tauri:// has no /static) -->
|
|
||||||
<style id="boot-splash-css">
|
|
||||||
body:not(.app-booted) > *:not(#boot-splash) { display: none !important; }
|
|
||||||
#boot-splash {
|
|
||||||
position: fixed; inset: 0; z-index: 2147483647;
|
|
||||||
display: flex; align-items: center; justify-content: center;
|
|
||||||
background: radial-gradient(1200px 600px at 50% -10%, #26272b 0%, #17181a 60%);
|
|
||||||
color: #eceeeb; font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
|
|
||||||
}
|
|
||||||
body.app-booted #boot-splash { display: none; }
|
|
||||||
.bs-card { display: flex; flex-direction: column; align-items: center; gap: 14px; }
|
|
||||||
.bs-logo {
|
|
||||||
width: 72px; height: 72px; display: flex; align-items: center; justify-content: center;
|
|
||||||
font-size: 38px; background: #1f2023; border: 1px solid #33353a; border-radius: 18px;
|
|
||||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45);
|
|
||||||
}
|
|
||||||
.bs-title { font-size: 22px; font-weight: 600; letter-spacing: 0.3px; }
|
|
||||||
.bs-spinner {
|
|
||||||
width: 34px; height: 34px; border-radius: 50%;
|
|
||||||
border: 3px solid rgba(124, 58, 237, 0.25); border-top-color: #7c3aed;
|
|
||||||
animation: bs-spin 0.9s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes bs-spin { to { transform: rotate(360deg); } }
|
|
||||||
.bs-status { font-size: 13px; color: #9aa3b2; min-height: 18px; }
|
|
||||||
@media (prefers-reduced-motion: reduce) { .bs-spinner { animation-duration: 1.8s; } }
|
|
||||||
</style>
|
|
||||||
<link rel="stylesheet" href="/static/style.css" />
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
<link rel="stylesheet" href="/static/draco-barre.css" />
|
<link rel="stylesheet" href="/static/draco-barre.css" />
|
||||||
<link rel="stylesheet" href="/static/draco-alertes.css" />
|
<link rel="stylesheet" href="/static/draco-alertes.css" />
|
||||||
@@ -89,7 +62,7 @@
|
|||||||
id="hljs-theme-light"
|
id="hljs-theme-light"
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<!-- defer: ne bloque pas le premier paint (le splash inline s'affiche immédiatement) -->
|
<!-- defer: ne bloque pas le premier paint -->
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
<!-- Langues highlight.js hors bundle "common" (enregistrement auto apres le core) -->
|
<!-- Langues highlight.js hors bundle "common" (enregistrement auto apres le core) -->
|
||||||
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dockerfile.min.js"></script>
|
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dockerfile.min.js"></script>
|
||||||
@@ -226,44 +199,6 @@
|
|||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body class="has-draco-bar">
|
<body class="has-draco-bar">
|
||||||
<!-- Boot splash : visible tant que l'app n'a pas booté avec son CSS.
|
|
||||||
Sur desktop, la page embarquée (tauri://) n'a pas /static/style.css →
|
|
||||||
sans ce splash, le squelette HTML brut serait visible pendant le
|
|
||||||
démarrage du backend (FOUC 3-10 s). Styles 100% inline. -->
|
|
||||||
<div id="boot-splash" role="status" aria-live="polite">
|
|
||||||
<div class="bs-card">
|
|
||||||
<div class="bs-logo">📖</div>
|
|
||||||
<div class="bs-title">ObsiGate</div>
|
|
||||||
<div class="bs-spinner" aria-hidden="true"></div>
|
|
||||||
<div class="bs-status" id="boot-splash-status">Démarrage…</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
(function () {
|
|
||||||
var done = false;
|
|
||||||
function bootReady() {
|
|
||||||
if (done) return;
|
|
||||||
done = true;
|
|
||||||
document.body.classList.add("app-booted");
|
|
||||||
var s = document.getElementById("boot-splash");
|
|
||||||
if (s && s.parentNode) s.parentNode.removeChild(s);
|
|
||||||
}
|
|
||||||
window.__obsigateBootReady = bootReady;
|
|
||||||
window.__setBootStatus = function (txt) {
|
|
||||||
var el = document.getElementById("boot-splash-status");
|
|
||||||
if (el) el.textContent = txt;
|
|
||||||
};
|
|
||||||
var poll = setInterval(function () {
|
|
||||||
if (window.__OBSIGATE_BOOTED) { clearInterval(poll); bootReady(); }
|
|
||||||
}, 80);
|
|
||||||
// Filet de sécurité : si le CSS principal a chargé (origine backend réelle)
|
|
||||||
// mais que le JS a échoué, révéler l'app après 10 s au lieu d'un splash infini.
|
|
||||||
setTimeout(function () {
|
|
||||||
var link = document.querySelector('link[href*="style.css"]');
|
|
||||||
if (link && link.sheet) { clearInterval(poll); bootReady(); }
|
|
||||||
}, 10000);
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
<div class="search-progress-bar" id="search-progress-bar">
|
<div class="search-progress-bar" id="search-progress-bar">
|
||||||
<div class="search-progress-bar__fill"></div>
|
<div class="search-progress-bar__fill"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-10
@@ -120,16 +120,8 @@ async function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
try {
|
await init();
|
||||||
await init();
|
Sync.init();
|
||||||
Sync.init();
|
|
||||||
} finally {
|
|
||||||
// Signale au splash de démarrage (index.html #boot-splash) que l'app est
|
|
||||||
// initialisée : le splash est retiré et le squelette stylé révélé.
|
|
||||||
// Set AFTER init() resolves so every handler (search, tabs, …) is bound.
|
|
||||||
window.__OBSIGATE_BOOTED = true;
|
|
||||||
if (typeof window.__obsigateBootReady === "function") window.__obsigateBootReady();
|
|
||||||
}
|
|
||||||
// Show version in header — always visible, fallback before the async fetch
|
// Show version in header — always visible, fallback before the async fetch
|
||||||
populateVersions();
|
populateVersions();
|
||||||
fetch('/api/health')
|
fetch('/api/health')
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
"""Tests — démarrage ObsiGate sans écran de chargement plein écran.
|
||||||
|
|
||||||
|
Le splash (``#boot-splash`` dans ``frontend/index.html`` : icône
|
||||||
|
livre, titre, spinner, « Démarrage… ») masquait tout le reste de la
|
||||||
|
page via ``body:not(.app-booted) > * { display: none !important }``
|
||||||
|
jusqu'au signal de ``frontend/js/app.js`` (ou un repli à 10 s).
|
||||||
|
Les autres applications (TikTak, Kanbante, Flowdeck, console Spark)
|
||||||
|
n'affichent aucun splash de ce type au démarrage : le contenu y
|
||||||
|
apparaît dès qu'il est prêt. Le retrait supprime donc l'écran, son
|
||||||
|
CSS de masquage, son script de pilotage et le signal côté ``app.js``,
|
||||||
|
sans toucher aux indicateurs internes aux vues ni à l'écran de
|
||||||
|
connexion.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
FRONTEND = ROOT / "frontend"
|
||||||
|
|
||||||
|
MARQUEURS_SPLASH = (
|
||||||
|
"boot-splash",
|
||||||
|
"app-booted",
|
||||||
|
"__OBSIGATE_BOOTED",
|
||||||
|
"__obsigateBootReady",
|
||||||
|
"__setBootStatus",
|
||||||
|
"bs-card",
|
||||||
|
"bs-logo",
|
||||||
|
"bs-title",
|
||||||
|
"bs-spinner",
|
||||||
|
"bs-status",
|
||||||
|
"bs-spin",
|
||||||
|
"Démarrage…",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _index() -> str:
|
||||||
|
return (FRONTEND / "index.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _app_js() -> str:
|
||||||
|
return (FRONTEND / "js" / "app.js").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def test_aucun_marqueur_splash():
|
||||||
|
index = _index()
|
||||||
|
app_js = _app_js()
|
||||||
|
for marqueur in MARQUEURS_SPLASH:
|
||||||
|
assert marqueur not in index, f"{marqueur} encore dans index.html"
|
||||||
|
assert marqueur not in app_js, f"{marqueur} encore dans js/app.js"
|
||||||
|
|
||||||
|
|
||||||
|
def test_pas_de_masquage_global_au_demarrage():
|
||||||
|
"""Aucune règle ne cache tout le <body> en attendant un signal :
|
||||||
|
sans elle, un chargement lent ne peut plus donner un écran blanc
|
||||||
|
bloqué."""
|
||||||
|
index = _index()
|
||||||
|
assert "body:not(.app-booted)" not in index
|
||||||
|
assert "display: none !important" not in index
|
||||||
|
|
||||||
|
|
||||||
|
def test_boot_js_conserve_et_indicateurs_internes_intacts():
|
||||||
|
"""Le démarrage SPA reste branché ; seuls le splash et son signal
|
||||||
|
disparaissent. L'écran de connexion et la barre de progression
|
||||||
|
de recherche (indicateurs internes aux vues) ne sont PAS visés."""
|
||||||
|
app_js = _app_js()
|
||||||
|
assert 'document.addEventListener("DOMContentLoaded"' in app_js
|
||||||
|
assert "await init();" in app_js
|
||||||
|
assert "Sync.init();" in app_js
|
||||||
|
assert "populateVersions" in app_js
|
||||||
|
index = _index()
|
||||||
|
assert 'id="login-screen"' in index
|
||||||
|
assert 'id="search-progress-bar"' in index
|
||||||
|
assert 'id="app"' in index
|
||||||
Reference in New Issue
Block a user