Furter notification tweaks

This commit is contained in:
David Heinemeier Hansson
2026-05-16 10:05:30 +02:00
parent 1513adb339
commit ecd9dc6ffe
3 changed files with 23 additions and 20 deletions
+4 -3
View File
@@ -68,11 +68,12 @@ done
# Tag as a user-action toast so it pops through DND. See dnd-fix-plan.md. # Tag as a user-action toast so it pops through DND. See dnd-fix-plan.md.
args+=("-a" "omarchy-action" "-u" "$urgency") args+=("-a" "omarchy-action" "-u" "$urgency")
# Glyph is now a custom hint the quickshell daemon renders into the icon # Send the glyph both as a custom hint and in a summary prefix.
# slot, not prepended to the summary. Old mako-era formatting was glyph + 4 # Quickshell strips the prefix back into the icon slot; other notification
# spaces + headline, which leaves ugly whitespace in the new card design. # daemons still get the familiar glyph + spacing presentation.
if [[ -n $glyph ]]; then if [[ -n $glyph ]]; then
args+=("--hint=string:omarchy-glyph:$glyph") args+=("--hint=string:omarchy-glyph:$glyph")
headline="$glyph $headline"
fi fi
if [[ -n $description ]]; then if [[ -n $description ]]; then
@@ -67,6 +67,7 @@ Item {
onFileChanged: reload() onFileChanged: reload()
} }
// Fired by IPC (`omarchy-shell-ipc notifications showHistory`) so the // Fired by IPC (`omarchy-shell-ipc notifications showHistory`) so the
// bar widget can drop its PopupCard from the same anchor a click would. // bar widget can drop its PopupCard from the same anchor a click would.
signal historyOpenRequested() signal historyOpenRequested()
@@ -142,16 +143,20 @@ Item {
function snapshotOf(notification) { function snapshotOf(notification) {
var glyph = "" var glyph = ""
try { try {
if (notification.hints && typeof notification.hints["omarchy-glyph"] === "string") { if (notification.hints) {
glyph = notification.hints["omarchy-glyph"] var hintGlyph = notification.hints["omarchy-glyph"]
if (hintGlyph !== undefined && hintGlyph !== null)
glyph = String(hintGlyph)
} }
} catch (e) { glyph = "" } } catch (e) { glyph = "" }
var summary = String(notification.summary || "")
return { return {
id: notification.id, id: notification.id,
originalId: notification.id, originalId: notification.id,
app: notification.appName || "", app: notification.appName || "",
appIcon: notification.appIcon || "", appIcon: notification.appIcon || "",
summary: notification.summary || "", summary: summary,
body: notification.body || "", body: notification.body || "",
image: notification.image || "", image: notification.image || "",
glyph: glyph, glyph: glyph,
@@ -54,12 +54,8 @@ Rectangle {
lower.endsWith(".jpeg") || lower.endsWith(".webp") || lower.endsWith(".jpeg") || lower.endsWith(".webp") ||
lower.endsWith(".gif") lower.endsWith(".gif")
} }
readonly property string mediaImageSource: { readonly property string mediaImageSource: ""
if (_isMediaFile(_imageFilePath(image))) return image readonly property bool mediaMode: false
if (_isMediaFile(_imageFilePath(appIcon))) return appIcon
return ""
}
readonly property bool mediaMode: mediaImageSource.length > 0
// Use only what the notification explicitly carries — no themed-icon // Use only what the notification explicitly carries — no themed-icon
// theme-lookup fallback because Quickshell's icon image provider returns // theme-lookup fallback because Quickshell's icon image provider returns
// a placeholder for missing names (rather than erroring), which means // a placeholder for missing names (rather than erroring), which means
@@ -68,7 +64,8 @@ Rectangle {
// `appIcon` (-i flag) still get one. // `appIcon` (-i flag) still get one.
readonly property string smallIconSource: image.length > 0 ? image : appIcon readonly property string smallIconSource: image.length > 0 ? image : appIcon
readonly property bool hasGlyph: glyph.length > 0 readonly property bool hasGlyph: glyph.length > 0
readonly property bool hasSmallIcon: !mediaMode && (smallIconSource.length > 0 || hasGlyph) readonly property bool inlineGlyph: summary.match(/^\S\s{2,}/) !== null
readonly property bool hasSmallIcon: !mediaMode && !inlineGlyph && (smallIconSource.length > 0 || hasGlyph)
readonly property color dimColor: Qt.darker(Color.notifications.text, 1.4) readonly property color dimColor: Qt.darker(Color.notifications.text, 1.4)
readonly property color bodyColor: Qt.darker(Color.notifications.text, 1.15) readonly property color bodyColor: Qt.darker(Color.notifications.text, 1.15)
@@ -157,24 +154,24 @@ Rectangle {
Layout.rightMargin: 12 Layout.rightMargin: 12
Layout.topMargin: 10 Layout.topMargin: 10
Layout.bottomMargin: 10 Layout.bottomMargin: 10
spacing: 10 spacing: 12
Item { Item {
id: smallIconSlot id: smallIconSlot
Layout.preferredWidth: 32 Layout.preferredWidth: 40
Layout.preferredHeight: 32 Layout.preferredHeight: 40
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
// Hide the slot when the icon failed to resolve (themed-icon name // Hide the slot when the icon failed to resolve (themed-icon name
// not in the user's icon theme) AND we don't have a glyph fallback // not in the user's icon theme) AND we don't have a glyph fallback
// — prevents rendering Qt's pink broken-image placeholder. // — prevents rendering Qt's pink broken-image placeholder.
visible: false visible: root.hasGlyph || (!root.mediaMode && smallIconSource.length > 0 && smallIconImage.status !== Image.Error)
Image { Image {
id: smallIconImage id: smallIconImage
anchors.fill: parent anchors.fill: parent
source: root.smallIconSource source: root.smallIconSource
sourceSize.width: 32 * Screen.devicePixelRatio sourceSize.width: 40 * Screen.devicePixelRatio
sourceSize.height: 32 * Screen.devicePixelRatio sourceSize.height: 40 * Screen.devicePixelRatio
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
asynchronous: true asynchronous: true
smooth: true smooth: true
@@ -219,7 +216,7 @@ Rectangle {
textFormat: Text.StyledText textFormat: Text.StyledText
font.family: root.fontFamily font.family: root.fontFamily
color: root.bodyColor color: root.bodyColor
font.pixelSize: 12 font.pixelSize: 13
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
elide: Text.ElideRight elide: Text.ElideRight
maximumLineCount: 3 maximumLineCount: 3