swadhinbiswas Add notification-center bar widget + history panel; toasts render on focused monitor with finite expiries · 8/24/2026
eb36c6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Ui
BarWidget {
id: root
moduleName: "deomarchy.notification-center"
// The notification service is a separate first-party plugin; reach it
// through the shell's service map so the bell reacts to live toasts.
readonly property var notifService: root.bar && root.bar.shell
? (root.bar.shell.firstPartyServiceFor("omarchy.notifications")
|| root.bar.shell.serviceFor("omarchy.notifications"))
: null
readonly property int liveCount: notifService ? notifService.popupModel.count : 0
readonly property bool dndOn: notifService ? notifService.doNotDisturb : false
readonly property bool opened: panelItem ? panelItem.opened === true : false
function open() { if (panelItem) panelItem.open() }
function close() { if (panelItem) panelItem.close() }
function togglePanel() { if (panelItem) panelItem.toggle() }
readonly property bool popoutSwitchClosing: panelItem ? panelItem.popoutSwitchClosing === true : false
function closeForPopoutSwitch() { if (panelItem) panelItem.closeForPopoutSwitch() }
property var panelItem: null
function injectPanel() {
var target = panelLoader.item
if (!target) return
panelItem = target
if ("bar" in target) target.bar = root.bar
if ("settings" in target) target.settings = root.settings
if ("anchorItem" in target) target.anchorItem = button
if ("hostWidget" in target) target.hostWidget = root
}
implicitWidth: button.implicitWidth + (badge.visible ? badge.width : 0)
implicitHeight: button.implicitHeight
onBarChanged: injectPanel()
onSettingsChanged: injectPanel()
Loader {
id: panelLoader
active: true
source: Qt.resolvedUrl("Panel.qml")
visible: false
onLoaded: {
root.injectPanel()
Qt.callLater(root.injectPanel)
}
}
IpcHandler {
target: "deomarchy.notification-center"
function open(): void { root.open() }
function close(): void { root.close() }
function toggle(): void { root.togglePanel() }
}
BarIconButton {
id: button
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
bar: root.bar
// md-bell / md-bell-ring / md-bell-off from JetBrainsMono Nerd Font — the
// family the bar already renders with, matching the other widgets.
text: String.fromCodePoint(
root.dndOn ? 0xF009C
: (root.liveCount > 0 ? 0xF009D : 0xF009A))
tooltipText: {
if (root.dndOn) return "Notifications muted (DND)"
if (root.liveCount > 0) return root.liveCount + " notification" + (root.liveCount === 1 ? "" : "s")
return "Notification Center"
}
onPressed: function(b) {
if (b === Qt.LeftButton) root.togglePanel()
else if (b === Qt.RightButton && root.notifService)
root.notifService.setDoNotDisturb(!root.notifService.doNotDisturb)
}
}
// Unread pill beside the bell while live toasts are up.
Rectangle {
id: badge
visible: root.liveCount > 0 && !root.dndOn
anchors.left: button.right
anchors.leftMargin: -2
anchors.top: parent.top
anchors.topMargin: 2
implicitWidth: Math.max(14, countLabel.implicitWidth + 6)
implicitHeight: 14
radius: height / 2
color: Color.accent
TextMetrics {
id: countText
text: root.liveCount > 9 ? "9+" : String(root.liveCount)
}
Text {
id: countLabel
anchors.centerIn: parent
text: countText.text
color: Color.background
font.family: root.bar && root.bar.fontFamily ? root.bar.fontFamily : "JetBrainsMono Nerd Font"
font.pixelSize: 8
font.bold: true
}
}
}