mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-04 20:28:23 +02:00
Remove unreleased Noctalia shell compatibility
This commit is contained in:
@@ -5,9 +5,6 @@ import Quickshell.Io
|
||||
|
||||
import "plugins/bar"
|
||||
import "services"
|
||||
import "compat/noctalia" as Compat
|
||||
import qs.Services.UI as NoctaliaUI
|
||||
import qs.Commons as NoctaliaCommons
|
||||
|
||||
ShellRoot {
|
||||
id: shell
|
||||
@@ -161,20 +158,8 @@ ShellRoot {
|
||||
// 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
|
||||
// popup, tooltip, etc.
|
||||
NoctaliaUI.BarService.bar = bar
|
||||
NoctaliaUI.TooltipService.bar = bar
|
||||
NoctaliaUI.PanelService.bar = bar
|
||||
NoctaliaUI.PanelService.shell = shell
|
||||
NoctaliaCommons.Settings.shellConfig = shell.shellConfig
|
||||
}
|
||||
|
||||
// Keep Noctalia.Settings.data in sync with whatever shell.json currently is.
|
||||
onShellConfigChanged: NoctaliaCommons.Settings.shellConfig = shell.shellConfig
|
||||
|
||||
function mutateShellConfig(mutator) {
|
||||
var copy = JSON.parse(JSON.stringify(shellConfig || builtinShellConfig))
|
||||
mutator(copy)
|
||||
@@ -190,181 +175,14 @@ ShellRoot {
|
||||
omarchyPath: shell.omarchyPath
|
||||
barWidgetRegistry: shell.barWidgetRegistry
|
||||
barConfig: shell.barConfig
|
||||
pluginRegistry: shell.pluginRegistry
|
||||
shell: shell
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Noctalia plugin API
|
||||
//
|
||||
// One pluginApi instance per plugin, cached for the life of the shell. The
|
||||
// factory is host-owned (shell, not bar) so non-bar Noctalia plugins
|
||||
// (panels, services) can call into it too. Settings lookups walk the live
|
||||
// shell.json on every read so reorders/edits never leave a stale entry
|
||||
// captured in the closure.
|
||||
Compat.PluginApiFactory { id: noctaliaApiFactory }
|
||||
|
||||
Item {
|
||||
id: noctaliaServiceHost
|
||||
visible: false
|
||||
}
|
||||
|
||||
property var _noctaliaApis: ({})
|
||||
property var _noctaliaServices: ({})
|
||||
|
||||
function findShellEntry(pluginId) {
|
||||
var config = shellConfig
|
||||
if (!isPlainObject(config)) return null
|
||||
if (isPlainObject(config.bar) && isPlainObject(config.bar.layout)) {
|
||||
var sections = ["left", "center", "right"]
|
||||
for (var s = 0; s < sections.length; s++) {
|
||||
var arr = config.bar.layout[sections[s]]
|
||||
if (!Array.isArray(arr)) continue
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] && arr[i].id === pluginId) return arr[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(config.plugins)) {
|
||||
for (var p = 0; p < config.plugins.length; p++) {
|
||||
if (config.plugins[p] && config.plugins[p].id === pluginId) return config.plugins[p]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function noctaliaPluginApiFor(pluginId) {
|
||||
var key = String(pluginId)
|
||||
if (!key) return null
|
||||
var existing = _noctaliaApis[key]
|
||||
if (existing) return existing
|
||||
var manifest = pluginRegistry && pluginRegistry.installedPlugins
|
||||
? pluginRegistry.installedPlugins[key] : null
|
||||
if (!manifest || !manifest.__noctaliaCompat) return null
|
||||
|
||||
var api = noctaliaApiFactory.create(
|
||||
key,
|
||||
manifest,
|
||||
function() {
|
||||
// Compute effective settings on every read. We can't capture an entry
|
||||
// reference because mutateSection rebuilds the layout objects and the
|
||||
// captured reference would point at a snapshot.
|
||||
var defaults = (manifest.barWidget && manifest.barWidget.defaults) || manifest.defaults || {}
|
||||
var merged = {}
|
||||
for (var k in defaults) merged[k] = defaults[k]
|
||||
var entry = findShellEntry(key)
|
||||
if (entry) {
|
||||
for (var ek in entry) if (ek !== "id") merged[ek] = entry[ek]
|
||||
}
|
||||
return merged
|
||||
},
|
||||
{
|
||||
persistSettings: function(_pluginId, settings) {
|
||||
if (typeof shell.updateEntryInline === "function")
|
||||
shell.updateEntryInline(key, settings)
|
||||
},
|
||||
openPanel: function(_pluginId, _screen, _btn) {
|
||||
shell.summon(key, JSON.stringify({ source: "noctalia" }))
|
||||
},
|
||||
closePanel: function(_pluginId, _screen) { shell.hide(key) },
|
||||
tooltipService: NoctaliaUI.TooltipService,
|
||||
currentScreen: function() {
|
||||
var screens = Quickshell.screens
|
||||
return screens && screens.length > 0 ? screens[0] : null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
var next = ({})
|
||||
for (var existingKey in _noctaliaApis) next[existingKey] = _noctaliaApis[existingKey]
|
||||
next[key] = api
|
||||
_noctaliaApis = next
|
||||
return api
|
||||
}
|
||||
|
||||
// -------------------------------------------------- noctalia service main
|
||||
//
|
||||
// Noctalia plugins with `entryPoints.main` (translated to kind="service")
|
||||
// are instantiated once per shell session when enabled. The Main.qml is
|
||||
// typically headless — a data source the plugin's bar widget reads from.
|
||||
// We rely on the shared pluginApi so the service and any bar widgets see
|
||||
// the same handle.
|
||||
function ensureNoctaliaService(pluginId) {
|
||||
var key = String(pluginId)
|
||||
if (_noctaliaServices[key]) return _noctaliaServices[key]
|
||||
var manifest = pluginRegistry && pluginRegistry.installedPlugins
|
||||
? pluginRegistry.installedPlugins[key] : null
|
||||
if (!manifest || !manifest.__noctaliaCompat) return null
|
||||
if (!manifest.entryPoints || !manifest.entryPoints.service) return null
|
||||
var url = pluginRegistry.entryPointUrl(manifest, "service")
|
||||
if (!url) return null
|
||||
var api = noctaliaPluginApiFor(key)
|
||||
if (!api) return null
|
||||
|
||||
var comp = Qt.createComponent(url, Component.PreferSynchronous)
|
||||
function finalize() {
|
||||
if (comp.status !== Component.Ready) {
|
||||
console.warn("noctalia service load failed for " + key + ": " + comp.errorString())
|
||||
return
|
||||
}
|
||||
var inst = comp.createObject(noctaliaServiceHost, { pluginApi: api })
|
||||
if (!inst) {
|
||||
console.warn("noctalia service createObject returned null for", key)
|
||||
return
|
||||
}
|
||||
var snext = ({})
|
||||
for (var sk in _noctaliaServices) snext[sk] = _noctaliaServices[sk]
|
||||
snext[key] = inst
|
||||
_noctaliaServices = snext
|
||||
api.mainInstance = inst
|
||||
}
|
||||
if (comp.status === Component.Loading) {
|
||||
comp.statusChanged.connect(finalize)
|
||||
return null
|
||||
}
|
||||
finalize()
|
||||
return _noctaliaServices[key] || null
|
||||
}
|
||||
|
||||
function _syncNoctaliaServices() {
|
||||
if (!pluginRegistry || !pluginRegistry.installedPlugins) return
|
||||
var plugins = pluginRegistry.installedPlugins
|
||||
for (var id in plugins) {
|
||||
var m = plugins[id]
|
||||
if (!m || !m.__noctaliaCompat) continue
|
||||
if (!m.entryPoints || !m.entryPoints.service) continue
|
||||
if (!pluginRegistry.isEnabled(id)) continue
|
||||
if (_noctaliaServices[id]) continue
|
||||
ensureNoctaliaService(id)
|
||||
}
|
||||
// Drop services for plugins that have been disabled or removed.
|
||||
for (var existingId in _noctaliaServices) {
|
||||
var stillThere = plugins[existingId]
|
||||
var stillEnabled = stillThere && pluginRegistry.isEnabled(existingId)
|
||||
if (stillThere && stillEnabled) continue
|
||||
var inst = _noctaliaServices[existingId]
|
||||
if (inst && typeof inst.destroy === "function") inst.destroy()
|
||||
var next = ({})
|
||||
for (var k in _noctaliaServices) if (k !== existingId) next[k] = _noctaliaServices[k]
|
||||
_noctaliaServices = next
|
||||
var apis = ({})
|
||||
for (var ak in _noctaliaApis) apis[ak] = _noctaliaApis[ak]
|
||||
if (apis[existingId]) apis[existingId].mainInstance = null
|
||||
_noctaliaApis = apis
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: shell.pluginRegistry
|
||||
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`.
|
||||
// (background updaters, idle inhibitor, etc.) plug in here.
|
||||
Item {
|
||||
id: firstPartyServiceHost
|
||||
visible: false
|
||||
@@ -382,7 +200,6 @@ ShellRoot {
|
||||
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")
|
||||
@@ -421,7 +238,7 @@ ShellRoot {
|
||||
var plugins = pluginRegistry.installedPlugins
|
||||
for (var id in plugins) {
|
||||
var m = plugins[id]
|
||||
if (!m || !m.__isFirstParty || m.__noctaliaCompat) continue
|
||||
if (!m || !m.__isFirstParty) continue
|
||||
if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue
|
||||
if (!m.entryPoints || !m.entryPoints.service) continue
|
||||
if (!pluginRegistry.isEnabled(id)) continue
|
||||
@@ -446,14 +263,11 @@ ShellRoot {
|
||||
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
|
||||
// merged plugin state. Returns true if anything actually changed.
|
||||
// Compute the proposed new shellConfig in a local clone, and only persist
|
||||
// if anything actually changed. Lets Noctalia plugins call saveSettings()
|
||||
// repeatedly with identical values (common for reactive QML bindings)
|
||||
// without dirtying shell.json or thrashing the file watcher.
|
||||
// Writes inline settings to a bar layout entry or top-level plugin entry in
|
||||
// shell.json. moduleName is the entry id; settings is the merged plugin
|
||||
// state. Returns true if anything actually changed. Compute the proposed
|
||||
// new shellConfig in a local clone, and only persist if anything actually
|
||||
// changed so reactive bindings do not dirty shell.json unnecessarily.
|
||||
function updateEntryInline(moduleName, settings) {
|
||||
var stripped = String(moduleName)
|
||||
var copy = JSON.parse(JSON.stringify(shellConfig || builtinShellConfig))
|
||||
@@ -647,9 +461,7 @@ ShellRoot {
|
||||
readonly property string sourceUrl: shell.pluginRegistry.entryPointUrl(manifest, entryKind)
|
||||
|
||||
property Loader panelLoader: Loader {
|
||||
readonly property bool wrapNoctaliaPanel: !!(panelEntry.manifest && panelEntry.manifest.__noctaliaCompat && panelEntry.entryKind === "panel")
|
||||
source: wrapNoctaliaPanel ? "" : panelEntry.sourceUrl
|
||||
sourceComponent: wrapNoctaliaPanel ? noctaliaPanelWrapperComponent : null
|
||||
source: panelEntry.sourceUrl
|
||||
active: panelEntry.sourceUrl !== "" && (panelEntry.keepLoaded || shell.openPanelIds[panelEntry.pluginId] === true)
|
||||
asynchronous: true
|
||||
onLoaded: {
|
||||
@@ -663,11 +475,6 @@ ShellRoot {
|
||||
// (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.
|
||||
if (panelEntry.manifest && panelEntry.manifest.__noctaliaCompat && "pluginApi" in item)
|
||||
item.pluginApi = shell.noctaliaPluginApiFor(panelEntry.pluginId)
|
||||
shell.registerPanelLoader(panelEntry.pluginId, this)
|
||||
}
|
||||
onStatusChanged: {
|
||||
@@ -683,12 +490,6 @@ ShellRoot {
|
||||
}
|
||||
Component.onDestruction: shell.unregisterPanelLoader(panelEntry.pluginId)
|
||||
}
|
||||
|
||||
property Component noctaliaPanelWrapperComponent: Component {
|
||||
Compat.PanelWrapper {
|
||||
panelSource: panelEntry.sourceUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,8 +499,8 @@ ShellRoot {
|
||||
// Each enabled plugin with kind "bar-widget" gets a Component created from
|
||||
// its manifest entry point and registered under its plain manifest id.
|
||||
// First-party widget ids (calendar, weather, etc.) are short and don't
|
||||
// collide with namespaced plugin ids like noctalia.air-quality, so we don't
|
||||
// need a separate "plugin:" namespace anymore.
|
||||
// collide with namespaced plugin ids, so we don't need a separate
|
||||
// "plugin:" namespace anymore.
|
||||
Connections {
|
||||
target: shell.pluginRegistry
|
||||
function onPluginsChanged() { shell.syncPluginWidgets() }
|
||||
|
||||
Reference in New Issue
Block a user