diff --git a/debian/changelog b/debian/changelog index f15df2b..6df2ff1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +devdesk (0.9.4) bookworm; urgency=medium + + * The inbox badge no longer only grows (#42). Opening the bell marks its + events seen, leaving just what is live (sessions waiting on you, + firing alerts). Viewing a session reads the events it reported while + you were away. + + -- Jared Vititoe Sun, 11 Oct 2026 22:46:00 -0400 + devdesk (0.9.3) bookworm; urgency=medium * Claude's footer shows 5-hour plan use instead of a dollar figure on a diff --git a/web/src/inbox.js b/web/src/inbox.js index fef0c78..ca83fb4 100644 --- a/web/src/inbox.js +++ b/web/src/inbox.js @@ -69,7 +69,27 @@ export class Inbox { this.button.setAttribute('aria-expanded', String(open)); if (!open) this.peeking = null; this.panel.classList.toggle('is-peek', false); - if (open) this.render(); + if (open) { + this.render(); + this.markSeen(); + } + } + + // Opening the bell counts as seeing what's in it (devdesk#42): the badge clears + // but the entries keep their highlight until the panel is redrawn. + markSeen() { + if (!this.events.some((e) => !e.read)) return; + this.events = this.events.map((e) => ({ ...e, read: true })); + save(this.events); + this.renderBadge(); + } + + // Looking at a session reads what it reported (finished, waiting) while you were away. + markSessionRead(session) { + if (!session || !this.events.some((e) => !e.read && e.session === session && !e.url)) return; + this.events = this.events.map((e) => (e.session === session && !e.url ? { ...e, read: true } : e)); + save(this.events); + this.render(); } // Something happened. The same kind for the same session within a minute is @@ -102,6 +122,11 @@ export class Inbox { } render() { + this.renderBadge(); + if (this.isOpen) this.renderPanel(); + } + + renderBadge() { const count = this.waiting.length + this.alerts.length + this.unread(); const urgent = this.waiting.length > 0 || this.alerts.some((a) => a.severity === 'critical'); this.badge.textContent = count ? String(count) : ''; @@ -114,7 +139,6 @@ export class Inbox { this.unread() && `${this.unread()} new event(s)`, ].filter(Boolean).join(' ยท ') : 'Inbox: nothing new'; - if (this.isOpen) this.renderPanel(); } // ------------------------------------------------------------ answer from here diff --git a/web/src/main.js b/web/src/main.js index 43fa1c7..5a093d9 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -181,6 +181,7 @@ function renderPanelsBadge() { } function onSessionData({ fetched = false } = {}) { + if (document.visibilityState === 'visible') inbox?.markSessionRead(state.session); renderUsage(); renderSessionChip(); renderAttention(fetched); @@ -1074,7 +1075,10 @@ function wireChrome() { window.addEventListener('online', retrySoon); document.addEventListener('visibilitychange', () => { - if (!document.hidden) retrySoon(); + if (!document.hidden) { + retrySoon(); + inbox.markSessionRead(state.session); + } }); // Session states feed the header chip and the notifications for other sessions, // so poll even in a background tab (browsers throttle that to about once a