Load enabled service plugins

This commit is contained in:
David Heinemeier Hansson
2026-05-20 15:14:04 +02:00
parent 93db08b217
commit 1e9a7a971e
+37 -31
View File
@@ -145,7 +145,7 @@ ShellRoot {
// chains rescan() once the directory exists. We also kick a scan here in // chains rescan() once the directory exists. We also kick a scan here in
// case the user dir already existed at startup. // case the user dir already existed at startup.
pluginRegistry.rescan() pluginRegistry.rescan()
shell._syncFirstPartyServices() shell._syncServices()
} }
function mutateShellConfig(mutator) { function mutateShellConfig(mutator) {
@@ -166,28 +166,32 @@ ShellRoot {
shell: shell shell: shell
} }
// ---------------------------------------------------- first-party services // ------------------------------------------------------------- services
// //
// Generic loader for any first-party plugin that declares kind "service". // Generic loader for any enabled plugin that declares kind "service".
// The notification daemon is the first user; future first-party services // First-party infrastructure services are implicitly enabled by the registry;
// (background updaters, idle inhibitor, etc.) plug in here. // third-party services are enabled by adding the plugin id to shell.json.
Item { Item {
id: firstPartyServiceHost id: serviceHost
visible: false visible: false
} }
property var _firstPartyServices: ({}) property var _services: ({})
function firstPartyServiceFor(pluginId) { function serviceFor(pluginId) {
return _firstPartyServices[String(pluginId)] || null return _services[String(pluginId)] || null
} }
function ensureFirstPartyService(pluginId) { function firstPartyServiceFor(pluginId) {
return serviceFor(pluginId)
}
function ensureService(pluginId) {
var key = String(pluginId) var key = String(pluginId)
if (_firstPartyServices[key]) return _firstPartyServices[key] if (_services[key]) return _services[key]
var manifest = pluginRegistry && pluginRegistry.installedPlugins var manifest = pluginRegistry && pluginRegistry.installedPlugins
? pluginRegistry.installedPlugins[key] : null ? pluginRegistry.installedPlugins[key] : null
if (!manifest || !manifest.__isFirstParty) return null if (!manifest) return null
if (!Array.isArray(manifest.kinds) || manifest.kinds.indexOf("service") === -1) return null if (!Array.isArray(manifest.kinds) || manifest.kinds.indexOf("service") === -1) return null
if (!manifest.entryPoints || !manifest.entryPoints.service) return null if (!manifest.entryPoints || !manifest.entryPoints.service) return null
var url = pluginRegistry.entryPointUrl(manifest, "service") var url = pluginRegistry.entryPointUrl(manifest, "service")
@@ -196,58 +200,60 @@ ShellRoot {
var comp = Qt.createComponent(url, Component.PreferSynchronous) var comp = Qt.createComponent(url, Component.PreferSynchronous)
function finalize() { function finalize() {
if (comp.status !== Component.Ready) { if (comp.status !== Component.Ready) {
console.warn("first-party service load failed for " + key + ": " + comp.errorString()) console.warn("service plugin load failed for " + key + ": " + comp.errorString())
return return
} }
var inst = comp.createObject(firstPartyServiceHost) var inst = comp.createObject(serviceHost)
if (!inst) { if (!inst) {
console.warn("first-party service createObject returned null for", key) console.warn("service plugin createObject returned null for", key)
return return
} }
if ("omarchyPath" in inst) inst.omarchyPath = shell.omarchyPath if ("omarchyPath" in inst) inst.omarchyPath = shell.omarchyPath
if ("shell" in inst) inst.shell = shell if ("shell" in inst) inst.shell = shell
if ("manifest" in inst) inst.manifest = manifest if ("manifest" in inst) inst.manifest = manifest
if ("barWidgetRegistry" in inst) inst.barWidgetRegistry = shell.barWidgetRegistry
if ("pluginRegistry" in inst) inst.pluginRegistry = shell.pluginRegistry
var snext = ({}) var snext = ({})
for (var sk in _firstPartyServices) snext[sk] = _firstPartyServices[sk] for (var sk in _services) snext[sk] = _services[sk]
snext[key] = inst snext[key] = inst
_firstPartyServices = snext _services = snext
} }
if (comp.status === Component.Loading) { if (comp.status === Component.Loading) {
comp.statusChanged.connect(finalize) comp.statusChanged.connect(finalize)
return null return null
} }
finalize() finalize()
return _firstPartyServices[key] || null return _services[key] || null
} }
function _syncFirstPartyServices() { function _syncServices() {
if (!pluginRegistry || !pluginRegistry.installedPlugins) return if (!pluginRegistry || !pluginRegistry.installedPlugins) return
var plugins = pluginRegistry.installedPlugins var plugins = pluginRegistry.installedPlugins
for (var id in plugins) { for (var id in plugins) {
var m = plugins[id] var m = plugins[id]
if (!m || !m.__isFirstParty) continue if (!m) continue
if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue
if (!m.entryPoints || !m.entryPoints.service) continue if (!m.entryPoints || !m.entryPoints.service) continue
if (!pluginRegistry.isEnabled(id)) continue if (!pluginRegistry.isEnabled(id)) continue
if (_firstPartyServices[id]) continue if (_services[id]) continue
ensureFirstPartyService(id) ensureService(id)
} }
// Drop services for plugins that have been disabled or removed. // Drop services for plugins that have been disabled or removed.
for (var existingId in _firstPartyServices) { for (var existingId in _services) {
var stillThere = plugins[existingId] var stillThere = plugins[existingId]
var stillEnabled = stillThere && pluginRegistry.isEnabled(existingId) var stillEnabled = stillThere && pluginRegistry.isEnabled(existingId)
if (stillThere && stillEnabled) continue if (stillThere && stillEnabled) continue
var inst = _firstPartyServices[existingId] var inst = _services[existingId]
if (inst && typeof inst.destroy === "function") inst.destroy() if (inst && typeof inst.destroy === "function") inst.destroy()
var next = ({}) var next = ({})
for (var k in _firstPartyServices) if (k !== existingId) next[k] = _firstPartyServices[k] for (var k in _services) if (k !== existingId) next[k] = _services[k]
_firstPartyServices = next _services = next
} }
} }
Connections { Connections {
target: shell.pluginRegistry target: shell.pluginRegistry
function onPluginsChanged() { shell._syncFirstPartyServices() } function onPluginsChanged() { shell._syncServices() }
} }
// Writes inline settings to a bar layout entry or top-level plugin entry in // Writes inline settings to a bar layout entry or top-level plugin entry in
@@ -456,10 +462,10 @@ ShellRoot {
if ("manifest" in item) item.manifest = panelEntry.manifest if ("manifest" in item) item.manifest = panelEntry.manifest
if ("barWidgetRegistry" in item) item.barWidgetRegistry = shell.barWidgetRegistry if ("barWidgetRegistry" in item) item.barWidgetRegistry = shell.barWidgetRegistry
if ("pluginRegistry" in item) item.pluginRegistry = shell.pluginRegistry if ("pluginRegistry" in item) item.pluginRegistry = shell.pluginRegistry
// First-party plugins that pair a panel UI with a service entry // Plugins that pair a panel UI with a service entry read shared
// (e.g. omarchy.notifications) read shared state off `service`. We // state off `service`. Hand them the matching singleton if one was
// hand them the matching service instance if one was loaded. // loaded.
if ("service" in item) item.service = shell.firstPartyServiceFor(panelEntry.pluginId) if ("service" in item) item.service = shell.serviceFor(panelEntry.pluginId)
shell.registerPanelLoader(panelEntry.pluginId, this) shell.registerPanelLoader(panelEntry.pluginId, this)
} }
onStatusChanged: { onStatusChanged: {