aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/sw.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-16 15:29:11 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-16 15:29:11 +0200
commit84e778d5cdd1bb5cded8a7c0238797c17d48666c (patch)
treed26f1d20735ba2db81da812371c92d257179882c /packages/meshbay-hub/src/meshbay_hub/static/sw.js
parent188a76f52d2f30609147b1beee7754f2cbd1e778 (diff)
downloadmeshbay-84e778d5cdd1bb5cded8a7c0238797c17d48666c.tar.gz
feat(hub): chat, presence, a Profile page, and downloads that do not freeze
Chat opens on the newest hundred messages, loads fifty older on demand with the reading position anchored — the distance from the *bottom*, since everything above the viewport just grew — and follows new messages only when the reader was already at the end. Day separators, sender grouping, an unread marker, and a jump-to-latest pill. Messages are keyed by id: index keys plus prepending makes Preact reuse the wrong bubbles. A presence dot per group in the sidebar, three states, each backed by something: the hub's registry, or a connection this browser made or failed to make. Never colour alone — red and green are the pair colour-blind readers cannot separate — so each dot carries a title and an aria-label. Profile is split out of Settings: identity, node link, pinned node identities and account deletion. Mixing them put an irreversible button two scrolls under a theme picker. The create-group page loses its centred 520 px card, which left 190 px of margin either side, and its two button panels become a radio group — a button conveys no chosen state to a screen reader, and side by side they read as two independent actions rather than one either/or. The Files toolbar shows its actions as icon buttons the moment Select is on, disabled when they do not apply rather than appearing and vanishing. On a phone the right-hand group could not wrap and ran 130 px off the screen. Streamed downloads no longer freeze after one chunk. `registration.active` says a worker exists, not that this page is controlled by it — and an uncontrolled page's requests never reach its fetch handler, so the worker took the stream and was never asked for it, leaving `writer.write()` waiting on backpressure that would never lift. The page now requires control and the worker confirms it actually served the request before the sink is trusted. Fixed on the way: `setActionsOpen` outlived the state it belonged to and threw on every Files action; the chat scrollbar stopped short of the bottom; the owner's row sat lower than the rest; About showed a version hardcoded two releases ago. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/sw.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/sw.js8
1 files changed, 8 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/sw.js b/packages/meshbay-hub/src/meshbay_hub/static/sw.js
index d0a8805..119687d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/sw.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/sw.js
@@ -29,6 +29,11 @@ self.addEventListener('message', (event) => {
readable: data.readable,
filename: data.filename || 'download',
size: Number(data.size) || 0,
+ // Answered when the navigation actually reaches us. The page waits for it:
+ // if this worker is never asked for the URL — an uncontrolled page, or a
+ // browser that blocks a download from a hidden iframe — nothing drains the
+ // stream and the page's first write blocks for good.
+ port: data.port || null,
});
// A tab that is closed before it navigates would leave a stream here for the
// life of the worker.
@@ -44,6 +49,9 @@ self.addEventListener('fetch', (event) => {
const entry = pending.get(url.pathname.slice(PREFIX.length));
if (!entry) return;
pending.delete(url.pathname.slice(PREFIX.length));
+ if (entry.port) {
+ try { entry.port.postMessage({ type: 'mbdl-serving', id: url.pathname }); } catch { /* gone */ }
+ }
const headers = {
'Content-Type': 'application/octet-stream',