notifications
The notification server: the newest 20 notifications and do-not-disturb.
button {
on_click = function()
local notifications = mantle.notifications:get()
if notifications then
mantle.notifications:set_dnd(not notifications.dnd)
end
end,
children = {
text {
content = mantle.notifications:map(function(notifications)
return (notifications and notifications.dnd) and "DND on" or "DND off"
end),
},
},
}
State
mantle.notifications:get() returns NotificationsState, nil before the first push. A field marked ? may be absent.
mantle.notifications’s payload.
| Field | Type | Description |
|---|---|---|
dnd | boolean | Do-not-disturb: mutes non-critical sounds only. Hiding popups is the config’s call. |
feed | Notification[] | The newest 20 of up to 100 queued notifications, newest first, expired ones included; a replacement keeps its place. An entry past 20 stays dismissable by id. |
Notification
One notifications.feed entry.
| Field | Type | Description |
|---|---|---|
actions | NotificationAction[] | Buttons in sender order, at most 8, excluding default and inline-reply. |
app_icon? | string | Application icon for icon { name = ... }: a theme name such as "firefox" or an absolute path, or nil. |
app_name | string | Sending application, truncated to 64 bytes. |
body | NotificationSpan[] | Parsed body markup; the raw body is truncated to 512 bytes first. |
desktop_entry? | string | Sender’s desktop id, e.g. "org.telegram.desktop", for mantle.applications.by_app_id; nil when absent or containing /. |
expired | boolean | The timeout ran out: drop it from popups, keep it in history until dismissed. Never true for critical or expire_timeout = 0; a replacement resets it. |
has_default_action | boolean | Clicking the card may :invoke_action(id, "default"). |
has_reply | boolean | The sender accepts mantle.notifications:reply(id, text). |
id | integer | Server id, from 1; a replacement keeps the id it replaces. |
image_path? | string | Attached picture (album art, avatar) as an existing absolute path, or nil. Never a theme name. |
reply_placeholder? | string | Placeholder for an empty reply field, e.g. "Reply to Alice", capped at 64 bytes; nil when unset. |
summary | string | Title as sent, truncated to 128 bytes. Not markup-parsed: the spec makes it plain text. |
timestamp | integer | Arrival time, Unix seconds; age is mantle.system.time - timestamp. A replacement restamps it. |
transient | boolean | Popup-only: removed on expiry instead of retired to history. |
urgency | Urgency | "normal" when the sender set none. "critical" never expires and plays sound through DND. |
NotificationAction
One action button.
| Field | Type | Description |
|---|---|---|
icon_name? | string | Theme icon name (the key) when the sender set action-icons, else nil. Never a path. |
key | string | Opaque key for :invoke_action(id, key). |
label | string | Button label, capped at 64 bytes. An empty label falls back to the key unless icon_name is set. |
NotificationSpan
One body-markup run: styled text or an image.
| Field | Type | Description |
|---|---|---|
bold? | boolean | Whether the run was inside <b>. |
href? | string | <a href> target, or nil when not a link. |
image_path? | string | Existing absolute path under an icon root; images elsewhere are dropped. |
italic? | boolean | Whether the run was inside <i>. |
kind | "text"|"image" | Which variant this is; each other field belongs to one variant. |
text? | string | Unescaped text; empty runs are omitted. |
underline? | boolean | Whether the run was inside <u>. |
Urgency
Notification urgency, also the set_sound tier.
One of "low", "normal", "critical".
Actions
Call each as mantle.notifications:<action>(arguments...); ? marks an argument you may omit.
| Action | Arguments | Description |
|---|---|---|
dismiss | id: integer | Removes a queued notification. |
invoke_action | id: integer, key: string | Invokes an actions[].key, or "default"; removes the notification unless it is resident. |
reply | id: integer, text: string | Sends reply text to a notification with has_reply; removes it unless it is resident. |
set_sound | urgency: Urgency, path: string | Sets an urgency tier’s sound: an existing file under /usr/share, /usr/local/share, /opt or $XDG_DATA_HOME, else ignored. Only Ogg Vorbis and 16-bit PCM WAV play. |
set_dnd | enabled: boolean | Gates non-critical notification sounds. |
set_quiet | enabled: boolean | Mutes non-critical sounds like set_dnd, without changing dnd. |
set_app_muted | app: string, muted: boolean | Silences every sound from an app, critical included, matched exactly on app_name or desktop_entry. |
hold_expiry | seconds: integer | Pauses every expiry countdown for seconds, capped at 300; 0 releases the hold. |
Backend
Mantle is the org.freedesktop.Notifications server on the session bus.
| Contract | Behavior |
|---|---|
| Name | Requested with DoNotQueue. If mako, dunst or another daemon owns it, the server stays off for the run and feed stays empty |
| Retention | 100-entry FIFO; feed shows the newest 20 |
| Expiry | A negative expire_timeout means 5 s. Critical and 0 never expire |
| Close reasons | NotificationClosed sends 1 expired, 2 dismiss or reply, 3 CloseNotification or invoke_action, 4 evicted from the FIFO. reply also emits NotificationReplied(id, text) |
| Body markup | Keeps <b>, <i>, <u>, <a href>, <img src>. Other tags lose their markup and keep their text; script and style lose both |
| Images | A path in image-path or <img> must be a regular file under /usr/share/icons, /usr/share/pixmaps, $XDG_DATA_HOME/icons or ~/.icons; an image-path without / and every action icon are theme names. Raw image-data (8-bit RGB or RGBA, at most 128 px a side) is spooled as notifications/notif-<id>.png |
| Sound | Nothing plays for a tier until set_sound registers a file for it, except a client’s own sound-file. Order: suppress-sound or set_app_muted silences, critical included; else the client’s sound-file; else sound-name from the freedesktop theme, only for a registered tier; else the tier’s file. Every file sits under /usr/share, /usr/local/share, /opt or $XDG_DATA_HOME and is Ogg Vorbis or 16-bit WAV, at most 4 MiB and 30 s |
How do I…
List notifications and dismiss one on click
list {
spacing = 6,
source = mantle.notifications:map(function(notifications)
return notifications and notifications.feed or {}
end),
key = function(item) return tostring(item.id) end,
itemfn = function(item)
return button {
width = 320,
padding = 8,
radius = 8,
background = "#1E1E2E",
on_click = function() mantle.notifications:dismiss(item.id) end,
children = {
column {
children = {
text { content = item.summary, font_size = 13, elide = "End", width = "Fill" },
text { content = item.app_name, font_size = 11, foreground = "#A6ADC8" },
},
},
},
}
end,
}
See also: Notification popups recipe.