A notification daemon for Omarchy. It replaces the built-in notification service with a stacking deck that groups by source, reads what a notification is actually offering you, and lets you act on it without leaving the card.
Stacks and groups. Notifications from the same source collect into one deck. Hovering expands it. Nothing is hidden behind a "3 more" summary — every notification is a real card you can act on.
A body is held to two lines while you are scanning a deck, and opens to its full length — up to eight lines — once the deck is expanded, or on hover when it is the only card on screen. Phone messages are why: a sentence and a half arriving as a sentence and an ellipsis is the commonest way to lose the point of a message.
Reads the contents. A verification code, a link or a phone number in the
body becomes a labelled button: Copy code, Open link, Copy number. A mark
on the headline tells you a card is
carrying something before you hover, because the useful part of a message is
usually past the ellipsis.
A message carrying two codes gets a button each, labelled with the digits,
because "Copy code" twice is a coin toss. Detection is deliberately
conservative: 412 passed, 0 failed is not a code, and neither is
Claude Code build 1841.
Shows the sender's own actions. The freedesktop spec has carried actions all along; most desktops draw none of them. Reply, Mark as read, whatever the app offered, appear as buttons on hover.
Replies to your phone. Phone notifications reach the desktop through KDE Connect, and the ones carrying a reply channel grow a text field on the card. The answer goes back to the conversation, and the notification is dismissed on the phone too. Nothing new lands and nothing expires while you are typing, so the field cannot move out from under you mid-sentence.
Sends you back where it came from. Clicking a card focuses the window that source is already showing in — a Slack notification lands in the Chrome you already have Slack open in, rather than a new tab — and opens something new only when there is nothing to go back to. It can only see the tab a browser is actually showing, so a site sitting in a background tab opens fresh.
Quietens one source at a time. Right-click a card and pick how long. It is
the source that goes quiet, not the app that relayed it: snoozing a Slack
notification that arrived through Chrome silences app.slack.com and nothing
else, and snoozing a noisy shopping app on your phone leaves WhatsApp alone.
Snoozed notifications are still recorded — they go to history without ever
being on screen.
The panel does the same for everything at once. Both offer the lengths in
snoozeDurations, which are yours to change.
Lets the code through anyway. A snooze or a silence holds everything back except a notification carrying a verification code. You asked for that code thirty seconds ago and it expires in five minutes — and being locked out of a login because you had quietened Slack is exactly the failure that makes people stop snoozing anything. The key beside the panel's snooze button closes the hole for the times you want nothing at all. Copying a code dismisses its notification; there is nothing left in it afterwards.
Shows you what quiet cost. Silence is only tolerable if you can see what it kept from you. While anything is being held back, the panel lists the sources that caught something and opens each one to show what it caught, newest first — capped at both ends, so a fortnight of silence doesn't turn a panel into a log file.
Resolves real icons. Your own icon themes win; web notifications fall back to the site's own icon, in dark and light variants to suit the theme.
git clone https://github.com/njpatel/omapager.git \
~/.config/omarchy/plugins/njpatel.omapagerThen in ~/.config/omarchy/shell.json, turn off the built-in service, add the
plugin, and put the indicator in the bar beside the other status glyphs, since
it behaves like one:
{
"disabledPlugins": ["omarchy.notifications"],
"plugins": [{ "id": "njpatel.omapager" }],
"bar": { "layout": { "center": ["omarchy.indicators", "njpatel.omapager", "omarchy.clock"] } }
}omarchy-restart-shell to pick it up. (Not omarchy-refresh-shell — that
resets shell.json to defaults.)
omarchy plugin remove njpatel.omapagerThen undo the three lines above: take omarchy.notifications back out of
disabledPlugins so the built-in service can claim the bus again, and drop
njpatel.omapager from the bar layout. omarchy-restart-shell to apply.
State is left behind on purpose, in case you are only reinstalling —
rm -rf ~/.local/state/omarchy/omapager clears the history and the resolved
icons for good.
| key | default | what it does |
|---|---|---|
stacking |
source |
source gives each sender its own deck; all puts everything in one |
actionsAlign |
right |
which end of a card its buttons sit at |
hideSettingsAction |
true |
drop the browser's "Settings" button, which is on every web notification and is never the one you wanted |
snoozeDurations |
30, 60, 240, tomorrow |
what the snooze menus offer — minutes, or tomorrow |
wakeHour |
8 |
the hour "until tomorrow" wakes a source at |
smartRaise |
true |
match a site against browser window titles, so a click lands in the window already showing it |
alwaysShow |
false |
keep the bar slot even when nothing is held back, so the centre of the bar never shifts |
codesBypassQuiet |
true |
let a notification carrying a verification code through a snooze or a silence |
timeFormat |
system |
system follows LC_TIME; 24h and 12h pin it |
sourceLimit |
8 |
how many quietened sources the panel lists |
heldPerSource |
10 |
how many held notifications it shows per source |
Every history entry is the text of a message somebody sent you, so it is trimmed by age as well as count: 7 days or 200 entries, whichever comes first. Resolved icons are dropped after 60 days unused, and nothing is ever written to the system log.
Settings live on the bar widget's entry in shell.json, all in one place
next to id:
{ "id": "njpatel.omapager", "snoozeDurations": ["60", "480"], "wakeHour": 9 }omapager takes a slot in the bar only while it is keeping something from you, and nothing at all the rest of the time. Hovering the centre of the bar reveals it, the way Omarchy reveals its own inactive indicators — that is the way back into silence when nothing is showing.
| crossed-out bell, in the theme's urgent colour | silenced |
bell with a z in it, in amber |
snoozed — everything, or a source |
| crossed-out bell, dimmed | nothing held back |
Same crossed-out bell as Omarchy's own indicator, so a silenced desktop looks the same whichever service is running. Amber rather than red for a snooze, because a snooze ends by itself.
Left-click silences and unsilences. Right-click opens the panel: the switch, a button beside it that snoozes everything for a while, and the list of what is being kept from you — when each source comes back, how much it has caught, and the messages themselves when you open one.
Omarchy's five stock bindings on the comma key keep working unchanged. omapager answers the same IPC target the built-in service did, so there is nothing to rebind and nothing to configure:
SUPER , |
dismiss the newest notification |
SUPER SHIFT , |
dismiss all of them |
SUPER CTRL , |
toggle silencing |
SUPER ALT , |
invoke the newest one, as clicking it would |
SUPER SHIFT ALT , |
put the last few back on screen |
Three things the stock bindings have no key for. All three are free on a stock
install — check yours with omarchy menu keybindings --print, and hl.unbind
first if you have moved things around.
In ~/.config/hypr/bindings.lua:
-- Copy a code without touching the mouse. The notification takes itself away.
o.bind("SUPER + ALT + C", "Copy code from newest notification",
"omarchy-shell omapager offer code")
-- Next to SUPER + CTRL + comma, which silences. Quiet for an hour, rather
-- than quiet until you remember you turned it off.
o.bind("SUPER + CTRL + ALT + comma", "Snooze all notifications for an hour",
"omarchy-shell omapager snoozeAll 60")
-- What is snoozed, what it has held, and the way back. Worth a key: the bar
-- icon is not there at all when nothing is being held back.
o.bind("SUPER + CTRL + SHIFT + comma", "Notification options",
"omarchy-shell omapager.panel toggle")omarchy-shell omapager count how many are on screen
omarchy-shell omapager clear dismiss them
omarchy-shell omapager dnd toggle Do Not Disturb
omarchy-shell omapager expand open the deck, as hovering would
omarchy-shell omapager offer code take the front card's offer (code|link|phone)
omarchy-shell omapager act reply invoke one of the sender's actions
omarchy-shell omapager reply "text" answer the front card ("" opens the field)
omarchy-shell omapager snooze 60 quieten the front card's source, in minutes
omarchy-shell omapager snoozeAll 60 quieten everything for 60 minutes, or wake it
omarchy-shell omapager codes off stop letting verification codes through
omarchy-shell omapager unsnooze "" wake everything ("" for all, or a source key)
omarchy-shell omapager snoozes what is snoozed, and until when
omarchy-shell omapager stack source switch stacking mode
omarchy-shell omapager align right switch which end the buttons sit at
omarchy-shell omapager probe what the daemon believes, as JSON
omarchy-shell omapager.panel toggle the panel
omarchy-shell omapager.panel expand x open a source's held list, as clicking it would
bin/omapager-demo # the everyday scenes
bin/omapager-demo --scene interactive # codes, links, and the sender's buttons
bin/omapager-demo --scene routing # where a click sends you, per source
bin/omapager-demo --scene reply # inline reply, against a stand-in phone
bin/omapager-demo --replay 40 # your own notifications, re-sent
bin/omapager-demo --list--scene routing reads your open windows and prints what each card should
do before it sends anything, so you can check it against what happens.
--scene reply writes a fixture the daemon treats as a repliable notification
and logs the reply to a file rather than sending it to a person.
Omarchy (Quickshell 0.3.x, Hyprland), and Python 3 for the helpers in bin/.
Two more things are worth having, and they are not the same kind of thing.
wl-clipboard — recommended. Not an Omarchy dependency, so check before you
assume it: command -v wl-copy. The copy buttons work either way, but with it a
copied verification code is marked sensitive and Omarchy's clipboard history
skips it. Either way the code is cleared again 90 seconds later, unless you have
copied something else since — that stays.
KDE Connect — the whole phone half. Without it there are no phone
notifications at all: it is the bridge that puts them on the bus in the first
place, so the reply field, Mark as read and the grouping by the app a message
really came from all go with it.
sudo pacman -S wl-clipboard kdeconnect # kdeconnect also needs the phone appApache-2.0.




