Bound the notification app list, and give Focus a real editor

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 14:25:26 -04:00
parent 07db1068f1
commit d5b6e62515
21 changed files with 1405 additions and 453 deletions
@@ -294,14 +294,13 @@ Singleton {
}
]
},
// Grouped with the workspaces rather than with focus, because a group is
// where a setting is found rather than what it is about: the slider that
// sets this is a schema-bound row on WorkspacesPage. The focus group
// routes to Notifications, which is where focusModes renders.
// Set by the duration chips on the Focus tab, which is also where
// focusModes renders and where the focus group routes -- one editor,
// one page, so search and the docs point at the only place it exists.
{
key: "focusDurationMinutes", type: "int", def: 45, min: 5, max: 180, step: 5,
unit: "min",
group: "workspaces",
group: "focus",
label: "Focus session length",
detail: "How long a focus session runs before it ends itself"
},
@@ -1490,6 +1489,20 @@ Singleton {
label: "Visible banners",
detail: "Maximum notification banners shown at once"
},
// The one hole anyone is allowed to punch in Do Not Disturb. Off by
// default, because Do Not Disturb that lets something through anyway is
// not the thing most people asked for -- and "critical" is the sender's
// word, not yours, so an application that calls everything critical
// would otherwise defeat the switch on its own say-so. Turned on, it
// shows a banner for critical notifications while a mode or a manual Do
// Not Disturb is silencing everything else; the per-application urgency
// override is how you decide which senders get to claim it.
{
key: "criticalBreaksThrough", type: "bool", def: false,
group: "notifications",
label: "Critical alerts break through",
detail: "Show critical notifications as banners even while Do Not Disturb is on"
},
// ── Sound ───────────────────────────────────────────────────────────
// The two audio preferences that are Panama's own. Everything else on
@@ -1618,8 +1631,17 @@ Singleton {
},
// ── Per-application notification rules ──────────────────────────────
// { "<appId>": { enabled } } -- lock-screen fields from older rules are
// dropped at normalization; hyprlock cannot render notifications.
// { "<appId>": { enabled, sound, display, urgency, lastSeenMs, name,
// icon } -- every field past `enabled` is optional, so rules written
// when this held only `enabled` still load. Lock-screen fields from
// older rules are dropped at normalization; hyprlock cannot render
// notifications.
//
// `name` and `icon` are a cache for the settings list, not the source:
// resolution stays live-first through DesktopEntries, and these only
// stand in for an application that is not installed (or not scanned
// yet). `lastSeenMs` is stamped on every notification and is what puts
// an application in the "Recent" section.
//
// Absent means "no rule", which is not the same as a rule that allows
// everything: a new application must be able to notify without needing
@@ -1630,7 +1652,7 @@ Singleton {
key: "notificationAppRules", type: "json", def: ({}), group: "notifications",
internal: true,
label: "Application notification rules",
detail: "Per-application notification and lock-screen visibility preferences"
detail: "Per-application notification sound, banner, and urgency preferences"
},
// ── Internal ────────────────────────────────────────────────────────
@@ -82,6 +82,9 @@ Singleton {
readonly property int notificationTimeoutCriticalMs: DesktopPreferences.get("notificationTimeoutCriticalMs") // 0 = never auto-expire
readonly property int notificationHistoryLimit: DesktopPreferences.get("notificationHistoryLimit")
readonly property int maxVisibleToasts: DesktopPreferences.get("maxVisibleToasts")
// The single exception to Do Not Disturb, read by the popup gate in
// services/Notifs.qml. Off means Do Not Disturb is absolute.
readonly property bool criticalBreaksThrough: DesktopPreferences.get("criticalBreaksThrough")
// ── Sound ───────────────────────────────────────────────────────────────
// Over-amplification is the clamp ceiling for output volume: off means 1.0,