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

36
native/native.css Normal file
View File

@@ -0,0 +1,36 @@
/* Native-only overrides.
reference/ is never edited; sync-www.sh injects this as the last stylesheet
in <head>, so on equal specificity these rules win. */
:root{
/* Filled in from Android WindowInsets by MainActivity via __applyInsets().
The env() defaults keep the page correct in a plain desktop browser, where
env() works and the native bridge is absent. */
--ins-t: env(safe-area-inset-top, 0px);
--ins-b: env(safe-area-inset-bottom, 0px);
--ins-l: env(safe-area-inset-left, 0px);
--ins-r: env(safe-area-inset-right, 0px);
/* The reference derives --sab from env(safe-area-inset-bottom), which is
always 0px inside an Android WebView. Re-point it at the real inset.
.bnav and .sheet both key off --sab, so they need no further changes. */
--sab: var(--ins-b);
}
@media (max-width:820px){
/* The reference sets `padding: 8px 8px calc(var(--nav) + var(--sab) + 8px)`.
Bottom already accounts for --sab; add the top and side insets so the unit
panel clears the status bar and any display cutout. */
.app{
padding-top: calc(8px + var(--ins-t));
padding-left: calc(8px + var(--ins-l));
padding-right: calc(8px + var(--ins-r));
}
/* Bottom nav is position:fixed and spans the full width, so it needs the
side insets itself. Its height/padding-bottom already follow --sab. */
.bnav{
padding-left: var(--ins-l);
padding-right: var(--ins-r);
}
}