Replace mako with quickshell-backed notification daemon
Adds first-party omarchy.notifications service plugin that hosts a freedesktop notification server and renders popups + a history popup inside the shell. Uninstalls mako and retargets every helper, keybind, indicator, and migration entry to the new daemon. Plugin (default/quickshell/omarchy-shell/plugins/notifications/): - Service.qml: NotificationServer, popupModel + pendingModel + pastModel (two-tier history, see below), DND via PersistentProperties + cache-file backstop, image cache for /tmp screenshots, IpcHandler with toggleDnd/setDnd/isDnd/showHistory/clear/clearPending/markAllSeen/ dismissAll/dismissOne/invokeLast/dismiss, per-theme override file ~/.config/omarchy/current/theme/notifications.json honoring borderColor/backgroundColor/textColor/countdownColor. - components/NotificationCard.qml: theme-driven card (Color.foreground/ background/border tokens from Commons/Color.qml), 32x32 icon slot, Nerd Font glyph fallback via omarchy-glyph hint, hero image strip for screenshot/image-path notifications, hover-pause progress bar, uses bar.fontFamily so all surfaces share one font. Filtering and DND: - transient hint and CLI-style senders (app_name in notify-send / omarchy-action) bypass history but still pop. - DND only allows omarchy-action toasts and notify-send -u critical through; real-app urgency=critical (Discord, Slack, Vesktop) is silenced and lands in pending instead. - Pending vs past split surfaced via tabs in the bar widget popup; past tab is auto-pruned at the 15-minute mark. - Click-to-jump: notifications without a libnotify default action focus the matching Hyprland window via class lookup. Shell host: - shell.qml: generic first-party service loader (mirrors the existing noctalia-compat path) and an alias for the bar so plugins can read barSize / barHidden / position for anchoring. - Commons/Color.qml: parses the theme's hyprland.conf for $activeBorderColor so notifications match Hyprland window borders; picks the explicit accent= key over the color4= alias. Bar widget rebase (plugins/bar/widgets/notificationCenter.qml): - Drops the chunk-1 stub server, binds count/dnd state to the service, hosts the history popup via PopupCard so it drops down from the notification glyph the same way Quick Settings does. - Pending/Past tabs, dismiss-individual close X, mark-all-as-seen and clear-recent action buttons, theme-driven palette. Quick Settings rework (plugins/bar/widgets/controlCenter.qml): - DND tile binds directly to service.doNotDisturb for instant feedback. - Drops the volume slider (already in audioPanel) and the no-op Theme tile; adds a Bluetooth toggle bound to Quickshell.Bluetooth. - Bigger 44x44 wallet was scaled back to 32x32 for tighter rows. Notification scripts (bin/omarchy-*): - omarchy-notification-send: passes glyph as a custom hint instead of prepending to the summary; adds -a omarchy-action and -u urgency automatically; supports -e/--transient passthrough. - User-action toasts in the capture / toggle / hyprland / default-* scripts and bindings/utilities.lua now tag themselves -a omarchy-action so DND treats them as intent-based bypass. - omarchy-toggle-notification-silencing, omarchy-notification-dismiss, default/waybar/indicators/notification-silencing.sh, and the Hyprland comma-keybinds all route through omarchy-shell-ipc notifications. - omarchy-capture-screenshot / -screenrecording set the image-path hint properly so the hero-image rendering kicks in. Mako removal (migrations/1778743515.sh): - pkill -x mako, systemctl --user stop mako.service, pacman -Rns mako (uninstalling deletes /usr/lib/systemd/user/mako.service so D-Bus activation can't respawn it). Removes ~/.config/mako/ and the legacy toggle file. Restarts quickshell so it claims the bus name. - Drops mako from install/omarchy-base.packages, autostart.lua, install/config/theme.sh + toggles.sh, default/themed/mako.ini.tpl, default/mako/, the omarchy-menu Mako restart row, bin/omarchy GROUP_DESCRIPTIONS, the settings panel catalogue, and the default/omarchy-skill paths table. - Removed scripts: bin/omarchy-restart-mako, bin/omarchy-style-corners-mako. - bin/omarchy-style-corners summary updated; corner radius for the notification card reads ~/.local/state/omarchy/toggles/quickshell-menu.json alongside the rest of the shell.
This commit is contained in:
@@ -158,6 +158,7 @@ ShellRoot {
|
||||
// chains rescan() once the directory exists. We also kick a scan here in
|
||||
// case the user dir already existed at startup.
|
||||
pluginRegistry.rescan()
|
||||
shell._syncFirstPartyServices()
|
||||
|
||||
// Wire Noctalia compat singletons. Plugins read state from these globals;
|
||||
// we hand them references to the host so they can route into the right
|
||||
@@ -178,6 +179,10 @@ ShellRoot {
|
||||
persistShellConfig(copy)
|
||||
}
|
||||
|
||||
// Exposed as a property so child plugins (notifications, future panels)
|
||||
// can read barSize/barHidden/position to anchor relative to the bar.
|
||||
property alias bar: bar
|
||||
|
||||
Bar {
|
||||
id: bar
|
||||
omarchyPath: shell.omarchyPath
|
||||
@@ -351,6 +356,94 @@ ShellRoot {
|
||||
function onPluginsChanged() { shell._syncNoctaliaServices() }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------- first-party services
|
||||
//
|
||||
// Generic loader for any first-party plugin that declares kind "service".
|
||||
// The notification daemon is the first user; future first-party services
|
||||
// (background updaters, idle inhibitor, etc.) plug in here. Mirrors
|
||||
// ensureNoctaliaService but skips the noctalia-specific pluginApi handoff
|
||||
// and gates on `__isFirstParty` instead of `__noctaliaCompat`.
|
||||
Item {
|
||||
id: firstPartyServiceHost
|
||||
visible: false
|
||||
}
|
||||
|
||||
property var _firstPartyServices: ({})
|
||||
|
||||
function firstPartyServiceFor(pluginId) {
|
||||
return _firstPartyServices[String(pluginId)] || null
|
||||
}
|
||||
|
||||
function ensureFirstPartyService(pluginId) {
|
||||
var key = String(pluginId)
|
||||
if (_firstPartyServices[key]) return _firstPartyServices[key]
|
||||
var manifest = pluginRegistry && pluginRegistry.installedPlugins
|
||||
? pluginRegistry.installedPlugins[key] : null
|
||||
if (!manifest || !manifest.__isFirstParty) return null
|
||||
if (manifest.__noctaliaCompat) return null
|
||||
if (!Array.isArray(manifest.kinds) || manifest.kinds.indexOf("service") === -1) return null
|
||||
if (!manifest.entryPoints || !manifest.entryPoints.service) return null
|
||||
var url = pluginRegistry.entryPointUrl(manifest, "service")
|
||||
if (!url) return null
|
||||
|
||||
var comp = Qt.createComponent(url, Component.PreferSynchronous)
|
||||
function finalize() {
|
||||
if (comp.status !== Component.Ready) {
|
||||
console.warn("first-party service load failed for " + key + ": " + comp.errorString())
|
||||
return
|
||||
}
|
||||
var inst = comp.createObject(firstPartyServiceHost, {
|
||||
omarchyPath: shell.omarchyPath,
|
||||
shell: shell,
|
||||
manifest: manifest
|
||||
})
|
||||
if (!inst) {
|
||||
console.warn("first-party service createObject returned null for", key)
|
||||
return
|
||||
}
|
||||
var snext = ({})
|
||||
for (var sk in _firstPartyServices) snext[sk] = _firstPartyServices[sk]
|
||||
snext[key] = inst
|
||||
_firstPartyServices = snext
|
||||
}
|
||||
if (comp.status === Component.Loading) {
|
||||
comp.statusChanged.connect(finalize)
|
||||
return null
|
||||
}
|
||||
finalize()
|
||||
return _firstPartyServices[key] || null
|
||||
}
|
||||
|
||||
function _syncFirstPartyServices() {
|
||||
if (!pluginRegistry || !pluginRegistry.installedPlugins) return
|
||||
var plugins = pluginRegistry.installedPlugins
|
||||
for (var id in plugins) {
|
||||
var m = plugins[id]
|
||||
if (!m || !m.__isFirstParty || m.__noctaliaCompat) continue
|
||||
if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue
|
||||
if (!m.entryPoints || !m.entryPoints.service) continue
|
||||
if (!pluginRegistry.isEnabled(id)) continue
|
||||
if (_firstPartyServices[id]) continue
|
||||
ensureFirstPartyService(id)
|
||||
}
|
||||
// Drop services for plugins that have been disabled or removed.
|
||||
for (var existingId in _firstPartyServices) {
|
||||
var stillThere = plugins[existingId]
|
||||
var stillEnabled = stillThere && pluginRegistry.isEnabled(existingId)
|
||||
if (stillThere && stillEnabled) continue
|
||||
var inst = _firstPartyServices[existingId]
|
||||
if (inst && typeof inst.destroy === "function") inst.destroy()
|
||||
var next = ({})
|
||||
for (var k in _firstPartyServices) if (k !== existingId) next[k] = _firstPartyServices[k]
|
||||
_firstPartyServices = next
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: shell.pluginRegistry
|
||||
function onPluginsChanged() { shell._syncFirstPartyServices() }
|
||||
}
|
||||
|
||||
// Used by Noctalia compat: pluginApi.saveSettings() ends up writing inline
|
||||
// settings to the plugin's entry in shell.json. moduleName is the entry id
|
||||
// (the bare manifest id, e.g. "noctalia.air-quality"); settings is the
|
||||
@@ -564,6 +657,10 @@ ShellRoot {
|
||||
if ("manifest" in item) item.manifest = panelEntry.manifest
|
||||
if ("barWidgetRegistry" in item) item.barWidgetRegistry = shell.barWidgetRegistry
|
||||
if ("pluginRegistry" in item) item.pluginRegistry = shell.pluginRegistry
|
||||
// First-party plugins that pair a panel UI with a service entry
|
||||
// (e.g. omarchy.notifications) read shared state off `service`. We
|
||||
// hand them the matching service instance if one was loaded.
|
||||
if ("service" in item) item.service = shell.firstPartyServiceFor(panelEntry.pluginId)
|
||||
// Noctalia panel/overlay/menu plugins expect a pluginApi just like
|
||||
// their bar widgets do. First-party Omarchy plugins don't declare
|
||||
// the property so the `in target` check skips them.
|
||||
|
||||
Reference in New Issue
Block a user