feat(ui): support old Android in-app WebViews (es2019 + core-js + engine screen)
CI / changes (push) Successful in 2s
CI / unit (push) Has been skipped
CI / integration (push) Has been skipped
CI / ui (push) Successful in 1m5s
CI / conformance (push) Successful in 9s
CI / gate (push) Successful in 0s
CI / deploy (push) Successful in 1m54s

Old Android System WebViews (the Telegram/VK in-app browser; a real device with Google
Play auto-updates the WebView, but emulators / no-Play / restricted devices stay frozen)
showed a white screen. Two causes, fixed in order:

- build.target es2022 shipped ?./?? verbatim, which the old engine cannot parse. Lowered
  to es2019 so esbuild down-levels the syntax.
- The es2020+ runtime globals the bundle and its deps call (globalThis, structuredClone,
  Array.at, ...) are missing on those engines. A conditional core-js loader (emitPolyfills
  writes dist/polyfills.js, document.write'd by an index.html gate only when needed) covers
  them; modern engines download nothing, so the bundle-size budget is untouched.

BigInt (the 64-bit FlatBuffers timestamp decode) and Proxy (Svelte 5 runes) cannot be
polyfilled, so the effective floor is Chrome 67. Below it, a permanent ES5 boot guard in
index.html shows a friendly "this device's OS or browser can't run the app" screen (with
the web-version link inside a Mini App) and a "Diagnostic information" view with a Copy
button, instead of a white screen. A reactive net raises the same screen on an uncaught
boot error that never signals window.__booted.

Board tile glyphs used container-query units (cqw, Chrome 105+) under .cell font-size:0,
so they collapsed to 0px on Chrome 74 (invisible letters); added vmin fallbacks.

The unsupported-engine telemetry beacon is a separate follow-up PR.
This commit was merged in pull request #176.
This commit is contained in:
2026-07-04 20:36:05 +00:00
parent 44117e906c
commit 4dfedd02a3
7 changed files with 280 additions and 4 deletions
+11
View File
@@ -479,6 +479,11 @@
position: absolute;
top: 5%;
left: 8%;
/* Chrome < 105 has no container-query units: a dropped `cqw` would inherit the .cell
`font-size: 0` and the glyph would vanish. Fall back to a viewport-relative size that tracks
the board (≈ the viewport-fitted query container) and the zoom (--z), so old Android System
WebViews still draw the tile glyphs. Chrome 105+ takes the exact `cqw` line below. */
font-size: calc(4.2vmin * var(--z, 1));
font-size: 4.2cqw;
font-weight: 700;
line-height: 1;
@@ -487,12 +492,14 @@
position: absolute;
right: 5%;
bottom: 3%;
font-size: calc(2.4vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 2.4cqw;
font-weight: 600;
}
/* A placed Erudit blank ("звёздочка") shows its star where the (absent) point value sits,
its ink centred on the same line as a neighbouring tile's value digit. */
.blankmark {
font-size: calc(2.8vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 2.8cqw;
bottom: 0;
}
@@ -501,6 +508,7 @@
inset: 0;
display: grid;
place-items: center;
font-size: calc(3.6vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 3.6cqw;
opacity: 0.7;
}
@@ -509,6 +517,7 @@
inset: 0;
display: grid;
place-items: center;
font-size: calc(2.7vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 2.7cqw;
font-weight: 600;
opacity: 0.9;
@@ -526,10 +535,12 @@
padding: 0 1px;
}
.bt {
font-size: calc(1.7vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 1.7cqw;
font-weight: 600;
}
.bb {
font-size: calc(1.9vmin * var(--z, 1)); /* cqw fallback for Chrome < 105 — see .letter */
font-size: 1.9cqw;
font-weight: 700;
white-space: nowrap;