omarchy-notification-center
A notification center for the Omarchy shell: a bell in the bar that opens a panel of notification history, with per-entry delete and a clear-all button. History stays until you remove it by hand — clicking an entry jumps to the sending app (relaunching it if it's closed) and clears it.

Built and verified against omarchy 4.0.0.r1849.g83881e9-1 (August 2026).
What it is, honestly
This is not a standalone widget. It is a clone of Omarchy's built-in
omarchy.notifications plugin (made with omarchy plugin clone, the supported
customization path) with the notification center added on top. The clone
replaces the built-in notification daemon at runtime.
That shape was forced by the requirements. The stock daemon owns the history
store and trims it to 10 entries on every write. A separate widget could only
ever read those 10 files and could not stop the trimming, so "preserve history
until I clear it" required changing the daemon itself. Cloning is how Omarchy
says to do that: the packaged copy in /usr/share/omarchy/ is never edited,
and deleting the clone restores stock behavior.
What this touches on the system
Nothing in /usr/share/omarchy/ (core, package-owned) is modified. The full
footprint:
~/.config/omarchy/plugins/apurva.notifications— a symlink to this repo. The shell discovers the plugin here.~/.config/omarchy/shell.json— three additions, all plain config:{"id": "apurva.notifications"}inbar.layout.right(the bell),{"id": "apurva.notifications"}inplugins[]and"omarchy.notifications"indisabledPlugins[](written byomarchy plugin clone; this is what swaps the daemon),cloneSourceRestoresbookkeeping, also written by the clone tool.
~/.local/state/omarchy/notifications/— runtime data the stock daemon already used (history files, image copies). Not config, survives either way.
Using it
- Left-click the bell: open or close the panel.
- Hover a card, click the ✕: delete that one entry (file, image copies, row).
- Clear all: empty the whole history.
- Click a card: jump to the sending app's window, relaunching the app if it was closed. The entry leaves history only when that worked; when the app can't be found at all, the entry stays put.
- Clicking a toast behaves the same way (default action, else focus, else relaunch) and archives the toast to history as before.
- Right-click the bell: toggle Do Not Disturb. The bell shows a slashed glyph while DND is on. The stock silence button in the middle of the bar keeps working too (see "Changes vs stock").
- The panel refreshes itself while open when new notifications get archived.
How much history is kept is configurable on the widget's shell.json bar
entry (default: the stock 10):
{ "id": "apurva.notifications", "historyLimit": 50 }
The shell picks up shell.json edits live. Lowering the limit trims the
on-disk history as soon as the new value lands; raising it applies to new
notifications from then on.
Toasts, DND rules, and the stock keybindings (Super+comma family) behave exactly as before. Super+Shift+Alt+comma still replays the last 10 entries as toasts; the panel is where the full history lives.
CLI:
omarchy-shell notification-center toggle # open/close the panel
omarchy-shell notifications clear # clear all history
omarchy-shell notifications removeHistory <stem> # delete one entry;
# stem = file name without .json,
# e.g. 1787848508512-2
Optional keybinding, in ~/.config/hypr/bindings.lua:
o.bind("SUPER + N", "Notification center", "omarchy-shell notification-center toggle")
Changes vs stock
The stock plugin already persisted every notification to
~/.local/state/omarchy/notifications/history/, one JSON file each. This
clone keeps all of that machinery and changes:
Service.qmlhistoryLimitis writable now (still defaulting to the stock 10); the bar widget pushes the user'shistoryLimitsetting over it, and a lowered limit trims the history directory right away instead of waiting for the next archive.- New
replayLimit: 10so the toast replay keeps its old size even when the user raises the history cap. - A small DND bridge object is registered under the stock
omarchy.notificationsservice id. The bar's silence indicator resolves its service withfirstPartyServiceFor("omarchy.notifications"), which — unlikesummon/toggle/call— does not route throughPluginRegistry.resolveEnabledId(), so enabling the clone left that button dead. The bridge only exposesdoNotDisturb/setDoNotDisturband re-registers itself whenever a plugin rescan sweeps it away. (The real fix belongs upstream:serviceForshould resolve clones like the other entry points do.) focusAppfalls back to launching the sender's desktop entry (resolved viaDesktopEntries.heuristicLookupfrom the app name, then the icon name) when no window matches, using the sameuwsm-app -- gtk-launchpath as the app launcher. It also reports back whether focus-or-launch worked, which is what lets the panel keep unactionable entries.- New
historyModel(a ListModel on the service, shared by every monitor's panel), filled by a directory read that goes through the same serialized file-job queue as all other history mutations, so reads are always ordered after the writes that made them necessary. - New
historyMutated()signal, emitted when a toast archives or a DND-silenced notification is written. Open panels listen and re-read. - New
deleteHistoryEntry(timestamp, originalId): removes the model row and deletes the entry's JSON file and image copies. - New IPC method
removeHistory(stem)on the existingnotificationstarget, calling the same function.
NotificationCenter.qml(new file, thebarWidgetentry point): bell button plus the panel. Reuses the stockNotificationCardfor rendering, adds a quiet received-at label per card (time today, day+time this week, date older).manifest.json: adds thebar-widgetkind and entry point.
docs/upstream-diff.patch is the full diff against
/usr/share/omarchy/shell/plugins/notifications/ at build time.
Install
omarchy plugin add https://github.com/apurvasaraiya/omarchy-notification-center.git --enable
Enabling asks where in the bar the bell should go, and — because the manifest
declares clonedFrom: omarchy.notifications — the registry swaps the stock
notification daemon out automatically (disabledPlugins plus the restore
bookkeeping in shell.json). Disabling or removing the plugin restores the
stock daemon the same way.
For development, install as a symlink instead so edits land live:
git clone https://github.com/apurvasaraiya/omarchy-notification-center.git ~/dev/omarchy-notification-center
ln -s ~/dev/omarchy-notification-center ~/.config/omarchy/plugins/apurva.notifications
omarchy plugin enable apurva.notifications
omarchy restart shell
Uninstall
rm ~/.config/omarchy/plugins/apurva.notifications
omarchy restart shell
Then remove the three shell.json additions listed above (or run
omarchy plugin disable apurva.notifications, which restores the clone
source). The stock daemon takes over again, including its 10-entry trim, so
history beyond 10 entries disappears at the next archive.
Development notes
Both of these cost a debugging round; don't repeat them:
- The shell hot-reloads plugin files on save, but a running
keepLoadedservice instance is not re-created. Edits toService.qml(including new IPC methods) silently don't apply untilomarchy restart shell. The restart is cheap: live toasts and history survive via the popup persistence files. - Adding a brand-new file to a plugin directory makes the widget fail to load
with Qt's "File name case mismatch" error. The file name is fine; the QML
type loader cached the directory listing from before the file existed.
omarchy restart shellfixes it.
When Omarchy updates change the stock notifications plugin, this clone keeps
running the old code. To rebase: diff the new stock plugin against
docs/upstream-diff.patch's base, re-apply the change list above, and
regenerate the patch:
diff -ru --exclude=.git --exclude=docs \
/usr/share/omarchy/shell/plugins/notifications . > docs/upstream-diff.patch
License
MIT — see LICENSE. Substantial portions are derived from
Omarchy (MIT, © David Heinemeier
Hansson): the plugin is a clone of the built-in omarchy.notifications
plugin with the notification center added on top.