Omarchy: new-mail toasts on the bar poll (default off)

Merged
JE
2mo ago
+1268-62

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_count grew (a new reply on a known thread). Fingerprints avoid updated_at (churns) and seen (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 use reseeds silently instead of toasting another account's backlog, and re-enabling after a --no-notify stretch drops stale fingerprints first.
  • One toast, replaced not stacked: Sender — Subject for 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-action bypasses silencing (NotificationLogic.js:34-38), so the toast passes --app-name HEY and SUPER+CTRL+comma mutes it into history like any other app.
  • Click focuses the TUI via the shared omarchy-launch-or-focus-tui exec 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.