import QtQuick import Quickshell.Services.Notifications import "." as M M.PopupPanel { id: menuWindow panelWidth: 350 // Header: title + clear all + DND toggle Item { width: menuWindow.panelWidth height: 32 Text { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "Notifications" color: M.Theme.base05 font.pixelSize: M.Theme.fontSize + 1 font.family: M.Theme.fontFamily font.bold: true } Row { anchors.right: parent.right anchors.rightMargin: 8 anchors.verticalCenter: parent.verticalCenter spacing: 8 // DND toggle Text { text: M.NotifService.dnd ? "\uDB82\uDE93" : "\uDB80\uDC9C" color: M.NotifService.dnd ? M.Theme.base09 : M.Theme.base04 font.pixelSize: M.Theme.fontSize font.family: M.Theme.iconFontFamily anchors.verticalCenter: parent.verticalCenter MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: M.NotifService.toggleDnd() } } // Clear all Text { text: "\uF1F8" color: clearArea.containsMouse ? M.Theme.base08 : M.Theme.base04 font.pixelSize: M.Theme.fontSize font.family: M.Theme.iconFontFamily anchors.verticalCenter: parent.verticalCenter visible: M.NotifService.count > 0 MouseArea { id: clearArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: M.NotifService.dismissAll() } } } } // Separator Rectangle { width: menuWindow.panelWidth - 16 height: 1 anchors.horizontalCenter: parent.horizontalCenter color: M.Theme.base03 } // Notification list Repeater { model: M.NotifService.active.slice(0, 20) delegate: Item { id: notifItem required property var modelData required property int index width: menuWindow.panelWidth height: notifContent.height + 12 Rectangle { anchors.fill: parent anchors.leftMargin: 4 anchors.rightMargin: 4 color: notifArea.containsMouse ? M.Theme.base02 : "transparent" radius: M.Theme.radius } // Urgency accent Rectangle { anchors.left: parent.left anchors.leftMargin: 4 anchors.top: parent.top anchors.topMargin: 4 anchors.bottom: parent.bottom anchors.bottomMargin: 4 width: 2 radius: 1 color: { const u = notifItem.modelData.urgency; return u === NotificationUrgency.Critical ? M.Theme.base08 : u === NotificationUrgency.Low ? M.Theme.base04 : M.Theme.base0D; } } Column { id: notifContent anchors.left: parent.left anchors.right: dismissBtn.left anchors.top: parent.top anchors.leftMargin: 14 anchors.topMargin: 6 spacing: 1 // App + time Row { width: parent.width Text { text: notifItem.modelData.appName || "Notification" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily elide: Text.ElideRight width: parent.width - ageText.width - 4 } Text { id: ageText text: { const diff = Math.floor((Date.now() - notifItem.modelData.time) / 60000); if (diff < 1) return "now"; if (diff < 60) return diff + "m"; if (diff < 1440) return Math.floor(diff / 60) + "h"; return Math.floor(diff / 1440) + "d"; } color: M.Theme.base03 font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } } Text { width: parent.width text: notifItem.modelData.summary || "" color: M.Theme.base05 font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily font.bold: true elide: Text.ElideRight } Text { width: parent.width text: notifItem.modelData.body || "" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize - 1 font.family: M.Theme.fontFamily wrapMode: Text.WordWrap maximumLineCount: 2 elide: Text.ElideRight visible: text !== "" } // Actions Row { spacing: 4 visible: notifItem.modelData.actions && notifItem.modelData.actions.length > 0 Repeater { model: notifItem.modelData.actions || [] delegate: Rectangle { required property var modelData width: actText.implicitWidth + 10 height: actText.implicitHeight + 4 radius: M.Theme.radius color: actArea.containsMouse ? M.Theme.base02 : "transparent" border.color: M.Theme.base03 border.width: 1 Text { id: actText anchors.centerIn: parent text: parent.modelData.text color: M.Theme.base0D font.pixelSize: M.Theme.fontSize - 2 font.family: M.Theme.fontFamily } MouseArea { id: actArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { parent.modelData.invoke(); M.NotifService.dismiss(notifItem.modelData.id); } } } } } } // Dismiss button Text { id: dismissBtn anchors.right: parent.right anchors.rightMargin: 10 anchors.top: parent.top anchors.topMargin: 8 text: "\uF00D" color: dismissArea.containsMouse ? M.Theme.base08 : M.Theme.base03 font.pixelSize: M.Theme.fontSize - 1 font.family: M.Theme.iconFontFamily MouseArea { id: dismissArea anchors.fill: parent anchors.margins: -4 hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: M.NotifService.dismiss(notifItem.modelData.id) } } MouseArea { id: notifArea anchors.fill: parent z: -1 hoverEnabled: true } } } // Empty state Text { visible: M.NotifService.count === 0 width: menuWindow.panelWidth height: 48 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "No notifications" color: M.Theme.base04 font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily } }