SkateSpots/index.html
shakespeare.diy 5334144d27 Fix native white screen: relative asset base + boot crash guard
Two changes targeting the post-update white screen on the Zapstore APK:

1. vite.config: set base './' when CAPACITOR_BUILD is set. In a native
   WebView the bundle is served from the local filesystem, so absolute '/'
   asset paths are the classic cause of a silent white screen. The web
   deploy keeps '/'. This is the documented Capacitor requirement and is
   zero-risk to the working web build.

2. index.html: add a dependency-free "boot crash guard" that runs before
   the app bundle and paints any uncaught error / unhandled rejection —
   plus a 12s "did not render" timeout — directly to the screen using
   inline styles only. A boot failure that would otherwise be an
   info-less white screen now shows the actual error ON DEVICE, with no
   USB/remote-debugging needed. main.tsx signals a clean boot to cancel
   the timeout guard.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 18:21:57 +07:00

103 lines
5.3 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<!-- CSP intentionally omitted: app is sandboxed by Android/iOS at the OS level.
A restrictive CSP here breaks Capacitor's internal asset loading scheme. -->
<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.webmanifest">
<!-- App Icons -->
<link rel="icon" type="image/png" href="/images/icon-1024.png">
<link rel="apple-touch-icon" href="/images/icon-1024.png">
<link rel="apple-touch-icon" sizes="152x152" href="/images/icon-1024.png">
<link rel="apple-touch-icon" sizes="180x180" href="/images/icon-1024.png">
<link rel="apple-touch-startup-image" href="/images/icon-1024.png">
<!-- Theme Color -->
<meta name="theme-color" content="#ff0040">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="SkateSpots">
<!-- SEO Meta Tags -->
<meta name="description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr. Free, decentralized, censorship-resistant.">
<meta name="keywords" content="skateboarding, skate spots, skate parks, nostr, decentralized, bitcoin, lightning, zaps">
<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:title" content="SkateSpots - Decentralized Skate Map & Community">
<meta property="og:description" content="Discover skate spots, find coaches, join events, and connect with the skateboarding community on Nostr.">
<meta property="og:image" content="https://skate-spots.org/images/icon-1024.png">
<meta property="og:url" content="https://skate-spots.org">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="SkateSpots - Decentralized Skate Map">
<meta name="twitter:description" content="Discover skate spots, find coaches, join events on Nostr.">
<meta name="twitter:image" content="https://skate-spots.org/images/icon-1024.png">
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
</head>
<body>
<div id="root"></div>
<!--
BOOT CRASH GUARD (native-safe).
Runs before the app bundle and paints any uncaught boot error directly to
the screen using inline styles only — so a failure that would otherwise be
a silent white screen (bundle load error, module-level throw, missing CSS)
becomes a readable message on device, with no cable/remote-debug needed.
If the app boots normally it clears itself and costs nothing.
-->
<script>
(function () {
var shown = false;
function paint(kind, err) {
if (shown) return;
shown = true;
try {
var msg = (err && (err.stack || err.message)) ? (err.stack || err.message) : String(err);
var el = document.createElement('div');
el.setAttribute('style', [
'position:fixed', 'inset:0', 'z-index:2147483647',
'background:#000', 'color:#fff', 'padding:20px',
'font:13px/1.5 -apple-system,system-ui,monospace',
'overflow:auto', '-webkit-overflow-scrolling:touch'
].join(';'));
el.innerHTML =
'<div style="color:#ff0040;font-weight:700;font-size:16px;margin-bottom:8px">SkateSpots failed to start</div>' +
'<div style="color:#9ca3af;margin-bottom:12px">' + kind + '</div>' +
'<pre style="white-space:pre-wrap;word-break:break-word;color:#f3f4f6;margin:0">' +
(msg ? msg.replace(/[<>&]/g, function (c) { return { '<': '&lt;', '>': '&gt;', '&': '&amp;' }[c]; }) : '(no message)') +
'</pre>' +
'<button onclick="location.reload()" style="margin-top:16px;padding:10px 16px;background:#ff0040;color:#000;border:0;border-radius:8px;font-weight:700">Reload</button>';
document.body.appendChild(el);
} catch (e) { /* last resort: nothing we can do */ }
}
window.addEventListener('error', function (e) {
paint('Uncaught error', e.error || e.message);
});
window.addEventListener('unhandledrejection', function (e) {
paint('Unhandled promise rejection', e.reason);
});
// If nothing has rendered into #root after 12s, surface a hint.
setTimeout(function () {
var root = document.getElementById('root');
if (root && root.childElementCount === 0) {
paint('App did not render (timeout)', 'The bundle loaded but nothing mounted within 12s. This usually means a boot-time exception was swallowed.');
}
}, 12000);
// Let the app signal a clean boot to cancel the timeout guard.
window.__SKATESPOTS_BOOTED__ = function () { shown = true; };
})();
</script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>