fix: address review-pass findings

- GameView: ignore game keys (Esc/R/U/Z) while the donate modal is open
  so its own Escape no longer doubles as a navigate-to-levels.
- GameView: tag the win dialog with role/aria-modal/aria-labelledby so
  screen readers announce it correctly.
- LevelSelectView: read the progress snapshot once per page render
  instead of two localStorage parses per visible tile (40 reads -> 1).
- progress-store: add snapshot() method for batch reads.
- vite-plugin-pwa: include jpg in the workbox precache glob so the
  donation QR keeps working offline.
This commit is contained in:
tiennm99 committed 2026-04-27 20:51:37 +07:00
1 parent fe2d98e710
commit dff4ea9eab
4 files changed
+19 -5

No files matched your search

+8
View File
@@ -46,6 +46,14 @@ export const progressStore = {
return Object.keys(readRaw().completed).length;
},
/**
* Returns the full progress snapshot. Lets callers read many levels
* without paying for one parse + one localStorage read per query.
*/
snapshot() {
return readRaw();
},
reset() {
writeRaw({ completed: {}, bestMoves: {} });
}
+5 -2
View File
@@ -97,6 +97,9 @@
let lastKeyAt = 0;
function onKey(e) {
// Don't route gameplay keys while the donate modal is open — its own
// Escape handler should be the only consumer.
if (donateOpen) return;
if (e.key === 'Escape') { onLevels(); return; }
if (e.key === 'r' || e.key === 'R') { restart(); return; }
if (e.key === 'u' || e.key === 'U' || e.key === 'z' || e.key === 'Z') { undo(); return; }
@@ -181,8 +184,8 @@
{#if won}
<div class="overlay">
<div class="dialog">
<h2>LEVEL COMPLETE!</h2>
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="win-title" tabindex="-1">
<h2 id="win-title">LEVEL COMPLETE!</h2>
<p class="final">Moves: <strong>{moves}</strong></p>
<div class="dialog-actions">
{#if hasNext}
+4 -2
View File
@@ -18,14 +18,16 @@
let completedCount = $state(progressStore.getCompletedCount());
let visibleLevels = $derived.by(() => {
// Read storage once per page render instead of 2× per visible level.
const { completed, bestMoves } = progressStore.snapshot();
const start = page * PER_PAGE;
const end = Math.min(start + PER_PAGE, total);
const out = [];
for (let i = start; i < end; i++) {
out.push({
index: i,
done: progressStore.isCompleted(i),
best: progressStore.getBestMoves(i)
done: !!completed[i],
best: bestMoves[i] ?? null
});
}
return out;
+2 -1
View File
@@ -26,7 +26,8 @@ export default defineConfig({
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,png,svg,webmanifest}']
// jpg is needed so the donation QR image works offline.
globPatterns: ['**/*.{js,css,html,png,jpg,svg,webmanifest}']
}
})
]