diff --git a/default/quickshell/omarchy-shell/plugins/bar/widgets/notificationCenter.qml b/default/quickshell/omarchy-shell/plugins/bar/widgets/notificationCenter.qml index 6e91b41c..2cfbe572 100644 --- a/default/quickshell/omarchy-shell/plugins/bar/widgets/notificationCenter.qml +++ b/default/quickshell/omarchy-shell/plugins/bar/widgets/notificationCenter.qml @@ -30,6 +30,22 @@ Item { ? hostShell.firstPartyServiceFor("omarchy.notifications") : null + function isChromiumDerived(app, appIcon) { + var source = (String(app || "") + "\n" + String(appIcon || "")).toLowerCase() + return source.indexOf("chrom") >= 0 || source.indexOf("brave") >= 0 || + source.indexOf("vivaldi") >= 0 || source.indexOf("microsoft-edge") >= 0 || + source.indexOf("opera") >= 0 + } + + function sanitizeBody(s, app, appIcon) { + var text = String(s || "").replace(/]*>/gi, "") + if (!isChromiumDerived(app, appIcon)) return text + + return text + .replace(/^\s*]*>\s*(?:https?:\/\/|www\.)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/[^<\s]*)?\s*<\/a>\s*/i, "") + .replace(/^\s*(?:https?:\/\/|www\.)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/\S*)?\s+/i, "") + } + readonly property int pendingCount: notificationService ? notificationService.pendingModel.count : 0 readonly property int pastCount: notificationService ? notificationService.pastModel.count : 0 readonly property bool dnd: notificationService ? notificationService.doNotDisturb : false @@ -272,6 +288,7 @@ Item { image.indexOf("image://icon//") === 0 || image.indexOf("file://") === 0) readonly property string smallIconSource: image.length > 0 ? image : appIcon readonly property bool hasIcon: !hasMedia && smallIconSource.length > 0 + readonly property string sanitizedBody: root.sanitizeBody(body, app, appIcon) width: listView.width implicitHeight: rowContent.implicitHeight + 20 @@ -334,10 +351,10 @@ Item { Text { Layout.fillWidth: true - visible: rowCard.body.length > 0 - text: String(rowCard.body).replace(/]*>/gi, "") - font.family: root.bar ? root.bar.fontFamily : "" -textFormat: Text.PlainText + visible: rowCard.sanitizedBody.length > 0 + text: rowCard.sanitizedBody + font.family: root.bar ? root.bar.fontFamily : "" + textFormat: Text.PlainText color: root.colDim font.pixelSize: 11 wrapMode: Text.WordWrap diff --git a/default/quickshell/omarchy-shell/plugins/notifications/components/NotificationCard.qml b/default/quickshell/omarchy-shell/plugins/notifications/components/NotificationCard.qml index 9980b70d..1a49b117 100644 --- a/default/quickshell/omarchy-shell/plugins/notifications/components/NotificationCard.qml +++ b/default/quickshell/omarchy-shell/plugins/notifications/components/NotificationCard.qml @@ -66,6 +66,13 @@ Rectangle { readonly property bool hasGlyph: glyph.length > 0 readonly property bool inlineGlyph: summary.match(/^[^\s]+\s{2,}/) !== null readonly property bool hasSmallIcon: !mediaMode && !inlineGlyph && (smallIconSource.length > 0 || hasGlyph) + readonly property bool chromiumDerived: { + var source = (app + "\n" + appIcon).toLowerCase() + return source.indexOf("chrom") >= 0 || source.indexOf("brave") >= 0 || + source.indexOf("vivaldi") >= 0 || source.indexOf("microsoft-edge") >= 0 || + source.indexOf("opera") >= 0 + } + readonly property string sanitizedBody: sanitizeBody(body) readonly property color dimColor: Qt.darker(Color.notifications.text, 1.4) readonly property color bodyColor: Qt.darker(Color.notifications.text, 1.15) @@ -73,12 +80,15 @@ Rectangle { readonly property color accentColor: urgency === 2 ? Color.urgent : (urgency === 0 ? dimColor : Color.notifications.countdown) function sanitizeBody(s) { - return String(s) - .replace(/]*>/gi, "") - // Chromium prepends the site origin to some web-push bodies - // (e.g. "web.whatsapp.com Message text"). The app/icon already - // identifies the sender, so hide that noisy URL/domain prefix. - .replace(/^(?:https?:\/\/)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?\/?\s+/i, "") + var text = String(s).replace(/]*>/gi, "") + if (!chromiumDerived) return text + + // Chromium web notifications often prefix the body with the sending + // origin, sometimes as a hyperlink. The browser icon already identifies + // the source, so drop only that leading URL/domain. + return text + .replace(/^\s*]*>\s*(?:https?:\/\/|www\.)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/[^<\s]*)?\s*<\/a>\s*/i, "") + .replace(/^\s*(?:https?:\/\/|www\.)?(?:[a-z0-9-]+\.)+[a-z]{2,}(?::\d+)?(?:\/\S*)?\s+/i, "") } implicitWidth: 380 @@ -216,8 +226,8 @@ Rectangle { Text { Layout.fillWidth: true Layout.topMargin: 2 - visible: root.body.length > 0 - text: root.sanitizeBody(root.body) + visible: root.sanitizedBody.length > 0 + text: root.sanitizedBody textFormat: Text.StyledText font.family: "Liberation Sans" color: root.bodyColor