pragma Singleton pragma ComponentBehavior: Bound import qs.config import qs.custom import Quickshell import Quickshell.Io import Quickshell.Hyprland import Quickshell.Services.Notifications import QtQuick Singleton { id: root readonly property list list: [] property bool dnd: false function toggleDnd(): void { dnd = !dnd } NotificationServer { id: server keepOnReload: false actionsSupported: true bodyHyperlinksSupported: true bodyImagesSupported: true bodyMarkupSupported: true imageSupported: true persistenceSupported: true onNotification: notif => { notif.tracked = true; root.list.push(notifComp.createObject(root, { popup: root.dnd ? null : Hypr.focusedMonitor, notification: notif })); } } CustomShortcut { name: "clearNotifs" description: "Clear all notifications" onPressed: { for (const notif of root.list) notif.popup = null; } } CustomShortcut { name: "dismissNotifs" description: "Dismiss all notifications" onPressed: { for (const notif of root.list) notif.popup = null; } } IpcHandler { target: "notifs" function clear(): void { for (const notif of root.list) notif.popup = null; } } component Notif: QtObject { id: notif // Monitor to display popup in (typically the focused monitor when received) property HyprlandMonitor popup: null readonly property date time: new Date() readonly property string timeStr: Qt.formatTime(time, "hh:mm:ss") readonly property string timeSince: { const diff = Time.date.getTime() - time.getTime(); const m = Math.floor(diff / 60000); const h = Math.floor(m / 60); const d = Math.floor(h / 24); if (m < 1) return "now"; if (h < 1) return `${m}m`; if (d < 1) return `${h}h`; return `${d}d`; } required property Notification notification readonly property string summary: notification.summary readonly property string appIcon: notification.appIcon readonly property string appName: notification.appName readonly property string image: notification.image readonly property int urgency: notification.urgency readonly property list actions: notification.actions // Split body text into lines parseable by StyledText format readonly property string body: notification.body.replace("\n", "
") // One-line version (for non-expanded notifications) readonly property string bodyOneLine: notification.body.replace("\n", " ") readonly property Timer timer: Timer { running: true interval: notif.notification.expireTimeout > 0 ? notif.notification.expireTimeout : Config.notifs.defaultExpireTimeout onTriggered: { if (Config.notifs.expire) notif.popup = null; } } readonly property Connections conn: Connections { target: notif.notification.Retainable function onDropped(): void { root.list.splice(root.list.indexOf(notif), 1); } function onAboutToDestroy(): void { notif.destroy(); } } } Component { id: notifComp Notif {} } }