Make Crystal an installable web app with an offline shell (#39)
Checks / web (push) Failing after 16s
Private ROM checks / private-rom (push) Failing after 17m47s

A manifest and icons rendered from icon.svg make the app installable.
The native build emits a service worker that precaches exactly the
built files and passes everything else to the network, so ROMs never
reach its cache. New versions wait for the player's Update, which saves
before reloading. Savegames offers installation, iPhone steps and
persistent storage, and a full disk gets its own save error.

Acceptance on physical iPhone and Android devices remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
samyandClaude Opus 5.5 committed 2026-10-03 20:51:57 +02:00
1 parent f289615bd2
commit ce8129703f
20 files changed
+661 -15

No files matched your search

+2 -1
View File
@@ -10,6 +10,7 @@ export function renderShell(root: HTMLElement) {
<a class="brand" href="./" aria-label="Crystal home">CRYSTAL</a>
<div class="header-state"><span class="power"><i id="power-light"></i><span id="power-label">STANDBY</span></span><button id="settings" class="text-button">Settings</button><button id="help" class="text-button">Help</button></div>
</header>
<div id="update-banner" class="update-banner" role="status" hidden><span>A new version of Crystal is ready.</span><button id="update-app" class="text-button">Update</button></div>
<main class="app-shell">
<nav class="tabs" role="tablist" aria-label="Crystal sections">
${tab("game", "Game", true)}${tab("saves", "Savegames")}${tab("map", "Map")}${tab("pokedex", "Pokédex")}${tab("link", "Link")}${tab("friends", "Friends")}
@@ -62,7 +63,7 @@ export function renderShell(root: HTMLElement) {
<header><p class="menu-title">SAVEGAMES</p><h2>Your local session</h2></header>
<div class="save-actions"><button id="save" disabled>Save session</button><button id="backup" disabled>Export backup</button><button id="import-save" disabled>Import native backup</button><input id="save-file" class="file-input" type="file" accept=".json"></div>
<div class="save-copy"><p>Native backups include your character, position and completed interactions. They are separate from cartridge .sav files.</p><p>Export before clearing browser data.</p></div>
<div class="replace-rom"><h3>Cartridge</h3><p>Select a ROM again after reloading, or choose another compatible copy.</p><label class="menu-button file-button" for="rom-file"><span aria-hidden="true">▶</span> Choose or replace ROM</label></div>
<div class="replace-rom"><h3>Cartridge</h3><p>Select a ROM again after reloading, or choose another compatible copy.</p><label class="menu-button file-button" for="rom-file"><span aria-hidden="true">▶</span> Choose or replace ROM</label></div><div class="app-settings"><h3>App</h3><p id="install-status">Install Crystal to open it from your home screen and play the app offline. Select your ROM again each time you open it.</p><button id="install-app" class="menu-button" hidden>Install Crystal</button><h3>Storage</h3><p id="storage-status">Checking this browser's storage…</p><button id="persist-storage" class="menu-button" hidden>Keep saves on this device</button></div>
</section>
<section id="panel-map" class="tab-panel menu-panel map-panel" role="tabpanel" aria-labelledby="tab-map" data-panel="map" hidden>
<header><p class="menu-title">MAP</p><h2 id="map-location">No area loaded</h2><p id="map-position">Choose a ROM to see your position.</p></header>