Repository navigation
Dunst Notification has a href tag url for web notifications #2030
Description
Activity
Checked this against the shipped dunst config. This looks like a dunst limitation rather than a HyDE bug:
markup = fullis set indunstrc, and per dunst's own documentation that mode only supports<b>,<i>,<s>,<u>— it does not support<a href="">hyperlinks. The notification body in the screenshot contains an<a href="...">web.whatsapp.com</a>tag sent by the browser (looks like a web-push notification for WhatsApp Web), which Pango can't parse as valid markup underfull, so it falls back to showing the raw tag text.HyDE ships dunst's own default markup behavior here unmodified, so this isn't something introduced by the latest HyDE release — more likely you just started noticing it once a browser notification happened to include a link. A workaround would be an app-specific match rule (e.g.
markup = stripscoped to the browser's notifications) rather than a global fix, since stripping markup globally would also kill bold/italic for other apps.Checked this against the shipped dunst config. This looks like a dunst limitation rather than a HyDE bug:
markup = fullis set indunstrc, and per dunst's own documentation that mode only supports<b>,<i>,<s>,<u>— it does not support<a href="">hyperlinks. The notification body in the screenshot contains an<a href="...">web.whatsapp.com</a>tag sent by the browser (looks like a web-push notification for WhatsApp Web), which Pango can't parse as valid markup underfull, so it falls back to showing the raw tag text.HyDE ships dunst's own default markup behavior here unmodified, so this isn't something introduced by the latest HyDE release — more likely you just started noticing it once a browser notification happened to include a link. A workaround would be an app-specific match rule (e.g.
markup = stripscoped to the browser's notifications) rather than a global fix, since stripping markup globally would also kill bold/italic for other apps.Not really, it has been working well for the past a year or so I have used HyDE, before I did the latest update like 2 weeks ago I started seen it that way.
Fair pushback — the markup limitation itself is real (and, as far as I can trace in the shipped
dunstrc, hasn't changed), but that alone doesn't explain why your own experience changed a couple weeks ago after a year of it working fine. That timing points at something else shifting, not just "you finally hit a pre-existing limit."A few things that could explain it without being a dunst limitation at all:
- A
dunstpackage update on your system (upstream markup parsing behavior could differ between versions) - The sender itself changed — is this actually the browser (web push for WhatsApp Web), or could it be a different app/wrapper that started including a link in the body around the same time?
- Something in your own
dunstrc/wallbash overrides changedmarkupaway fromfullat some point and then back, or a different dunstrc is now being picked up than before
If you can share
dunst --versionand roughly what update you did 2 weeks ago (HyDE update, system update, or both), that would narrow it down a lot.- A
Fair pushback — the markup limitation itself is real (and, as far as I can trace in the shipped
dunstrc, hasn't changed), but that alone doesn't explain why your own experience changed a couple weeks ago after a year of it working fine. That timing points at something else shifting, not just "you finally hit a pre-existing limit."A few things that could explain it without being a dunst limitation at all:
- A
dunstpackage update on your system (upstream markup parsing behavior could differ between versions) - The sender itself changed — is this actually the browser (web push for WhatsApp Web), or could it be a different app/wrapper that started including a link in the body around the same time?
- Something in your own
dunstrc/wallbash overrides changedmarkupaway fromfullat some point and then back, or a different dunstrc is now being picked up than before
If you can share
dunst --versionand roughly what update you did 2 weeks ago (HyDE update, system update, or both), that would narrow it down a lot.
something with dust is broken, also notification such as power gets spammed i saw a similar issue which hints to breaking the parsing done dunst.
- A
Swaync got this too. But it depends on what application is notifying. I want to set expectations that not all apps follows proper standards on dbus so some provide weird outputs.
I think there is solution in swaync where you can use scripts to parse it , never tried it.For dunst, I will try to use it for a week and see what client apps are acting up.
Can you list the clients that output those href tags? Mine is viber and kdeconnect (seldom).
After a lot of debugging, here is what I found.
Both SwayNC and Dunst appear to be installed on my system.
I did a clean reinstall of HyDE on August 25 and only ran:
./install.sh
For some reason, this resulted in both notification daemons being installed. I'm not sure whether this is intentional behavior, but it looks like the installer also pulled in the
extraconfiguration containing SwayNC.From the HyDE configuration:
-
core.toml(line 22) includes../dots/dunst.toml- Installs
dunst - Sets up
~/.config/dunst
- Installs
-
extra.toml(line 24) includes../dots/swaync.toml- Installs
swaync - Sets up
~/.config/swaync
- Installs
However, HyDE's startup configuration appears to explicitly use Dunst, not SwayNC.
In
variables.lua(line 57):hc.start.notifications = "hyde-shell app -u " .. unt .. "-notifications.service -t " .. svc .. " -- dunst"
So initially, Dunst was running normally.
What happened
Dunst appears to have worked for about a day before crashing.
At 22:54:41 on August 26,
/usr/bin/dunstcrashed with a segmentation fault (SIGSEGV, exit status11):systemd[925]: hyde-Hyprland-notifications.service: Main process exited, code=dumped, status=11/SEGV systemd[925]: hyde-Hyprland-notifications.service: Failed with result 'core-dump'.After Dunst crashed, the D-Bus interface:
org.freedesktop.Notificationsbecame available.
SwayNC had apparently been trying to start in the background, and once Dunst released the notification interface, SwayNC acquired it:
systemd[925]: Started Swaync notification daemon.From that point onward, SwayNC became the active notification daemon, including for notifications from applications such as Chrome.
I'm still not sure why Dunst did not take over again after subsequent reboots, since I have rebooted the machine multiple times since then.
For now, I stopped SwayNC and restarted Dunst manually, and notifications are once again displaying the way HyDE appears to intend.
Original / Dunst
When SwayNC was running

-
One more data point, on top of justkelvin's dbus/journalctl findings above: I checked the actual shipped service definitions on my own system, and both notification daemons declare the same D-Bus name:
/usr/share/dbus-1/services/org.knopwob.dunst.service: Name=org.freedesktop.Notifications /usr/share/dbus-1/services/org.erikreider.swaync.service: Name=org.freedesktop.NotificationsThat's a genuine collision, not app-specific weirdness — whichever of the two currently owns the name is the one that ends up handling every notification, dunst or not. And
Scripts/install.sh:459-465deployscore.toml(dunst) andextra.toml(swaync) unconditionally on a plain./install.sh, no prompt either way, so both packages and both service files land on every install.On why it stays on swaync afterward rather than switching back on reboot: swaync's packaged unit has
Restart=on-failureandWantedBy=graphical-session.target, so once it's running it's set up to persist and restart itself. I haven't verified which of the two colliding service files dbus-daemon actually prefers when the name is unclaimed and both are candidates — that part's still open.Same root cause either way: two notification daemons shipped and installed together with no exclusivity between them, even though only one (dunst) is what HyDE's own startup config actually launches.
Here is the battery spam bug @kRHYME7 this will help.
Why Dunst Spams Notifications at 5% Battery
The spam is caused by a chain of three interacting bugs between
batterynotify.luaandluautils/global/notify.lua:-
The 1-Second Countdown Loop (
batterynotify.lua:214-228)
When the battery drops to 5% (conf.battery_critical_threshold),start_critical_countdown()starts a GLib timer that fires every 1 second for 120 seconds (conf.timer). On every single tick, it callsnotify_send('Battery Critically Low', ...)to show the remaining time. -
Function Signature Mismatch (
notify.lua:35-41)
notify.luaexpects a options table:function M.send(summary, body, opts)
However,
batterynotify.luacalls it with positional arguments:notify_send('Battery Critically Low', message, 'critical', 'xfce4-battery-critical')
Because argument 3 is the string
'critical'instead of a table:opts.replace_idevaluates tonil, which defaulted to0.opts.urgencyevaluates tonil, which defaulted to'normal'.- The icon argument was completely dropped.
-
The Unconditional
-r 0Flag (notify.lua:48-51)
notify.luabuilt the command:notify-send -a "HyDE Power" -t 5000 -r %d ...
Because
replace_idwas0, it executednotify-send ... -r 0. Under the Desktop Notifications Specification andlibnotify,replace_id = 0means "no replacement".
Instead of updating the existing popup in place, the notification daemon assigned a new ID and spawned a brand-new notification window every second, producing up to 120 stacked popups flooding your display. -
Countdown Notification Stuck on Plug-in (
batterynotify.lua:253-263)
When the charger is plugged in,cancel_critical_countdown()cancelled the timer but never closed or dismissed the critical notification window, leaving the frozen popup stuck on screen.
What Needs to Change
1. In
luautils/global/notify.lua- Support both table and positional arguments so calls like
notify.send(summary, body, 'critical', icon, timeout, replace_id)work seamlessly alongside table options{ urgency = '...', replace_id = ... }. - Only pass
-r <id>whenreplace_id > 0. Never pass-r 0. - Normalize urgency with
:lower()so'CRITICAL'and'NORMAL'don't fail. - Add a
close(replace_id)function using D-Bus (org.freedesktop.Notifications.CloseNotification) so persistent notifications can be dismissed programmatically.
2. In
batterynotify.lua- Define a dedicated notification ID for battery alerts (e.g.,
local BATTERY_NOTIFY_ID = 9991). - Pass
replace_id = BATTERY_NOTIFY_IDto the critical countdown notification:- This updates the single existing popup in place as a live countdown clock without spawning extra windows.
- Call
notify_mod.close(BATTERY_NOTIFY_ID)insidecancel_critical_countdown()so the critical popup disappears as soon as you plug the charger in. - Use replacement IDs for
Battery Lowas well so multiple low-battery warnings replace each other instead of piling up.
Proposed Diffs
In
luautils/global/notify.lua:--- a/Configs/.local/lib/hyde/luautils/global/notify.lua +++ b/Configs/.local/lib/hyde/luautils/global/notify.lua @@ -32,21 +32,45 @@ local function has_notify_send() return notify_send_available end --- Primary send function -function M.send(summary, body, opts) - opts = opts or {} - local urgency = opts.urgency or 'normal' - local icon = opts.icon or '' - local timeout = tonumber(opts.timeout) or 5000 - local replace_id = tonumber(opts.replace_id) or 0 +-- Close notification by replace_id via DBus +function M.close(replace_id) + local id = tonumber(replace_id) + if not id or id <= 0 then return end + os.execute(string.format( + 'gdbus call --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications --method org.freedesktop.Notifications.CloseNotification %d >/dev/null 2>&1 &', + id + )) +end + +-- Primary send function: supports both table options and positional arguments +function M.send(summary, body, opts, icon_arg, timeout_arg, replace_id_arg) + local urgency = 'normal' + local icon = '' + local timeout = 5000 + local replace_id = nil + local app_name = 'HyDE Power' + + if type(opts) == 'table' then + urgency = opts.urgency or urgency + icon = opts.icon or icon + timeout = tonumber(opts.timeout) or timeout + replace_id = tonumber(opts.replace_id) + app_name = opts.app_name or opts.app or app_name + elseif type(opts) == 'string' or opts == nil then + urgency = opts or urgency + icon = icon_arg or icon + timeout = tonumber(timeout_arg) or timeout + replace_id = tonumber(replace_id_arg) + end + urgency = tostring(urgency):lower() -- Call notify-send in background with & so we don't block. if has_notify_send() then local icon_flag = '' if icon ~= '' then icon_flag = ' -i ' .. sh_quote(icon) end - local cmd = string.format('notify-send -a "HyDE Power" -t %d -r %d -u %s%s %s %s >/dev/null 2>&1 &', - timeout, replace_id, tostring(urgency), icon_flag, sh_quote(summary or ''), sh_quote(body or '')) + local replace_flag = (replace_id and replace_id > 0) and string.format(' -r %d', replace_id) or '' + local cmd = string.format('notify-send -a %s -t %d%s -u %s%s %s %s >/dev/null 2>&1 &', + sh_quote(app_name), timeout, replace_flag, urgency, icon_flag, sh_quote(summary or ''), sh_quote(body or ''))
In
batterynotify.lua:--- a/Configs/.local/lib/hyde/batterynotify.lua +++ b/Configs/.local/lib/hyde/batterynotify.lua @@ -196,6 +196,8 @@ end +local BATTERY_CRITICAL_NOTIFY_ID = 9991 +local BATTERY_LOW_NOTIFY_ID = 9992 + -- state ------------------------------------------------------------------ local verbose = false local last_notified_percentage = 0 @@ -219,8 +221,8 @@ local function start_critical_countdown() local mm = math.floor(crit_remaining / 60); local ss = crit_remaining % 60 notify_send('Battery Critically Low', string.format('%d%% is critically low. Device will execute %s in %d:%02d.', pct, conf.execute_critical, - mm, ss), 'critical', 'xfce4-battery-critical') + mm, ss), 'critical', 'xfce4-battery-critical', 5000, BATTERY_CRITICAL_NOTIFY_ID) crit_remaining = crit_remaining - 1 @@ -253,6 +255,7 @@ end local function cancel_critical_countdown() + notify_mod.close(BATTERY_CRITICAL_NOTIFY_ID) if not crit_source then return end if GLib and type(crit_source) == 'number' then GLib.source_remove(crit_source); crit_source = nil; return
When Charger Plugged in (Notice the low battery notification is never cleared):

Fix with the above diff applied only one notification appears now:

When chrager plugged we just update that same notifcation without popping a new one:

-

This here, can anyone tell me why, started happening after installing the latest version