Boot-failure seatbelt: no future crash becomes a silent white screen

Per Codex. A branded recovery card in app.html shows if the app hasn't mounted
in 7s, or on a pre-mount JS error/unhandledrejection — with a "Refresh Upbeat
Bytes" button. A chunk/preload failure (vite:preloadError) reloads once
(sessionStorage-guarded). +layout calls window.__ubBooted() on mount to clear
the card + timer. A pre-mount failure also fires a tiny anonymous client_error
beacon; the admin Overview now shows "Load errors today" (red if >0) so we can
see if blank-risk is happening in the wild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-11 12:10:46 -04:00
parent 254db67055
commit 9e387a0a09
5 changed files with 76 additions and 0 deletions
+58
View File
@@ -19,8 +19,66 @@
<meta name="twitter:title" content="Upbeat Bytes — calm, constructive news" />
<meta name="twitter:description" content="Calm, constructive news, summarized — get the gist, go deeper only if you want." />
%sveltekit.head%
<style>
#boot-fallback {
display: none; position: fixed; inset: 0; z-index: 9999;
align-items: center; justify-content: center; background: #f7f4ec;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; padding: 24px;
}
#boot-fallback .bf { text-align: center; max-width: 360px; }
#boot-fallback img { height: 46px; width: auto; margin-bottom: 18px; }
#boot-fallback p { color: #4a5560; font-size: 1rem; line-height: 1.55; margin: 0 0 20px; }
#boot-fallback button {
background: #0083ad; color: #fff; border: none; border-radius: 999px;
padding: 12px 26px; font: inherit; font-weight: 600; font-size: 0.95rem; cursor: pointer;
}
</style>
<script>
// Reliability seatbelt: never let a slow/failed boot become a silent white
// screen. Show a calm recovery card if the app hasn't mounted, and reload
// once on a chunk/preload failure (e.g. a just-deployed hashed chunk).
(function () {
function showBoot() {
if (window.__ubMounted) return; // app is running; in-app handles it
var el = document.getElementById('boot-fallback');
if (el) el.style.display = 'flex';
try {
var b = new Blob([JSON.stringify({ kind: 'client_error', visitor: 'e' + Math.random().toString(36).slice(2) })],
{ type: 'application/json' });
navigator.sendBeacon && navigator.sendBeacon('/api/events', b);
} catch (e) { /* best-effort telemetry */ }
}
var timer = setTimeout(showBoot, 7000);
// Svelte calls this once it has mounted (see +layout.svelte).
window.__ubBooted = function () {
window.__ubMounted = true;
clearTimeout(timer);
var el = document.getElementById('boot-fallback');
if (el && el.parentNode) el.parentNode.removeChild(el);
try { sessionStorage.removeItem('ub_reloaded'); } catch (e) {}
};
addEventListener('vite:preloadError', function (e) {
try {
if (!sessionStorage.getItem('ub_reloaded')) {
sessionStorage.setItem('ub_reloaded', '1');
e.preventDefault();
location.reload();
}
} catch (err) { /* ignore */ }
});
addEventListener('error', showBoot);
addEventListener('unhandledrejection', showBoot);
})();
</script>
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<div id="boot-fallback" role="alert" aria-live="polite">
<div class="bf">
<img src="%sveltekit.assets%/logo.svg" alt="Upbeat Bytes" />
<p>We had a little trouble loading. A quick refresh usually sorts it out.</p>
<button type="button" onclick="location.reload()">Refresh Upbeat Bytes</button>
</div>
</div>
</body>
</html>