Port the AGS widgets into the shell as plugins

This commit is contained in:
2026-09-20 00:09:41 -04:00
parent c5434026a8
commit 0c422e7345
31 changed files with 1740 additions and 17 deletions
@@ -0,0 +1,198 @@
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import qs.Commons
import qs.Ui
import "HistoryModel.js" as HistoryModel
Item {
id: root
property var shell: null
property var manifest: null
property bool opened: false
readonly property string pluginId: (manifest && manifest.id) || "blob.notification-center"
readonly property string historyDir: Quickshell.env("HOME") + "/.local/state/blob/notifications/history"
property var entries: []
property bool doNotDisturb: false
readonly property bool empty: root.entries.length === 0
function open(payloadJson) {
root.opened = true
root.refresh()
}
function close() {
root.opened = false
}
function dismiss() {
root.opened = false
if (root.shell && typeof root.shell.hide === "function")
root.shell.hide(root.pluginId)
}
function refresh() {
if (!historyProbe.running) historyProbe.running = true
if (!dndProbe.running) dndProbe.running = true
}
function run(command) {
Quickshell.execDetached(["bash", "-c", command])
}
function clearAll() {
root.run("blob-shell notifications clearHistory")
root.entries = []
}
function toggleSilence() {
root.run("blob-shell notifications toggleDnd")
refreshTimer.restart()
}
function dismissEntry(index) {
var entry = root.entries[index]
if (!entry) return
root.run("blob-notify-dismiss " + entry.id)
var next = []
for (var i = 0; i < root.entries.length; i++)
if (i !== index) next.push(root.entries[i])
root.entries = next
}
Process {
id: historyProbe
command: ["bash", "-c", "cat " + root.historyDir + "/*.json 2>/dev/null || true"]
stdout: StdioCollector {
onStreamFinished: root.entries = HistoryModel.parseHistory(text)
}
}
Process {
id: dndProbe
command: ["bash", "-c", "blob-shell notifications dndState 2>/dev/null"]
stdout: StdioCollector {
onStreamFinished: root.doNotDisturb = String(text).trim() === "on"
}
}
Timer {
id: refreshTimer
interval: 250
onTriggered: root.refresh()
}
Timer {
interval: 5000
running: root.opened
repeat: true
onTriggered: root.refresh()
}
PanelWindow {
visible: root.opened
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
WlrLayershell.namespace: "blob-notification-center"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
MouseArea {
anchors.fill: parent
onClicked: root.dismiss()
}
Card {
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: Style.spaceReal(10)
anchors.rightMargin: Style.spaceReal(10)
implicitWidth: Style.spaceReal(440)
implicitHeight: Style.spaceReal(420)
fillColor: Util.alpha(Color.background, Style.panelFillAlpha)
spacing: Style.spaceReal(10)
Item {
width: parent.width
implicitHeight: titleText.implicitHeight
Text {
id: titleText
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
text: "Notifications"
textFormat: Text.PlainText
color: Color.foreground
font.family: Style.font.family
font.pixelSize: Style.font.body
font.bold: true
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Style.spaceReal(4)
CardIconButton {
icon: root.doNotDisturb ? "" : ""
active: root.doNotDisturb
onActivated: root.toggleSilence()
}
CardIconButton {
icon: ""
onActivated: root.clearAll()
}
}
}
ListView {
width: parent.width
height: Style.spaceReal(340)
clip: true
spacing: Style.spaceReal(8)
visible: !root.empty
model: root.entries
delegate: NotificationItem {
required property var modelData
required property int index
width: ListView.view.width
app: modelData.app
summary: modelData.summary
body: modelData.body
onDismissRequested: root.dismissEntry(index)
}
}
Column {
width: parent.width
visible: root.empty
spacing: Style.spaceReal(8)
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: ""
textFormat: Text.PlainText
color: Util.alpha(Color.blue, 0.6)
font.family: Style.font.family
font.pixelSize: Style.font.heading
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: "No notifications"
textFormat: Text.PlainText
color: Util.alpha(Color.foreground, 0.6)
font.family: Style.font.family
font.pixelSize: Style.font.bodySmall
}
}
}
}
}