diff --git a/assets/icon-180.png b/assets/icon-180.png new file mode 100644 index 0000000..591653d Binary files /dev/null and b/assets/icon-180.png differ diff --git a/assets/icon-192.png b/assets/icon-192.png new file mode 100644 index 0000000..17357e3 Binary files /dev/null and b/assets/icon-192.png differ diff --git a/assets/icon-512.png b/assets/icon-512.png new file mode 100644 index 0000000..c02d04a Binary files /dev/null and b/assets/icon-512.png differ diff --git a/build.sh b/build.sh index 0c64958..bb2c5c6 100755 --- a/build.sh +++ b/build.sh @@ -46,12 +46,18 @@ def build(name): out.write_text(src) return out.stat().st_size -for name in ("index.html","editor.html","editor-beta.html","pm_e-2.html","player.html","teacher.html","stage.html","micro.html","showcase.html","kit.html","explorer.html","grid.html", +for name in ("index.html","editor.html","editor-beta.html","pm_e-2.html","player.html","mobile.html","teacher.html","stage.html","micro.html","showcase.html","kit.html","explorer.html","grid.html", "embed.html", "info-editor.html","info-pm_e-2.html","info-player.html","info-teacher.html","info-stage.html","info-micro.html","info-showcase.html","info-kit.html","info-explorer.html","info-grid.html"): print("built %s (%dKB)" % (name, build(name) // 1024)) pathlib.Path("dist/embed.js").write_text(pathlib.Path("embed.js").read_text()) # loader, served as-is print("copied embed.js") +# PWA support files for mobile.html (the phone/tablet app): manifest, service worker, icons. +for f in ("manifest.webmanifest", "mobile-sw.js"): + pathlib.Path("dist/" + f).write_text(pathlib.Path(f).read_text()) +for f in ("icon-192.png", "icon-512.png", "icon-180.png"): + pathlib.Path("dist/" + f).write_bytes((A / f).read_bytes()) +print("copied PWA files (manifest.webmanifest, mobile-sw.js, icon-{192,512,180}.png)") pathlib.Path("dist/pico-main.py").write_text(pathlib.Path("pico/main.py").read_text()) # PM_K-1 firmware, downloadable print("copied pico-main.py") _appsrc = pathlib.Path("pico-cp/app.py").read_text() diff --git a/deploy.sh b/deploy.sh index 06515bc..5ad06d4 100755 --- a/deploy.sh +++ b/deploy.sh @@ -40,13 +40,17 @@ fi # stamp the version into the built copy only (source stays clean) echo "deployed v$BUILD -> $DEST_DIR" -for f in index.html editor.html editor-beta.html pm_e-2.html player.html teacher.html stage.html micro.html showcase.html kit.html explorer.html grid.html \ +for f in index.html editor.html editor-beta.html pm_e-2.html player.html mobile.html teacher.html stage.html micro.html showcase.html kit.html explorer.html grid.html \ embed.html \ info-editor.html info-pm_e-2.html info-player.html info-teacher.html info-stage.html info-micro.html info-showcase.html info-kit.html info-explorer.html info-grid.html; do sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f" echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)" done cp "$DIST_DIR/embed.js" "$DEST_DIR/embed.js"; echo " embed.js ($(stat -c '%s' "$DEST_DIR/embed.js") bytes)" +# PWA assets for mobile.html (manifest + service worker + icons) — served at the web root +for f in manifest.webmanifest mobile-sw.js icon-192.png icon-512.png icon-180.png; do + cp "$DIST_DIR/$f" "$DEST_DIR/$f"; echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)" +done cp "$DIST_DIR/pico-main.py" "$DEST_DIR/pico-main.py"; echo " pico-main.py ($(stat -c '%s' "$DEST_DIR/pico-main.py") bytes)" # PM_K-1 firmware download # Rust firmware (RP2350) — served if built via rust/pm-kit/build.sh (gitignored artifact, not in dist/) if [[ -f "$SRC_DIR/rust/pm-kit/pm-kit.uf2" ]]; then diff --git a/index.html b/index.html index 02bf149..0f82a87 100644 --- a/index.html +++ b/index.html @@ -161,6 +161,7 @@ const SAMPLES = {}; let state = { bpm:120, volume:0.85 }, meters = [], muteWindo const VERSIONS = [ // PM_E-1 (editor.html) is hidden from the landing — PM_E-2 is the focus. The page still exists. { key:"pme2", file:"/pm_e-2.html", name:"PM_E‑2 Editor", chip:"app", h:640, sum:"The PolyMeter editor, built around engraved drum notation — a 5‑line percussion staff (Bravura/SMuFL) with Staff / TUBS / Konnakol views, edit‑on‑staff, plus flams/drags/rolls, odd meters & clave." }, + { key:"mobile", file:"/mobile.html", name:"PM_M‑1 Mobile", chip:"app", h:600, sum:"Phone & tablet app — a touch‑first, full‑screen player you can “Add to Home Screen.” Big tap targets, drag‑to‑scrub tempo, a pulsing beat display, screen‑wake‑lock, and an iOS fix for the ring/silent switch. Installable & works offline." }, { key:"kit", file:"/kit.html", name:"PM_K‑1 Kit", chip:"hw", h:560, sum:"Build it today — a Raspberry Pi Pico on the 52Pi touchscreen kit; tap the 3.5″ screen, joystick tempo, RGB beat light, buzzer. MicroPython firmware included." }, { key:"explorer", file:"/explorer.html", name:"PM_X‑1 Explorer", chip:"hw", h:500, sum:"Off‑the‑shelf — the Pimoroni Explorer (RP2350, 2.8″ LCD, 6 buttons, piezo) as a button‑driven sibling to the Kit. Edit on the web with Live sync; the device mirrors play/stop/tempo/track changes both ways." }, { key:"grid", file:"/grid.html", name:"PM_G‑1 Grid", chip:"hw", h:470, sum:"Off‑the‑shelf — a Pimoroni Pico Scroll Pack (17×7 white LED matrix + 4 buttons) on a Raspberry Pi Pico. The matrix IS the editor's lane × step pad grid in miniature; edit on the web with Live sync." }, diff --git a/manifest.webmanifest b/manifest.webmanifest new file mode 100644 index 0000000..103f2a4 --- /dev/null +++ b/manifest.webmanifest @@ -0,0 +1,18 @@ +{ + "name": "VARASYS PolyMeter", + "short_name": "PolyMeter", + "description": "Polymetric groove-trainer & metronome — touch-first, full-screen.", + "id": "/mobile.html", + "start_url": "/mobile.html?standalone=1", + "scope": "/mobile.html", + "display": "standalone", + "display_override": ["standalone", "fullscreen"], + "orientation": "any", + "background_color": "#05070a", + "theme_color": "#0b0d11", + "categories": ["music", "productivity", "utilities"], + "icons": [ + { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, + { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } + ] +} diff --git a/mobile-sw.js b/mobile-sw.js new file mode 100644 index 0000000..b21bf11 --- /dev/null +++ b/mobile-sw.js @@ -0,0 +1,51 @@ +/* Service worker for the PolyMeter mobile app (mobile.html). + * + * Deliberately minimal and non-intrusive: it only manages its OWN app-shell URLs + * (the page, manifest, icons). For every other request it does NOT call + * respondWith(), so the rest of the site behaves exactly as if no SW existed. + * + * Strategy for the shell: network-first, fall back to cache. The page is a single + * self-contained file that is version-stamped on deploy, so when the device is + * online it always gets the freshest build; offline it still launches from cache. + */ +const CACHE = "polymeter-mobile-v1"; +const SHELL = [ + "/mobile.html", + "/manifest.webmanifest", + "/icon-192.png", + "/icon-512.png", + "/icon-180.png", +]; +const SHELL_PATHS = new Set(SHELL); + +self.addEventListener("install", (e) => { + self.skipWaiting(); + e.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).catch(() => {})); +}); + +self.addEventListener("activate", (e) => { + e.waitUntil( + caches.keys() + .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) + .then(() => self.clients.claim()) + ); +}); + +self.addEventListener("fetch", (e) => { + const req = e.request; + if (req.method !== "GET") return; + const url = new URL(req.url); + if (url.origin !== self.location.origin) return; + // Treat any navigation to /mobile.html (with or without ?standalone=1 etc.) as the shell. + const path = url.pathname; + if (!SHELL_PATHS.has(path)) return; // not ours — let the browser handle it + + e.respondWith( + fetch(req) + .then((res) => { + if (res && res.ok) { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(path, copy)); } + return res; + }) + .catch(() => caches.match(path).then((hit) => hit || caches.match("/mobile.html"))) + ); +}); diff --git a/mobile.html b/mobile.html new file mode 100644 index 0000000..49f070b --- /dev/null +++ b/mobile.html @@ -0,0 +1,448 @@ + + +
+ + + +