aboutsummaryrefslogtreecommitdiffstats
path: root/shared/NotifyConfig.js
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-19 09:18:49 +0200
committerDanilo M. <danix@danix.xyz>2026-10-19 09:18:49 +0200
commita6e3e42d0687dbc4a5b939faa3c2ca355e690e2e (patch)
tree50765e6b0b8ca5a70cde48d219702066596941cd /shared/NotifyConfig.js
parent61ac7f2e62375c0644ffea30c8d93b81c2f29b96 (diff)
downloadquickshell-a6e3e42d0687dbc4a5b939faa3c2ca355e690e2e.tar.gz
quickshell-a6e3e42d0687dbc4a5b939faa3c2ca355e690e2e.zip
notifications: resolve notifyd's config.json in a shared library
One resolver for both shells and the settings page, with the defaults that reproduce today's behaviour, and the mute/DND decision beside it so the node check covers it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'shared/NotifyConfig.js')
-rw-r--r--shared/NotifyConfig.js63
1 files changed, 63 insertions, 0 deletions
diff --git a/shared/NotifyConfig.js b/shared/NotifyConfig.js
new file mode 100644
index 0000000..ff522c4
--- /dev/null
+++ b/shared/NotifyConfig.js
@@ -0,0 +1,63 @@
+// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+.pragma library
+
+// ~/.config/notifyd/config.json, resolved over its defaults. notifyd reads
+// the same file for timeouts and caps (its config.go clamps the same way);
+// the balloon keys are this side's alone. Tested by test_notify_config.js.
+
+const corners = ["top-left", "top-right", "bottom-left", "bottom-right"];
+
+function defaults() {
+ return {
+ timeouts: { low: 10, normal: 10, critical: 0 },
+ liveCap: 20,
+ historyCap: 20,
+ balloons: { screen: "DP-1", corner: "bottom-right" },
+ dndPassCritical: true,
+ muted: [],
+ };
+}
+
+// Out of range (negative, or above a day) is the default, as in the daemon.
+function seconds(v, d) { return Number.isInteger(v) && v >= 0 && v <= 86400 ? v : d; }
+function cap(v, d) { return Number.isInteger(v) ? Math.min(Math.max(v, 1), 100) : d; }
+
+// Never throws: anything that is not the right shape reads as its default.
+function resolve(raw) {
+ const d = defaults();
+ if (!raw || typeof raw !== "object") return d;
+ const t = raw.timeouts || {};
+ const b = raw.balloons || {};
+ return {
+ timeouts: {
+ low: seconds(t.low, d.timeouts.low),
+ normal: seconds(t.normal, d.timeouts.normal),
+ critical: seconds(t.critical, d.timeouts.critical),
+ },
+ liveCap: cap(raw.liveCap, d.liveCap),
+ historyCap: cap(raw.historyCap, d.historyCap),
+ balloons: {
+ screen: typeof b.screen === "string" && b.screen !== "" ? b.screen : d.balloons.screen,
+ corner: corners.indexOf(b.corner) >= 0 ? b.corner : d.balloons.corner,
+ },
+ dndPassCritical: typeof raw.dndPassCritical === "boolean" ? raw.dndPassCritical : d.dndPassCritical,
+ muted: Array.isArray(raw.muted) ? raw.muted.filter(a => typeof a === "string") : d.muted,
+ };
+}
+
+// Whether a balloon is held back by mute or DND. Drawer and snooze are the
+// balloon's own checks; this is only what the config decides.
+function withheld(cfg, n, dnd) {
+ if (cfg.muted.indexOf(n.app) >= 0) return true;
+ return dnd && !(n.urgency === "critical" && cfg.dndPassCritical);
+}