@zooid/web 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-BSXXmV89.js +444 -0
- package/dist/assets/{index-BUKrmiTk.js → index-Bg0ADyZy.js} +1 -1
- package/dist/assets/index-Lm6-iv02.css +1 -0
- package/dist/assets/{reaction-picker-emoji-Ca_Lq5UC.js → reaction-picker-emoji-B3s_6rb_.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +113 -0
- package/package.json +2 -1
- package/dist/assets/index-B2FkL9sD.js +0 -439
- package/dist/assets/index-C_9y-fTg.css +0 -1
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>Zooid</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BSXXmV89.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Lm6-iv02.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/dist/sw.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Zooid service worker. Deliberately imports no application code — no bundler
|
|
2
|
+
// integration, no build-graph coupling. Push and notificationclick, plus the
|
|
3
|
+
// two lifecycle handlers that make an update to those actually take effect.
|
|
4
|
+
|
|
5
|
+
const ROOM_PATH = /\/room\/([^/?#]+)/;
|
|
6
|
+
|
|
7
|
+
function roomOf(url) {
|
|
8
|
+
const m = ROOM_PATH.exec(url);
|
|
9
|
+
return m ? decodeURIComponent(m[1]) : null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function titleFor(p) {
|
|
13
|
+
return p.room_name || p.room_id;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// The three agent events are, by definition, things you asked to be told
|
|
17
|
+
// about *while you are away* — a turn you're waiting on, an approval that
|
|
18
|
+
// blocks the agent, an error. A macOS/Chrome banner auto-dismisses after a
|
|
19
|
+
// few seconds, so an unattended one is a notification that never happened.
|
|
20
|
+
// These stay on screen until dismissed; ordinary chat messages keep the
|
|
21
|
+
// transient default, as chat everywhere else does.
|
|
22
|
+
const PERSISTENT_TYPES = [
|
|
23
|
+
"dev.zooid.approval_request",
|
|
24
|
+
"dev.zooid.turn.end",
|
|
25
|
+
"dev.zooid.error",
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function bodyFor(p) {
|
|
29
|
+
// Agent events carry no `body` prose of their own. Rendering
|
|
30
|
+
// `${sender}: ${undefined}` is the failure this branch exists to prevent.
|
|
31
|
+
switch (p.type) {
|
|
32
|
+
case "dev.zooid.approval_request":
|
|
33
|
+
return `${p.sender_display_name || "An agent"} needs approval`;
|
|
34
|
+
case "dev.zooid.turn.end":
|
|
35
|
+
// The prose itself never pushes — agent messages are `m.notice`, which
|
|
36
|
+
// `.m.rule.suppress_notices` silences so a chatty turn doesn't fire one
|
|
37
|
+
// push per chunk. turn.end carries a preview of the final message so the
|
|
38
|
+
// notification says what the agent actually said, not just that it stopped.
|
|
39
|
+
return p.preview
|
|
40
|
+
? `${p.sender_display_name || "An agent"}: ${p.preview}`
|
|
41
|
+
: `${p.sender_display_name || "An agent"} finished`;
|
|
42
|
+
case "dev.zooid.error":
|
|
43
|
+
return p.body || `${p.sender_display_name || "An agent"} hit an error`;
|
|
44
|
+
default:
|
|
45
|
+
return p.sender_display_name ? `${p.sender_display_name}: ${p.body || ""}` : p.body || "New message";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// This worker caches nothing and holds no versioned app state — it answers
|
|
50
|
+
// push and notificationclick, nothing else. So there is no half-updated app
|
|
51
|
+
// to guard against, and the default "wait until every tab of the origin is
|
|
52
|
+
// closed" only means a shipped fix to notification behaviour sits inert for
|
|
53
|
+
// anyone who keeps a tab open indefinitely. Take over immediately instead.
|
|
54
|
+
self.addEventListener("install", () => self.skipWaiting());
|
|
55
|
+
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
|
|
56
|
+
|
|
57
|
+
self.addEventListener("push", (event) => {
|
|
58
|
+
event.waitUntil(
|
|
59
|
+
(async () => {
|
|
60
|
+
let payload;
|
|
61
|
+
try {
|
|
62
|
+
payload = event.data.json();
|
|
63
|
+
} catch {
|
|
64
|
+
// userVisibleOnly is a contract: a push that renders nothing gets the
|
|
65
|
+
// origin penalised, so always show *something*.
|
|
66
|
+
await self.registration.showNotification("Zooid", { body: "New activity" });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
|
71
|
+
|
|
72
|
+
// Suppression (§5): a visible client already looking at this room.
|
|
73
|
+
const looking = windows.some(
|
|
74
|
+
(c) => c.visibilityState === "visible" && roomOf(c.url) === payload.room_id,
|
|
75
|
+
);
|
|
76
|
+
if (looking) return;
|
|
77
|
+
|
|
78
|
+
// Sound (§12). A worker cannot play audio, so a live client plays the
|
|
79
|
+
// real cue and the notification stays silent; with no client at all the
|
|
80
|
+
// OS default sound is the ceiling.
|
|
81
|
+
let cued = false;
|
|
82
|
+
if (payload.sound && windows.length > 0) {
|
|
83
|
+
for (const c of windows) c.postMessage({ type: "sound", roomId: payload.room_id });
|
|
84
|
+
cued = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await self.registration.showNotification(titleFor(payload), {
|
|
88
|
+
body: bodyFor(payload),
|
|
89
|
+
tag: payload.event_id,
|
|
90
|
+
data: { roomId: payload.room_id },
|
|
91
|
+
silent: cued || !payload.sound,
|
|
92
|
+
requireInteraction: PERSISTENT_TYPES.indexOf(payload.type) !== -1,
|
|
93
|
+
});
|
|
94
|
+
})(),
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
self.addEventListener("notificationclick", (event) => {
|
|
99
|
+
event.notification.close();
|
|
100
|
+
const roomId = event.notification.data && event.notification.data.roomId;
|
|
101
|
+
event.waitUntil(
|
|
102
|
+
(async () => {
|
|
103
|
+
const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
|
104
|
+
if (windows.length > 0) {
|
|
105
|
+
// No React Router closure survives out here — the page navigates itself.
|
|
106
|
+
await windows[0].focus();
|
|
107
|
+
windows[0].postMessage({ type: "navigate", roomId });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
await self.clients.openWindow(`/room/${roomId}`);
|
|
111
|
+
})(),
|
|
112
|
+
);
|
|
113
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zooid/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"@vitejs/plugin-react": "^4.3.0",
|
|
57
57
|
"@vitest/browser": "3.2.4",
|
|
58
58
|
"@vitest/coverage-v8": "3.2.4",
|
|
59
|
+
"fake-indexeddb": "^6.2.5",
|
|
59
60
|
"happy-dom": "^20.9.0",
|
|
60
61
|
"jsdom": "^25.0.0",
|
|
61
62
|
"msw": "^2.4.0",
|