Skip to content

feat(ui): desktop notifications via Web Push with per-category settings#353

Merged
geodro merged 1 commit into
mainfrom
feat/desktop-notifications
May 15, 2026
Merged

feat(ui): desktop notifications via Web Push with per-category settings#353
geodro merged 1 commit into
mainfrom
feat/desktop-notifications

Conversation

@geodro
Copy link
Copy Markdown
Owner

@geodro geodro commented May 15, 2026

Summary

Adds an end-to-end desktop notification system to the lerd dashboard. Five kinds dispatch through a single in-process notifier inside lerd-ui, fanning every event out over both /api/ws (open tabs) and Web Push (closed PWA / minimised tabs): captured mail, queue and framework worker failures, finished service operations (with separate op_done and op_failed payloads), service update transitions, and ray()/dump() arrivals carrying the dumped content as a single-line preview.

Push is RFC 8291 encrypted via webpush-go, signed with a per-install VAPID key pair stored under ~/.local/share/lerd. Subscriptions persist with their per-category preferences so closed-PWA delivery honours the toggles too; the server prunes endpoints automatically on HTTP 410 Gone / 404 Not Found. The service worker push handler is kind-agnostic, reading title, body, tag, url, and data straight off the payload so it never grows kind-specific branches.

A new Settings panel under System → Desktop notifications exposes the master switch, per-kind toggles (mail / worker failures / op done / service updates / dumps, with dumps off by default since it's typically noisy), a test-push button that always passes the category filter, and the subscribed-devices list with Forget actions. Every visible string travels with both an English fallback and a Paraglide key + params so the page localises through m.notify_* while the SW (no DOM, no Paraglide) renders correctly with the English copy.

Worker-failure detection used to short-circuit when no tab was visible, which made closed-PWA push pointless for that kind. The visibility gate is now only on the eventbus publish; detection and notification dispatch always run.

Five notification kinds now ride a single in-process notifier inside lerd-ui that fans every event out across both /api/ws (open tabs) and Web Push (closed PWA / minimised tabs): captured mail from Mailpit, queue/horizon/reverb worker failures, finished service operations (install, migrate, reinstall, update, rollback) with success/failed variants, registry update-available transitions, and ray()/dump() arrivals with the dumped content as a single-line preview in the body.

Push is RFC 8291-encrypted, signed with a per-install VAPID key pair stored under ~/.local/share/lerd, fans out from the central dispatchNotification chokepoint, and prunes subscriptions automatically on 410/404. The service worker push handler is kind-agnostic, reading title, body, tag, url, and data straight off the payload so the SW never grows kind-specific branches.

Settings live under System → Desktop notifications: master switch plus per-category toggles, a test-push button, and the subscribed-devices list with Forget actions. Per-kind preferences are stored both client-side in localStorage and server-side on the Subscription so closed-PWA Web Push honours the toggles too. Every visible string is translatable via Paraglide keys under notify_*, with the English fallback always travelling in the payload so the SW (no DOM, no Paraglide) keeps rendering correctly.

Worker-failure detection no longer gates on whether a dashboard tab is visible; only the eventbus publish does. That way a closed-PWA developer still gets the push when a queue worker dies. Tests are TDD-first across both Go and Svelte with stub HTTPClient and real P-256 keys to exercise the encryption path without hitting the network.
@geodro geodro merged commit b791d8d into main May 15, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant