Files
404-Layers/scripts/sync-www.sh
ncode 066b9e8c34 Render between the system bars deterministically; delete my inset bridge
The app sometimes drew under the status and gesture bars and sometimes
between them. Cause: Capacitor 8 ships a SystemBars plugin that already
handles insets, from a listener on the decor view, and my MainActivity
had installed another one on the WebView. Different views, so they
stacked rather than replaced each other and the result depended on which
fired last.

SystemBars switches on the viewport meta tag. With viewport-fit=cover it
passes insets through once onPageCommitVisible has checked the tag, decor
padding drops to 0 and the WebView grows 735 -> 820, leaving the page to
place itself from injected CSS vars. Without it the plugin pads the decor
view and the WebView is laid out between the bars already.

So sync-www.sh now strips viewport-fit=cover from the generated www/
(patch 5), MainActivity is an empty BridgeActivity, native.css and
native.js no longer touch insets, and bar style moves to
capacitor.config.json. No native->web handshake remains, so there is
nothing to race and no launch-time layout jump.

Verified identical across 3 cold launches, 2 resumes and an in-place
reload: h=735 padTop=8px overflowBy=0, panel 485px, pad rows 44.8px.

Costs no space: 820-45-114 and 735-8-66 both leave 661px. The bars take
the same room either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 23:26:31 +03:00

73 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Builds www/ from reference/ for the Capacitor wrapper.
#
# reference/ is the frozen spec and is NEVER edited. Everything native-specific
# is either an additive file (native.css, native.js) or one of the three
# removals below. Each patch asserts its anchor exists, so if the reference
# ever changes shape this fails loudly instead of silently shipping a broken app.
set -euo pipefail
cd "$(dirname "$0")/.."
SRC="reference"
OUT="www"
[ -f "$SRC/index.html" ] || { echo "missing $SRC/index.html"; exit 1; }
rm -rf "$OUT"
mkdir -p "$OUT"
# sw.js and README.md are deliberately not copied:
# - the service worker would cache index.html and serve a stale app after an
# APK update; assets are already local so it buys nothing.
# - README documents the web deployment, irrelevant inside the APK.
cp -r "$SRC/fonts" "$SRC/icons" "$OUT/"
cp "$SRC/manifest.webmanifest" "$OUT/" # harmless, keeps icon refs valid
cp "$SRC/index.html" "$OUT/index.html"
cp native/native.css native/native.js "$OUT/"
python3 - "$OUT/index.html" <<'PY'
import sys, re
path = sys.argv[1]
html = open(path, encoding="utf-8").read()
def patch(html, anchor, replacement, label):
if anchor not in html:
sys.exit(f"sync-www: anchor not found for '{label}':\n {anchor[:90]}")
return html.replace(anchor, replacement, 1)
# 1. drop the service worker registration
sw = '<script>if("serviceWorker" in navigator&&location.protocol!=="file:")addEventListener("load",()=>navigator.serviceWorker.register("sw.js"));</script>\n'
html = patch(html, sw, "", "service worker registration")
# 2. drop the web manifest link (the APK's own manifest governs the app)
html = patch(html, '<link rel="manifest" href="manifest.webmanifest">', "", "manifest link")
# 3. native stylesheet last in <head> so its overrides win on equal specificity
html = patch(html, "</head>",
'<link rel="stylesheet" href="native.css">\n</head>', "head close")
# 4. native script after the app script, so its globals already exist
html = patch(html, "</body>",
'<script src="native.js"></script>\n</body>', "body close")
# 5. drop viewport-fit=cover.
#
# Capacitor's SystemBars plugin switches behaviour on this exact string. With
# it, shouldPassthroughInsets becomes true once onPageCommitVisible has checked
# the meta tag, decor padding drops to 0, the WebView grows from 735 to 820 and
# the page must position itself from injected CSS variables - a native->web
# handshake that raced, so the app sometimes drew under the status and gesture
# bars. Without it the plugin pads the decor view instead and the page is always
# laid out between the bars, with no handshake and no launch-time jump.
#
# The web build keeps the cover behaviour; this only applies to the APK.
html = patch(html, 'content="width=device-width,initial-scale=1,viewport-fit=cover"',
'content="width=device-width,initial-scale=1"', "viewport-fit=cover")
open(path, "w", encoding="utf-8").write(html)
print("sync-www: patched index.html (5/5 anchors matched)")
PY
echo "sync-www: $OUT/ built from $SRC/"