import QtQuick import Quickshell import Quickshell.Services.Notifications import "root:/config" import "root:/services" // One notification, used both in the history list and as a floating toast. Item { id: root required property var notif property bool showActions: true readonly property color tone: root.notif ? Notifs.urgencyColor(root.notif.urgency) : Theme.primary signal dismissed() implicitHeight: panel.height P5Panel { id: panel width: parent.width height: layout.implicitHeight + Theme.padM * 2 lean: 0.05 chop: 12 fill: Theme.surface fillOpacity: 0.97 border: Theme.alpha(root.tone, hover.hovered ? 0.95 : 0.5) slash: true slashColor: root.tone slashWidth: 4 halftone: root.notif?.urgency === NotificationUrgency.Critical halftoneColor: Theme.accent halftoneOpacity: 0.12 padding: Theme.padM Column { id: layout anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: 3 // App line Row { spacing: Theme.padS Icon { anchors.verticalCenter: parent.verticalCenter text: Notifs.appIconFor(root.notif) color: root.tone font.pixelSize: Theme.fsBody } Text { anchors.verticalCenter: parent.verticalCenter text: (root.notif?.appName || "System").toUpperCase() color: root.tone font.family: Theme.fontMono font.pixelSize: 8 font.letterSpacing: 2 renderType: Text.NativeRendering } } Text { width: parent.width text: root.notif?.summary || "" color: Theme.text elide: Text.ElideRight maximumLineCount: 2 wrapMode: Text.WordWrap font.family: Theme.fontDisplay font.pixelSize: Theme.fsBody font.weight: Font.Black font.italic: true renderType: Text.NativeRendering } Text { width: parent.width visible: text !== "" text: root.notif?.body || "" color: Theme.subtext elide: Text.ElideRight maximumLineCount: 4 wrapMode: Text.WordWrap textFormat: Text.StyledText font.family: Theme.fontDisplay font.pixelSize: Theme.fsSmall renderType: Text.NativeRendering } // Inline image, when the sender supplied one. Image { visible: source != "" && status === Image.Ready source: root.notif?.image || "" sourceSize.height: 90 fillMode: Image.PreserveAspectFit asynchronous: true } Row { visible: root.showActions && (root.notif?.actions?.length ?? 0) > 0 spacing: Theme.padS topPadding: Theme.padS Repeater { model: root.notif?.actions ?? [] Item { id: actionChip required property var modelData implicitWidth: actionLabel.implicitWidth + Theme.padM implicitHeight: 22 Rectangle { anchors.fill: parent color: actionHover.hovered ? Theme.alpha(root.tone, 0.3) : Theme.alpha(root.tone, 0.1) border.width: 1 border.color: Theme.alpha(root.tone, 0.6) transform: Matrix4x4 { matrix: Qt.matrix4x4(1, -Theme.skew, 0, 22 * Theme.skew, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) } } Text { id: actionLabel anchors.centerIn: parent text: actionChip.modelData.text color: Theme.text font.family: Theme.fontDisplay font.pixelSize: Theme.fsMicro font.italic: true font.weight: Font.DemiBold renderType: Text.NativeRendering } HoverHandler { id: actionHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: { actionChip.modelData.invoke(); root.dismissed(); } } } } } } // Close affordance. Icon { anchors.right: parent.right anchors.top: parent.top visible: hover.hovered text: "󰅖" color: closeHover.hovered ? Theme.accent : Theme.muted font.pixelSize: Theme.fsBody HoverHandler { id: closeHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: root.dismissed() } } } HoverHandler { id: hover } // Middle click dismisses, matching the old dunst muscle memory. TapHandler { acceptedButtons: Qt.MiddleButton onTapped: root.dismissed() } }