web: native-gated hook to drive Eleven desktop notifications (#65)
chat.js posts a decrypted message preview to the Mac app via window.webkit.messageHandlers.lchatNotify when running inside the Eleven wrapper. Completely inert on the web and mobile (the bridge only exists in the native app), so no behaviour change for browser/PWA users. Pairs with the native handler in PR #47. Suppresses your own messages and the thread you're actively reading.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 file changed,
+18
−1
web/static/chat.js+18 −1
diff --git a/web/static/chat.js b/web/static/chat.js| index a18aa95..a79289b 100644 |
| --- a/web/static/chat.js |
| +++ b/web/static/chat.js |
| @@ -747,6 +747,20 @@ function previewText(t, data) { |
| return t.kind === "group" && data.name ? `${data.name}: ${body}` : body; |
| } |
| +// Native (Eleven) local notification: inside the Mac wrapper, hand a decrypted |
| +// preview to the app so it posts a system notification. No-op on the web (there the |
| +// service worker handles push). Suppresses your own messages and the thread you're |
| +// actively reading in a focused window. |
| +function maybeNativeNotify(tid, data, preview) { |
| + const bridge = window.webkit?.messageHandlers?.lchatNotify; |
| + if (!bridge) return; |
| + if (data.name === session.name) return; |
| + if (tid === activeId && document.hasFocus()) return; |
| + try { |
| + bridge.postMessage({ title: threadName[tid] || "New message", body: preview, thread: tid }); |
| + } catch (e) {} |
| +} |
| + |
| async function displayName(t) { |
| if (t.kind === "self") return "Notes"; |
| if (t.kind === "dm") { |
| @@ -2960,7 +2974,10 @@ function connectWS() { |
| if (t) { |
| t.last_at = msg.created_at || Math.floor(Date.now() / 1000); |
| try { |
| - threadPreview[tid] = previewText(t, await decrypt(threadKeys[tid], msg.blob)); |
| + const nd = await decrypt(threadKeys[tid], msg.blob); |
| + const preview = previewText(t, nd); |
| + threadPreview[tid] = preview; |
| + maybeNativeNotify(tid, nd, preview); |
| } catch {} |
| } |
| renderThreadList(); |