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>
73 lines
3.0 KiB
Bash
Executable File
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/"
|