diff --git a/ui/index.html b/ui/index.html
index f1f5282..9d150d6 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -162,7 +162,7 @@
['numeric separator 1_000', 'return 1_000', 75],
['dynamic import()', 'return import("")', 63],
];
- var synPre = section('JS SYNTAX (esbuild target is es2022 — the bundle uses these)');
+ var synPre = section('JS SYNTAX (engine feature ceiling — first NO dates the engine)');
function mark(ok) {
return ok ? 'OK ' : 'NO ';
}
diff --git a/ui/vite.config.ts b/ui/vite.config.ts
index f4d0d8a..817abff 100644
--- a/ui/vite.config.ts
+++ b/ui/vite.config.ts
@@ -54,7 +54,13 @@ export default defineConfig(({ mode }) => ({
},
},
build: {
- target: 'es2022',
+ // Down-level to es2019 so an old Android System WebView (seen: Telegram/VK in-app WebView on
+ // Chromium 66, Android 9) can parse the bundle — esbuild lowers es2020+ syntax (?., ??, private
+ // fields, static blocks, …) that Chrome 66 rejects with "Unexpected token ?", which left the SPA
+ // on a white screen. esbuild lowers syntax only, not runtime APIs; the app's own source uses
+ // none beyond this floor (the mock-only helpers are tree-shaken from production). Reach for
+ // @vitejs/plugin-legacy if runtime-API polyfills for even older engines turn out to be needed.
+ target: 'es2019',
// Emit sourcemaps everywhere except the production build. A shipped `.map`
// carries full `sourcesContent` — the entire TypeScript/Svelte source — and the
// gateway/landing images serve `dist/` verbatim, so production maps would expose