dashboard: browser notifications for operator-bound events

three signals fire OS notifications:
- new approval lands in the queue (per id, via /api/state delta)
- new ask_operator question queued (per id)
- broker message sent to operator (live via SSE)

first /api/state render after page load seeds the 'seen' sets
without firing — only items that arrive while the page is open
count. controls in a row under the banner: 🔔 enable
notifications (calls requestPermission, hides on grant), 🔕 mute /
🔔 unmute toggle (localStorage-backed so operator can silence
without revoking the permission), inline status text when blocked
or unsupported.

notification tag='hyperhive' collapses rapid bursts; onclick
focuses the dashboard tab. requires secure context (HTTPS or
localhost) — on other origins the API is unavailable and the
controls hide themselves.

todo: entry dropped.
This commit is contained in:
müde 2026-05-15 21:10:20 +02:00
parent a67aada7c9
commit 237b215c55
4 changed files with 148 additions and 25 deletions

23
TODO.md
View file

@ -44,29 +44,6 @@ Pick anything from here when relevant. Cross-cutting design notes live in
## UI / UX
- **Browser notifications for operator-bound events.** Dashboard
pings the OS notification center when (a) a new approval lands
in the queue, (b) a new `ask_operator` question is queued, (c) a
broker message is sent `to: "operator"`. All three data sources
are already in `/api/state` + `/messages/stream` so this is
pure frontend. Sketch:
1. Small "🔔 enable notifications" button somewhere (header
or near the inbox section). Clicks call
`Notification.requestPermission()`. Hide once granted.
2. Track last-seen counts in the JS app
(`approvals.length`, `questions.length`). On
`refreshState`, if the count went up, fire
`new Notification(...)` per new item.
3. SSE handler for `messages/stream` fires a notification on
`kind === 'sent' && to === 'operator'` (already triggers
`refreshState`; just adds a notify call alongside).
4. Notification body links back to the dashboard (`onclick →
window.focus()` + section anchor).
Caveats: Notification API requires a secure context (HTTPS or
localhost). Most operators access via LAN / Tailscale — works
fine for localhost forwards, otherwise needs a TLS cert in the
module. Persist a per-browser "muted" toggle in localStorage so
the operator can silence without revoking permission.
- **Terminal: `/model` slash command.** Operator-typeable model
override from the terminal. Depends on the model-override work