Omarchy: new-mail toasts on the bar poll (default off)
Third of the three-PR stack (theming #220 → setup+bar #225 → this). The design record is in docs/omarchy.md.
What
hey setup omarchy --notify rewrites the hey-unread bar module's exec to hey omarchy bar-status --notify. The same 3-minute tick that lights the indicator now also diffs the unseen Imbox postings against a fingerprint file (~/.local/state/hey-cli/omarchy-poll.json) and sends at most one toast per tick via omarchy-notification-send. One Imbox fetch serves both. --no-notify reverts; a plain re-run leaves it as it is; --remove also deletes the state file.
Enablement lives in the module's exec string — no config key, visible where it acts, gone with --remove.
Design
- New = an unseen posting not fingerprinted yet, or one whose
visible_entry_countgrew (a new reply on a known thread). Fingerprints avoidupdated_at(churns) andseen(flips on read). Muted threads are remembered but never toast. - First run seeds silently — never toast the backlog. Fingerprints carry the server+account identity, so
hey accounts usereseeds silently instead of toasting another account's backlog, and re-enabling after a--no-notifystretch drops stale fingerprints first. - One toast, replaced not stacked:
Sender — Subjectfor one thread (sender falls back name → email like other renderers),N new in Imbox+ first senders for more. The daemon's printed id (-r <id> -p) is cached so the next tick replaces the on-screen toast; a stale id after a shell restart just makes a fresh one. - Pruning waits for a complete page: HEY sorts Imbox postings unseen-first, so a page holding any seen posting proves the unseen set is complete; an all-unseen (possibly truncated) page keeps absent fingerprints so a thread pushed off a full page doesn't toast again when it returns.
- DND honored by construction: omarchy's default app-name
omarchy-actionbypasses silencing (NotificationLogic.js:34-38), so the toast passes--app-name HEYand SUPER+CTRL+comma mutes it into history like any other app. - Click focuses the TUI via the shared
omarchy-launch-or-focus-tuiexec hint. - Same silence discipline as the bar: any error → no output beyond the bar JSON, exit 0. A failed fetch leaves fingerprints untouched; a failed send keeps the undelivered postings out of them so the toast retries next tick.
Why the bar tick and not a push: inline command bar modules are interval-only — Bar.qml has no IpcHandler and omarchy bar has no refresh verb — so event-driven freshness needs a widget plugin, recorded as the follow-up in docs/omarchy.md.
Verified live
First --notify tick seeded 30 fingerprints and toasted nothing; flipping a thread unseen toasted Sender — Subject with the envelope glyph, app: HEY, urgency low, and the focus exec (checked in the daemon's history record); two back-to-back new threads shared one toast id (replaced, not stacked); with DND on, the toast landed in history instead of popping; --no-notify reverted; --remove cleared module + state file.