Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions urgent-on-notification/Main.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import QtQuick
import Quickshell.Io
import Quickshell.Services.Notifications

import qs.Services.System

Item {
id: root
property var pluginApi: null

property var trackedNotifs: []

property var niriWindowListProcess: Component {
Process {
property string appEntry: ""
property bool unset: false

command: ["niri", "msg", "--json", "windows"]

stdout: SplitParser {
onRead: data => {
try {
const windows = JSON.parse(data);
const matches = windows.filter(w => w.app_id && w.app_id.toLowerCase() === appEntry || w.app_id.includes(appEntry) || appEntry.includes(w.app_id));
if (matches.length > 0) {
matches.forEach(m => {
niriUrgentProcess.createObject(root, {
windowId: m.id,
unset: unset
}).running = true;
});
}
} catch (e) {}
destroy();
}
}
}
}

property var niriUrgentProcess: Component {
Process {
property int windowId: 0
property bool unset: false
command: ["niri", "msg", "action", unset ? "unset-window-urgent" : "set-window-urgent", "--id", String(windowId)]
onExited: destroy()
}
}

property var server: NotificationServer {
keepOnReload: false
imageSupported: true
actionsSupported: true

onNotification: notification => {
const entry = (notification.desktopEntry || notification.appName || "").toLowerCase();
if (!entry)
return;
trackedNotifs = [...trackedNotifs,
{
id: notification.id,
entry: entry
}
];
niriWindowListProcess.createObject(root, {
appEntry: entry.toLowerCase()
}).running = true;
}
}

property var historyWatcher: Connections {
target: NotificationService.historyModel

function onCountChanged() {
const model = NotificationService.historyModel;

const currentIds = new Set();
for (let i = 0; i < model.count; i++) {
const n = model.get(i);
currentIds.add(n.originalId);
}

const removedNotifs = trackedNotifs.filter(n => !currentIds.has(n.id));
const remainingNotifs = trackedNotifs.filter(n => currentIds.has(n.id));

for (const n of removedNotifs) {
const appHasMultipleNotifs = remainingNotifs.some(r => r.entry === n.entry);
if (!appHasMultipleNotifs) {
niriWindowListProcess.createObject(root, {
appEntry: n.entry.toLowerCase(),
unset: true
}).running = true;
}
}

trackedNotifs = remainingNotifs;
}
}
}
20 changes: 20 additions & 0 deletions urgent-on-notification/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Set Urgent Window on Notification

This plugin captures notifications and sets the related window's urgent status on Niri.
Useful for having urgent borders on multi-monitor setups.

---

## Features

* Set window to urgent on notification received
* If multiple windows exist for the same app, all of them are set to urgent
* Unset urgent status when all notifications are cleared for an app
* Some leniency when matching based on app ID

---

## Requirements

* Niri
* Usage of Noctalia's notification history
24 changes: 24 additions & 0 deletions urgent-on-notification/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "urgent-on-notification",
"name": "Set Window to Urgent on Notification",
"version": "1.0.0",
"minNoctaliaVersion": "3.6.0",
"author": "matt-hb",
"tags": [
"System",
"Utility",
"Niri"
],
"license": "MIT",
"repository": "https://github.com/noctalia-dev/noctalia-plugins",
"description": "Sets windows to urgent when they receive a notification and sets them back to normal when their notifications are cleared",
"entryPoints": {
"main": "Main.qml"
},
"dependencies": {
"plugins": []
},
"metadata": {
"defaultSettings": {}
}
}
Binary file added urgent-on-notification/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.