Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ auto-generated notes — but stable semver tags must pass.

- Thread view no longer flickers on every refresh. The post list is now diffed in place (keyed by post id), so unchanged posts keep their existing DOM nodes — SSE bursts, the 8s poll fallback, and cross-tab broadcasts can all fire together without repainting the column. When SSE is healthy the per-thread poll is suppressed entirely.
- Post column no longer shifts left/right when the pointer hovers the thread view. The Slack-style hover-reveal scrollbar now reserves its gutter at all times, so toggling the bar in/out doesn't reflow the column.
- A single hard refresh (Cmd+Shift+R) is now enough to pick up new app.js / style.css after a deploy. The PWA service worker was using stale-while-revalidate for shell assets, which always served the previous version on the first load and only swapped in the new one on the next refresh — it now uses network-first with cache fallback, so the cache is only consulted when the network actually fails (offline).
- _Add entries here as they ship. They get cut into the next `v<semver>` section at release time._

<!--
Expand Down
40 changes: 25 additions & 15 deletions web/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
*
* Strategy:
* - /api/* → network-only, never cached (live data, SSE, etc.)
* - same-origin GET → stale-while-revalidate for a small allow-list of
* static shell assets; everything else falls through
* to network without touching the cache.
* - shell allow-list → network-first with cache fallback. Fresh code
* wins on every load; cache is only used when the
* network actually fails (offline). This is what
* keeps a single hard refresh (Cmd+Shift+R) enough
* to pick up new app.js / style.css — the older
* stale-while-revalidate strategy meant the user
* always saw the previous version on the first
* load after a deploy.
* - everything else → falls through to the network without touching
* the cache.
*
* Bump CACHE_VERSION whenever you change cached files or this file itself
* so old clients pull fresh copies on activate.
*/
const CACHE_VERSION = 'openforge-shell-v1';
const CACHE_VERSION = 'openforge-shell-v2';

// Allow-list of path prefixes we are willing to cache. Anything outside this
// list (HTML pages, /api/*, ad-hoc endpoints) is left to the network.
Expand Down Expand Up @@ -65,16 +72,19 @@ self.addEventListener('fetch', (event) => {

event.respondWith((async () => {
const cache = await caches.open(CACHE_VERSION);
const cached = await cache.match(req);
const network = fetch(req)
.then((res) => {
// Only cache successful, basic (same-origin) responses.
if (res && res.ok && res.type === 'basic') {
cache.put(req, res.clone()).catch(() => {});
}
return res;
})
.catch(() => cached); // offline: fall back to cache if we have it
return cached || network;
try {
const res = await fetch(req);
// Only cache successful, basic (same-origin) responses.
if (res && res.ok && res.type === 'basic') {
cache.put(req, res.clone()).catch(() => {});
}
return res;
} catch (_e) {
// Network failed (offline / DNS / etc.) → serve from cache if we
// have it; otherwise let the browser surface the network error.
const cached = await cache.match(req);
if (cached) return cached;
throw _e;
}
})());
});
Loading