Skip to content

Commit d783a16

Browse files
tsemachhclaude
andcommitted
PC load-speed fixes, consistent recent cards, native-AF-ready toggle
- SW: heavy assets (emu/*, posters/) move to a persistent static cache that survives deploys — no more 12MB wasm re-download after every shell deploy (the main cause of slow PC loads lately). Navigations stay network-first but now race a 1.5s timeout and fall back to cache, so a slow connection never blocks a launch. - Recently-played cards fixed to the grid card footprint (170px). - AF toggle prefers the emulator's native ATWasmSetAutoFire (upstream 27d37558) with the JS pulse as fallback until the bundle updates. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2a8b79e commit d783a16

4 files changed

Lines changed: 86 additions & 32 deletions

File tree

emu/index.html

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7837,20 +7837,31 @@ <h2 id="fw-title">Download standard Atari ROMs?</h2>
78377837
try { if (window.Module && Module._ATWasmSetJoystick) Module._ATWasmSetJoystick(ext.mask, ext.fire ? 1 : 0); } catch (e) {}
78387838
}
78397839

7840-
// ---- auto-fire toggle (~11 Hz pulse on the external trigger) -------
7840+
// ---- auto-fire toggle ----------------------------------------------
7841+
// Prefers the emulator's native auto-fire (ATWasmSetAutoFire, added
7842+
// upstream after our #85: holding the fire button pulses in-core).
7843+
// Falls back to an ~11 Hz JS pulse on the external trigger when the
7844+
// bundle predates the export.
78417845
var afBtn = document.getElementById('arcade-af');
78427846
var afTimer = null;
7847+
function afNative() {
7848+
try { return !!(window.Module && Module._ATWasmSetAutoFire); } catch (e) { return false; }
7849+
}
78437850
if (afBtn) afBtn.addEventListener('click', function (e) {
78447851
e.preventDefault(); e.stopPropagation();
7845-
if (afTimer) {
7846-
clearInterval(afTimer); afTimer = null;
7847-
afBtn.classList.remove('on');
7848-
ext.fire = false; pushExt();
7849-
} else {
7850-
afBtn.classList.add('on');
7852+
var on = !afBtn.classList.contains('on');
7853+
afBtn.classList.toggle('on', on);
7854+
if (afNative()) {
7855+
try { Module._ATWasmSetAutoFire(on ? 1 : 0); } catch (err) {}
7856+
if (afTimer) { clearInterval(afTimer); afTimer = null; }
7857+
if (!on && ext.fire) { ext.fire = false; pushExt(); }
7858+
} else if (on) {
78517859
afTimer = setInterval(function () {
78527860
ext.fire = !ext.fire; pushExt();
78537861
}, 45);
7862+
} else {
7863+
if (afTimer) { clearInterval(afTimer); afTimer = null; }
7864+
ext.fire = false; pushExt();
78547865
}
78557866
});
78567867

index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@
102102
padding-bottom: 4px;
103103
}
104104
.recent-row .card {
105-
min-width: 130px;
105+
/* same footprint as the grid cards for a consistent look */
106+
flex: 0 0 170px;
107+
width: 170px;
106108
min-height: 0;
107109
position: relative;
108110
}

scripts/emu-autofullscreen.snippet.html

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,20 +281,31 @@
281281
try { if (window.Module && Module._ATWasmSetJoystick) Module._ATWasmSetJoystick(ext.mask, ext.fire ? 1 : 0); } catch (e) {}
282282
}
283283

284-
// ---- auto-fire toggle (~11 Hz pulse on the external trigger) -------
284+
// ---- auto-fire toggle ----------------------------------------------
285+
// Prefers the emulator's native auto-fire (ATWasmSetAutoFire, added
286+
// upstream after our #85: holding the fire button pulses in-core).
287+
// Falls back to an ~11 Hz JS pulse on the external trigger when the
288+
// bundle predates the export.
285289
var afBtn = document.getElementById('arcade-af');
286290
var afTimer = null;
291+
function afNative() {
292+
try { return !!(window.Module && Module._ATWasmSetAutoFire); } catch (e) { return false; }
293+
}
287294
if (afBtn) afBtn.addEventListener('click', function (e) {
288295
e.preventDefault(); e.stopPropagation();
289-
if (afTimer) {
290-
clearInterval(afTimer); afTimer = null;
291-
afBtn.classList.remove('on');
292-
ext.fire = false; pushExt();
293-
} else {
294-
afBtn.classList.add('on');
296+
var on = !afBtn.classList.contains('on');
297+
afBtn.classList.toggle('on', on);
298+
if (afNative()) {
299+
try { Module._ATWasmSetAutoFire(on ? 1 : 0); } catch (err) {}
300+
if (afTimer) { clearInterval(afTimer); afTimer = null; }
301+
if (!on && ext.fire) { ext.fire = false; pushExt(); }
302+
} else if (on) {
295303
afTimer = setInterval(function () {
296304
ext.fire = !ext.fire; pushExt();
297305
}, 45);
306+
} else {
307+
if (afTimer) { clearInterval(afTimer); afTimer = null; }
308+
ext.fire = false; pushExt();
298309
}
299310
});
300311

sw.js

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
99
*/
1010

1111
const CACHE_VERSION = "arcade-v1";
12+
// Immutable heavy assets (emulator binaries, game files, posters) live in
13+
// a cache that SURVIVES deploys — their URLs are content-addressed (?v=
14+
// tags / new filenames), so a shell deploy must not force a 12MB wasm
15+
// re-download. Only the versioned shell cache is dropped on activate.
16+
const STATIC_CACHE = "arcade-static-v1";
1217
const SHELL = ["index.html", "manifest.webmanifest", "games.json"];
18+
function isStatic(url) {
19+
return url.pathname.indexOf("/emu/") > -1 ||
20+
url.pathname.indexOf("/posters/") > -1;
21+
}
1322

1423
self.addEventListener("install", function (event) {
1524
event.waitUntil(
@@ -30,11 +39,15 @@ self.addEventListener("install", function (event) {
3039
var files = ((data && data.games) || []).map(function (g) {
3140
return "emu/library/" + g.file;
3241
});
33-
return Promise.all(
34-
files.map(function (u) {
35-
return cache.add(u).catch(function () { /* best effort */ });
36-
}),
37-
);
42+
return caches.open(STATIC_CACHE).then(function (sc) {
43+
return Promise.all(
44+
files.map(function (u) {
45+
return sc.match(u).then(function (hit) {
46+
return hit ? null : sc.add(u).catch(function () {});
47+
});
48+
}),
49+
);
50+
});
3851
})
3952
.catch(function () { /* library warmup is best effort */ });
4053
});
@@ -53,7 +66,7 @@ self.addEventListener("activate", function (event) {
5366
return Promise.all(
5467
keys
5568
.filter(function (k) {
56-
return k !== CACHE_VERSION;
69+
return k !== CACHE_VERSION && k !== STATIC_CACHE;
5770
})
5871
.map(function (k) {
5972
return caches.delete(k);
@@ -83,29 +96,46 @@ self.addEventListener("fetch", function (event) {
8396
url.pathname.endsWith("build-info.json");
8497

8598
if (alwaysFresh) {
99+
// Network-first, but never make the user wait on a slow connection:
100+
// race the network against a short timeout and fall back to cache.
101+
// The network response still lands in the cache in the background,
102+
// so a timed-out launch is at most one deploy behind.
86103
event.respondWith(
87-
fetch(request)
88-
.then(function (response) {
89-
const copy = response.clone();
90-
caches.open(CACHE_VERSION).then(function (cache) {
91-
cache.put(request, copy);
104+
new Promise(function (resolve) {
105+
let settled = false;
106+
const timer = setTimeout(function () {
107+
caches.match(request).then(function (cached) {
108+
if (!settled && cached) { settled = true; resolve(cached); }
92109
});
93-
return response;
94-
})
95-
.catch(function () {
96-
return caches.match(request);
97-
}),
110+
}, 1500);
111+
fetch(request)
112+
.then(function (response) {
113+
clearTimeout(timer);
114+
const copy = response.clone();
115+
caches.open(CACHE_VERSION).then(function (cache) {
116+
cache.put(request, copy);
117+
});
118+
if (!settled) { settled = true; resolve(response); }
119+
})
120+
.catch(function () {
121+
clearTimeout(timer);
122+
caches.match(request).then(function (cached) {
123+
if (!settled) { settled = true; resolve(cached || Response.error()); }
124+
});
125+
});
126+
}),
98127
);
99128
return;
100129
}
101130

131+
const bucket = isStatic(url) ? STATIC_CACHE : CACHE_VERSION;
102132
event.respondWith(
103133
caches.match(request, { ignoreSearch: false }).then(function (cached) {
104134
if (cached) return cached;
105135
return fetch(request).then(function (response) {
106136
if (response && response.ok) {
107137
const copy = response.clone();
108-
caches.open(CACHE_VERSION).then(function (cache) {
138+
caches.open(bucket).then(function (cache) {
109139
cache.put(request, copy);
110140
});
111141
}

0 commit comments

Comments
 (0)