Skip to content

fix: offline mode for web page#99

Merged
scaryrawr merged 5 commits intomainfrom
fix/offline-mode
Jan 26, 2026
Merged

fix: offline mode for web page#99
scaryrawr merged 5 commits intomainfrom
fix/offline-mode

Conversation

@scaryrawr
Copy link
Owner

Summary

Fix offline mode for the SL web page PWA.

Problem

The service worker was failing to register, preventing the page from working offline. Additionally, stale WASM files could accumulate in the cache.

Changes

  • Service Worker: Bump cache version to v2, add cleanup logic for stale WASM files
  • Manifest: Use relative paths for GitHub Pages compatibility
  • Build: Select newest WASM file by mtime to avoid stale artifacts
  • slTerminal: Add error handling for WASM loading failures
  • Style: Format JSX components with prettier

Testing

Verified using Playwright:

  • ✅ Service worker registers successfully
  • ✅ All assets cached in sl-page-v2
  • ✅ Page loads fully offline
  • ✅ Train animation runs from cached WASM
  • ✅ No console errors

Before fix: Service worker failed to register with script evaluation error, offline navigation failed with ERR_INTERNET_DISCONNECTED.

- Bump cache version from v1 to v2
- Add cleanup logic to remove stale WASM files during activation
- Create ASSET_URLS set for efficient lookup of valid cached assets
- Change start_url from '/' to './'
- Change favicon src from '/favicon.svg' to './favicon.svg'
- Filter and sort WASM files by modification time
- Select newest file when multiple WASM files exist in lib directory
- Wrap WASM loading in try-catch block
- Display user-friendly error message when WASM fails to load
Copilot AI review requested due to automatic review settings January 26, 2026 23:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes offline functionality for the SL web page PWA by addressing service worker registration failures and implementing proper cache management.

Changes:

  • Service worker improvements: Bumped cache version to v2, added cleanup logic to remove stale WASM files while preserving current assets
  • GitHub Pages compatibility: Changed manifest.json paths from absolute (/) to relative (./) for proper PWA installation
  • Build process enhancement: Added logic to select the newest WASM file by modification time when multiple artifacts exist
  • Error handling: Added try-catch wrapper around WASM loading with user-friendly error messages
  • Code formatting: Applied prettier formatting to JSX components across multiple files

Reviewed changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/page/src/service-worker.ts Bumped cache version to v2, added ASSET_URLS set for efficient lookup, implemented cleanup logic to remove stale WASM files during activation
apps/page/manifest.json Changed start_url and icon src from absolute to relative paths for GitHub Pages compatibility
apps/page/build.ts Added logic to select newest WASM file by mtime when multiple files exist, preventing stale artifact selection
apps/page/src/components/slTerminal.tsx Wrapped WASM loading in try-catch block with user-friendly error message display, applied prettier formatting
apps/page/src/components/terminal.tsx Applied prettier formatting to JSX attributes
apps/page/src/components/home/Installation.tsx Applied prettier formatting to JSX attributes
apps/page/src/components/home/Home.tsx Applied prettier formatting to JSX attributes
apps/page/src/components/home/Form.tsx Applied prettier formatting to JSX attributes

Comment on lines +53 to +61
const stats = await Promise.all(
wasmFiles.map(async (file) => {
const stat = await Bun.file(path.join(libDir, file)).stat();
return { file, mtime: stat?.mtime ?? 0 };
})
);
stats.sort(
(a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0)
);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mtime fallback value (0) could result in unexpected behavior. When stat.mtime is undefined or null, using 0 as a fallback means the file will be treated as created at Unix epoch (January 1, 1970). This could cause a stale file to be selected if its mtime fails to be read while newer files have valid timestamps.

Consider handling the error case explicitly by either:

  1. Filtering out files where stat is null/undefined before sorting
  2. Using a more defensive approach like throwing an error if stat is unavailable
  3. At minimum, logging a warning when mtime cannot be determined
Suggested change
const stats = await Promise.all(
wasmFiles.map(async (file) => {
const stat = await Bun.file(path.join(libDir, file)).stat();
return { file, mtime: stat?.mtime ?? 0 };
})
);
stats.sort(
(a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0)
);
const stats = (
await Promise.all(
wasmFiles.map(async (file) => {
const stat = await Bun.file(path.join(libDir, file)).stat();
if (!stat || !stat.mtime) {
console.warn(`Warning: could not determine mtime for WASM file "${file}", skipping it.`);
return null;
}
return { file, mtime: stat.mtime };
})
)
).filter(
(entry): entry is { file: string; mtime: Date } => entry !== null
);
if (stats.length === 0) {
console.error('No WASM file with a valid modification time found in lib directory');
process.exit(1);
}
stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +63
return { file, mtime: stat?.mtime ?? 0 };
})
);
stats.sort(
(a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0)
);
wasmFile = stats[0].file;
console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`);
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type check for mtime in the sort comparator is inconsistent with the fallback value. Line 56 uses stat?.mtime ?? 0 where 0 is a number, but line 60 checks instanceof Date and falls back to 0 again. If mtime is a Date object, the code works correctly. However, if stat is null/undefined and the fallback is 0 (a number), then the instanceof check fails and it falls back to 0 again, which works coincidentally.

This creates confusion about the expected type of mtime. Consider either:

  1. Using consistent type handling - if mtime should be a Date, handle the null case by creating a Date(0) instead of using raw 0
  2. Adding a type guard or assertion to clarify the expected type
  3. Simplifying the logic to handle numbers uniformly if that's the intended type
Suggested change
return { file, mtime: stat?.mtime ?? 0 };
})
);
stats.sort(
(a, b) => (b.mtime instanceof Date ? b.mtime.getTime() : 0) - (a.mtime instanceof Date ? a.mtime.getTime() : 0)
);
wasmFile = stats[0].file;
console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`);
const mtime = stat?.mtime instanceof Date ? stat.mtime : new Date(0);
return { file, mtime };
})
);
stats.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
wasmFile = stats[0].file;
console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`);
console.log(`Multiple WASM files found, selecting newest: ${wasmFile}`);

Copilot uses AI. Check for mistakes.
@scaryrawr scaryrawr merged commit 1cf7672 into main Jan 26, 2026
16 checks passed
@scaryrawr scaryrawr deleted the fix/offline-mode branch January 26, 2026 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants