Phase 1: wrap the reference web app in Capacitor for Android

Wraps reference/sp404-layers as a sideloadable APK without editing the
reference, which stays the frozen spec for the Phase 2 React port.

- sync-www.sh builds www/ from reference/ + native/, asserting each patch
  anchor so a reference change fails loudly rather than shipping broken
- drops the service worker (would serve a stale app after an APK update)
- haptics via a navigator.vibrate override, so buzz() needs no change
- localStorage mirrored into Preferences; sync load()/save() untouched
- hardware back: sheet -> Explore tab -> exit
- edge-to-edge handled in MainActivity: Android 16 + targetSdk 36 removes
  the opt-out, and a WebView never fills env(safe-area-inset-*), so insets
  are pushed into CSS custom properties instead

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-16 00:26:35 +03:00
commit 1a8a12f7f3
75 changed files with 3542 additions and 0 deletions

58
scripts/sync-www.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/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")
open(path, "w", encoding="utf-8").write(html)
print("sync-www: patched index.html (4/4 anchors matched)")
PY
echo "sync-www: $OUT/ built from $SRC/"