From de2f1432be6def33a3812ab2c54ce35c4ee000a4 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sat, 4 Jul 2026 17:40:31 +0200 Subject: [PATCH] chore(ui): surface caught boot errors on the diagnostic panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The es2019 + conditional core-js fixes let the module mount on the Chromium 66 in-app WebView, but the lobby then shows a white screen and a generic "something went wrong" toast — a caught error that never reaches window.onerror, so the diagnostic ERRORS panel stayed empty and could not name the cause. Mirror console.error / console.warn into the panel, and add a temporary, mock-gated console.error of the raw error (with stack) in handleError, so the on-device panel shows exactly what threw and whether it is one error or several. Expected: a ReferenceError from the 64-bit FlatBuffers timestamp decode (BigInt, absent on Chrome 66 and not polyfillable by core-js) — to be confirmed on-device before deciding the support floor. Temporary — both hunks revert together with the index.html BOOT-DIAG block. --- ui/index.html | 28 ++++++++++++++++++++++++++++ ui/src/lib/app.svelte.ts | 6 ++++++ 2 files changed, 34 insertions(+) diff --git a/ui/index.html b/ui/index.html index 28c2bbd..cc2c2f8 100644 --- a/ui/index.html +++ b/ui/index.html @@ -143,6 +143,34 @@ var r = e && e.reason; logErr('unhandledrejection', r && (r.stack || r.message) ? r.stack || r.message : String(r)); }); + // Mirror console.error / console.warn into the panel. The app catches its boot failures and + // surfaces them only as a toast (they never reach window.onerror); a temporary console.error + // at the app's handleError routes the real cause here, so the panel shows what actually threw. + function fmt(a) { + if (a && (a.stack || a.message)) return a.stack || a.message; + if (typeof a === 'object') { + try { + return JSON.stringify(a); + } catch (e) { + return String(a); + } + } + return String(a); + } + function wrapConsole(orig, label) { + return function () { + try { + var parts = []; + for (var i = 0; i < arguments.length; i++) parts.push(fmt(arguments[i])); + logErr('console.' + label, parts.join(' ')); + } catch (e) {} + if (orig) return orig.apply(console, arguments); + }; + } + if (window.console) { + console.error = wrapConsole(console.error, 'error'); + console.warn = wrapConsole(console.warn, 'warn'); + } // ---- environment ----------------------------------------------------------------------- var ua = nav.userAgent || '(no userAgent)'; diff --git a/ui/src/lib/app.svelte.ts b/ui/src/lib/app.svelte.ts index 07be39e..8e86829 100644 --- a/ui/src/lib/app.svelte.ts +++ b/ui/src/lib/app.svelte.ts @@ -283,6 +283,12 @@ export function markChatRead(gameId: string): void { * failure — or anything raised while the app is mid-reconnect — is shown by the "Connecting…" * header indicator (and auto-retried), never a red toast. */ export function handleError(err: unknown): void { + // TEMP (boot diagnostic): the toast is generic, so route the real caught error to the on-device + // BOOT-DIAG panel via console (it mirrors console.error). Mode-gated to keep it out of the mock + // e2e. Revert together with the index.html BOOT-DIAG block. + if (import.meta.env.MODE !== 'mock') { + console.error('[diag] handleError:', err instanceof Error ? err.stack || err.message : err); + } const code = err instanceof GatewayError ? err.code : ''; if (code === 'session_invalid' || code === 'unauthenticated') { void logout();