diff options
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml | 876 |
1 files changed, 876 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml new file mode 100644 index 0000000..03f9be6 --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Services/PluginService.qml @@ -0,0 +1,876 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import QtCore +import QtQuick +import Qt.labs.folderlistmodel +import Quickshell +import qs.Common +import qs.Services + +Singleton { + id: root + + property var availablePlugins: ({}) + property var loadedPlugins: ({}) + property var pluginWidgetComponents: ({}) + property var pluginDaemonComponents: ({}) + property var pluginLauncherComponents: ({}) + property var pluginDesktopComponents: ({}) + property var availablePluginsList: [] + property string pluginDirectory: { + var configDir = StandardPaths.writableLocation(StandardPaths.ConfigLocation); + var configDirStr = configDir.toString(); + if (configDirStr.startsWith("file://")) { + configDirStr = configDirStr.substring(7); + } + return configDirStr + "/DankMaterialShell/plugins"; + } + property string systemPluginDirectory: "/etc/xdg/quickshell/dms-plugins" + + property var knownManifests: ({}) + property var pathToPluginId: ({}) + property var pluginInstances: ({}) + property var globalVars: ({}) + + property var _stateCache: ({}) + property var _stateLoaded: ({}) + property var _stateWriters: ({}) + property var _stateDirtyPlugins: ({}) + property bool _stateDirCreated: false + + signal pluginLoaded(string pluginId) + signal pluginUnloaded(string pluginId) + signal pluginLoadFailed(string pluginId, string error) + signal pluginDataChanged(string pluginId) + signal pluginStateChanged(string pluginId) + signal pluginListUpdated + signal globalVarChanged(string pluginId, string varName) + signal requestLauncherUpdate(string pluginId) + + Timer { + id: resyncDebounce + interval: 120 + repeat: false + onTriggered: resyncAll() + } + + Timer { + id: _stateWriteTimer + interval: 150 + repeat: false + onTriggered: root._flushDirtyStates() + } + + Component.onCompleted: { + userWatcher.folder = Paths.toFileUrl(root.pluginDirectory); + systemWatcher.folder = Paths.toFileUrl(root.systemPluginDirectory); + Qt.callLater(resyncAll); + } + + FolderListModel { + id: userWatcher + showDirs: true + showFiles: false + showDotAndDotDot: false + + onCountChanged: resyncDebounce.restart() + onStatusChanged: { + if (status === FolderListModel.Ready) + resyncDebounce.restart(); + } + } + + FolderListModel { + id: systemWatcher + showDirs: true + showFiles: false + showDotAndDotDot: false + + onCountChanged: resyncDebounce.restart() + onStatusChanged: { + if (status === FolderListModel.Ready) + resyncDebounce.restart(); + } + } + + function snapshotModel(model, sourceTag) { + const out = []; + const n = model.count; + const baseDir = sourceTag === "user" ? pluginDirectory : systemPluginDirectory; + for (let i = 0; i < n; i++) { + let dirPath = model.get(i, "filePath"); + if (dirPath.startsWith("file://")) { + dirPath = dirPath.substring(7); + } + if (!dirPath.startsWith(baseDir)) { + continue; + } + const manifestPath = dirPath + "/plugin.json"; + out.push({ + path: manifestPath, + source: sourceTag + }); + } + return out; + } + + function resyncAll() { + const userList = snapshotModel(userWatcher, "user"); + const sysList = snapshotModel(systemWatcher, "system"); + const seenPaths = {}; + + function consider(entry) { + const key = entry.path; + seenPaths[key] = true; + const prev = knownManifests[key]; + if (!prev) { + loadPluginManifestFile(entry.path, entry.source, Date.now()); + } + } + for (let i = 0; i < userList.length; i++) + consider(userList[i]); + for (let i = 0; i < sysList.length; i++) + consider(sysList[i]); + + const removed = []; + for (const path in knownManifests) { + if (!seenPaths[path]) + removed.push(path); + } + if (removed.length) { + removed.forEach(function (path) { + const pid = pathToPluginId[path]; + if (pid) { + unregisterPluginByPath(path, pid); + } + delete knownManifests[path]; + delete pathToPluginId[path]; + }); + _updateAvailablePluginsList(); + pluginListUpdated(); + } + } + + function loadPluginManifestFile(manifestPathNoScheme, sourceTag, mtimeEpochMs) { + const manifestId = "m_" + Math.random().toString(36).slice(2); + const qml = ` + import QtQuick + import Quickshell.Io + FileView { + id: fv + property string absPath: "" + onLoaded: { + try { + let raw = text() + if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1) + const manifest = JSON.parse(raw) + root._onManifestParsed(absPath, manifest, "${sourceTag}", ${mtimeEpochMs}) + } catch (e) { + console.error("PluginService: bad manifest", absPath, e.message) + knownManifests[absPath] = { mtime: ${mtimeEpochMs}, source: "${sourceTag}", bad: true } + } + fv.destroy() + } + onLoadFailed: (err) => { + console.warn("PluginService: manifest load failed", absPath, err) + fv.destroy() + } + } + `; + + const loader = Qt.createQmlObject(qml, root, "mf_" + manifestId); + loader.absPath = manifestPathNoScheme; + loader.path = manifestPathNoScheme; + } + + function _onManifestParsed(absPath, manifest, sourceTag, mtimeEpochMs) { + if (!manifest || !manifest.id || !manifest.name || !manifest.component) { + console.error("PluginService: invalid manifest fields:", absPath); + knownManifests[absPath] = { + mtime: mtimeEpochMs, + source: sourceTag, + bad: true + }; + return; + } + + const dir = absPath.substring(0, absPath.lastIndexOf('/')); + let comp = manifest.component; + if (comp.startsWith("./")) + comp = comp.slice(2); + let settings = manifest.settings; + if (settings && settings.startsWith("./")) + settings = settings.slice(2); + + const info = {}; + for (const k in manifest) + info[k] = manifest[k]; + + let perms = manifest.permissions; + if (typeof perms === "string") { + perms = perms.split(/\s*,\s*/); + } + if (!Array.isArray(perms)) { + perms = []; + } + info.permissions = perms.map(p => String(p).trim()); + + info.manifestPath = absPath; + info.pluginDirectory = dir; + info.componentPath = dir + "/" + comp; + info.settingsPath = settings ? (dir + "/" + settings) : null; + info.loaded = isPluginLoaded(manifest.id); + info.type = manifest.type || "widget"; + info.source = sourceTag; + info.requires_dms = manifest.requires_dms || null; + + const existing = availablePlugins[manifest.id]; + const shouldReplace = (!existing) || (existing && existing.source === "system" && sourceTag === "user"); + + if (shouldReplace) { + if (existing && existing.loaded && existing.source !== sourceTag) { + unloadPlugin(manifest.id); + } + const newMap = Object.assign({}, availablePlugins); + newMap[manifest.id] = info; + availablePlugins = newMap; + pathToPluginId[absPath] = manifest.id; + knownManifests[absPath] = { + mtime: mtimeEpochMs, + source: sourceTag + }; + _updateAvailablePluginsList(); + pluginListUpdated(); + const enabled = info.type === "desktop" || SettingsData.getPluginSetting(manifest.id, "enabled", false); + if (enabled && !info.loaded) + loadPlugin(manifest.id); + } else { + knownManifests[absPath] = { + mtime: mtimeEpochMs, + source: sourceTag, + shadowedBy: existing.source + }; + pathToPluginId[absPath] = manifest.id; + } + } + + function unregisterPluginByPath(absPath, pluginId) { + const current = availablePlugins[pluginId]; + if (current && current.manifestPath === absPath) { + if (current.loaded) + unloadPlugin(pluginId); + const newMap = Object.assign({}, availablePlugins); + delete newMap[pluginId]; + availablePlugins = newMap; + } + } + + function loadPlugin(pluginId, bustCache) { + const plugin = availablePlugins[pluginId]; + if (!plugin) { + console.error("PluginService: Plugin not found:", pluginId); + pluginLoadFailed(pluginId, "Plugin not found"); + return false; + } + + if (plugin.loaded) { + return true; + } + + const isDaemon = plugin.type === "daemon"; + const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher")); + const isDesktop = plugin.type === "desktop"; + + const prevInstance = pluginInstances[pluginId]; + if (prevInstance) { + prevInstance.destroy(); + const newInstances = Object.assign({}, pluginInstances); + delete newInstances[pluginId]; + pluginInstances = newInstances; + } + + try { + let url = "file://" + plugin.componentPath; + if (bustCache) + url += "?t=" + Date.now(); + const comp = Qt.createComponent(url, Component.PreferSynchronous); + if (comp.status === Component.Error) { + console.error("PluginService: component error", pluginId, comp.errorString()); + pluginLoadFailed(pluginId, comp.errorString()); + return false; + } + + if (isDaemon) { + const newDaemons = Object.assign({}, pluginDaemonComponents); + newDaemons[pluginId] = comp; + pluginDaemonComponents = newDaemons; + } else if (isLauncher) { + const instance = comp.createObject(root, { + "pluginService": root + }); + if (!instance) { + console.error("PluginService: failed to instantiate plugin:", pluginId, comp.errorString()); + pluginLoadFailed(pluginId, comp.errorString()); + return false; + } + const newInstances = Object.assign({}, pluginInstances); + newInstances[pluginId] = instance; + pluginInstances = newInstances; + + const newLaunchers = Object.assign({}, pluginLauncherComponents); + newLaunchers[pluginId] = comp; + pluginLauncherComponents = newLaunchers; + } else if (isDesktop) { + const newDesktop = Object.assign({}, pluginDesktopComponents); + newDesktop[pluginId] = comp; + pluginDesktopComponents = newDesktop; + } else { + const newComponents = Object.assign({}, pluginWidgetComponents); + newComponents[pluginId] = comp; + pluginWidgetComponents = newComponents; + } + + plugin.loaded = true; + const newLoaded = Object.assign({}, loadedPlugins); + newLoaded[pluginId] = plugin; + loadedPlugins = newLoaded; + + pluginLoaded(pluginId); + return true; + } catch (e) { + console.error("PluginService: Error loading plugin:", pluginId, e.message); + pluginLoadFailed(pluginId, e.message); + return false; + } + } + + function unloadPlugin(pluginId) { + const plugin = loadedPlugins[pluginId]; + if (!plugin) { + console.warn("PluginService: Plugin not loaded:", pluginId); + return false; + } + + try { + const isDaemon = plugin.type === "daemon"; + const isLauncher = plugin.type === "launcher" || (plugin.capabilities && plugin.capabilities.includes("launcher")); + const isDesktop = plugin.type === "desktop"; + + const instance = pluginInstances[pluginId]; + if (instance) { + instance.destroy(); + const newInstances = Object.assign({}, pluginInstances); + delete newInstances[pluginId]; + pluginInstances = newInstances; + } + + if (isDaemon && pluginDaemonComponents[pluginId]) { + const newDaemons = Object.assign({}, pluginDaemonComponents); + delete newDaemons[pluginId]; + pluginDaemonComponents = newDaemons; + } else if (isLauncher && pluginLauncherComponents[pluginId]) { + const newLaunchers = Object.assign({}, pluginLauncherComponents); + delete newLaunchers[pluginId]; + pluginLauncherComponents = newLaunchers; + } else if (isDesktop && pluginDesktopComponents[pluginId]) { + const newDesktop = Object.assign({}, pluginDesktopComponents); + delete newDesktop[pluginId]; + pluginDesktopComponents = newDesktop; + } else if (pluginWidgetComponents[pluginId]) { + const newComponents = Object.assign({}, pluginWidgetComponents); + delete newComponents[pluginId]; + pluginWidgetComponents = newComponents; + } + + plugin.loaded = false; + const newLoaded = Object.assign({}, loadedPlugins); + delete newLoaded[pluginId]; + loadedPlugins = newLoaded; + + _cleanupPluginStateWriter(pluginId); + pluginUnloaded(pluginId); + return true; + } catch (error) { + console.error("PluginService: Error unloading plugin:", pluginId, "Error:", error.message); + return false; + } + } + + function getWidgetComponents() { + return pluginWidgetComponents; + } + + function getDaemonComponents() { + return pluginDaemonComponents; + } + + function getDesktopComponents() { + return pluginDesktopComponents; + } + + function getAvailablePlugins() { + return availablePluginsList; + } + + function _updateAvailablePluginsList() { + const result = []; + for (const key in availablePlugins) { + result.push(availablePlugins[key]); + } + availablePluginsList = result; + } + + function getPluginVariants(pluginId) { + const plugin = availablePlugins[pluginId]; + if (!plugin) { + return []; + } + const variants = SettingsData.getPluginSetting(pluginId, "variants", []); + return variants; + } + + function getAllPluginVariants() { + const result = []; + for (const pluginId in availablePlugins) { + const plugin = availablePlugins[pluginId]; + if (plugin.type !== "widget") { + continue; + } + const variants = getPluginVariants(pluginId); + if (variants.length === 0) { + result.push({ + pluginId: pluginId, + variantId: null, + fullId: pluginId, + name: plugin.name, + icon: plugin.icon || "extension", + description: plugin.description || "Plugin widget", + loaded: plugin.loaded + }); + } else { + for (let i = 0; i < variants.length; i++) { + const variant = variants[i]; + result.push({ + pluginId: pluginId, + variantId: variant.id, + fullId: pluginId + ":" + variant.id, + name: plugin.name + " - " + variant.name, + icon: variant.icon || plugin.icon || "extension", + description: variant.description || plugin.description || "Plugin widget variant", + loaded: plugin.loaded + }); + } + } + } + return result; + } + + function createPluginVariant(pluginId, variantName, variantConfig) { + const variants = getPluginVariants(pluginId); + const variantId = "variant_" + Date.now(); + const newVariant = Object.assign({}, variantConfig, { + id: variantId, + name: variantName + }); + variants.push(newVariant); + SettingsData.setPluginSetting(pluginId, "variants", variants); + pluginDataChanged(pluginId); + return variantId; + } + + function removePluginVariant(pluginId, variantId) { + const variants = getPluginVariants(pluginId); + const newVariants = variants.filter(function (v) { + return v.id !== variantId; + }); + SettingsData.setPluginSetting(pluginId, "variants", newVariants); + + const fullId = pluginId + ":" + variantId; + removeWidgetFromDankBar(fullId); + + pluginDataChanged(pluginId); + } + + function removeWidgetFromDankBar(widgetId) { + function filterWidget(widget) { + const id = typeof widget === "string" ? widget : widget.id; + return id !== widgetId; + } + + const defaultBar = SettingsData.barConfigs[0] || SettingsData.getBarConfig("default"); + if (!defaultBar) + return; + const leftWidgets = defaultBar.leftWidgets || []; + const centerWidgets = defaultBar.centerWidgets || []; + const rightWidgets = defaultBar.rightWidgets || []; + + const newLeft = leftWidgets.filter(filterWidget); + const newCenter = centerWidgets.filter(filterWidget); + const newRight = rightWidgets.filter(filterWidget); + + if (newLeft.length !== leftWidgets.length) { + SettingsData.setDankBarLeftWidgets(newLeft); + } + if (newCenter.length !== centerWidgets.length) { + SettingsData.setDankBarCenterWidgets(newCenter); + } + if (newRight.length !== rightWidgets.length) { + SettingsData.setDankBarRightWidgets(newRight); + } + } + + function updatePluginVariant(pluginId, variantId, variantConfig) { + const variants = getPluginVariants(pluginId); + for (let i = 0; i < variants.length; i++) { + if (variants[i].id === variantId) { + variants[i] = Object.assign({}, variants[i], variantConfig); + break; + } + } + SettingsData.setPluginSetting(pluginId, "variants", variants); + pluginDataChanged(pluginId); + } + + function getPluginVariantData(pluginId, variantId) { + const variants = getPluginVariants(pluginId); + for (let i = 0; i < variants.length; i++) { + if (variants[i].id === variantId) { + return variants[i]; + } + } + return null; + } + + function getLoadedPlugins() { + const result = []; + for (const key in loadedPlugins) { + result.push(loadedPlugins[key]); + } + return result; + } + + function isPluginLoaded(pluginId) { + return loadedPlugins[pluginId] !== undefined; + } + + function enablePlugin(pluginId) { + SettingsData.setPluginSetting(pluginId, "enabled", true); + return loadPlugin(pluginId); + } + + function disablePlugin(pluginId) { + SettingsData.setPluginSetting(pluginId, "enabled", false); + return unloadPlugin(pluginId); + } + + function reloadPlugin(pluginId) { + if (isPluginLoaded(pluginId)) + unloadPlugin(pluginId); + return loadPlugin(pluginId, true); + } + + function togglePlugin(pluginId) { + let instance = pluginInstances[pluginId]; + + // Lazy instantiate daemon plugins on first toggle + // This respects the daemon lifecycle (not instantiated on load) + // while supporting toggle functionality for slideout-capable daemons + if (!instance && pluginDaemonComponents[pluginId]) { + const comp = pluginDaemonComponents[pluginId]; + const newInstance = comp.createObject(root, { + "pluginId": pluginId, + "pluginService": root + }); + if (newInstance) { + const newInstances = Object.assign({}, pluginInstances); + newInstances[pluginId] = newInstance; + pluginInstances = newInstances; + instance = newInstance; + } + } + + if (instance && typeof instance.toggle === "function") { + instance.toggle(); + return true; + } + return false; + } + + function savePluginData(pluginId, key, value) { + SettingsData.setPluginSetting(pluginId, key, value); + pluginDataChanged(pluginId); + return true; + } + + function loadPluginData(pluginId, key, defaultValue) { + return SettingsData.getPluginSetting(pluginId, key, defaultValue); + } + + function getPluginPath(pluginId) { + const plugin = availablePlugins[pluginId]; + if (!plugin) + return ""; + return plugin.pluginDirectory || ""; + } + + function saveAllPluginSettings() { + SettingsData.savePluginSettings(); + } + + function getPluginStatePath(pluginId) { + return Paths.strip(Paths.state) + "/plugins/" + pluginId + "_state.json"; + } + + function loadPluginState(pluginId, key, defaultValue) { + if (!_stateLoaded[pluginId]) + _loadStateFromDisk(pluginId); + const state = _stateCache[pluginId]; + if (!state) + return defaultValue; + return state[key] !== undefined ? state[key] : defaultValue; + } + + function savePluginState(pluginId, key, value) { + if (!_stateLoaded[pluginId]) + _loadStateFromDisk(pluginId); + if (!_stateCache[pluginId]) + _stateCache[pluginId] = {}; + _stateCache[pluginId][key] = value; + _stateDirtyPlugins[pluginId] = true; + _stateWriteTimer.restart(); + pluginStateChanged(pluginId); + } + + function clearPluginState(pluginId) { + _stateCache[pluginId] = {}; + _stateLoaded[pluginId] = true; + _flushStateToDisk(pluginId); + pluginStateChanged(pluginId); + } + + function removePluginStateKey(pluginId, key) { + if (!_stateCache[pluginId]) + return; + delete _stateCache[pluginId][key]; + _stateDirtyPlugins[pluginId] = true; + _stateWriteTimer.restart(); + pluginStateChanged(pluginId); + } + + function _ensureStateDir() { + if (_stateDirCreated) + return; + _stateDirCreated = true; + Paths.mkdir(Paths.state + "/plugins"); + } + + function _loadStateFromDisk(pluginId) { + _stateLoaded[pluginId] = true; + _ensureStateDir(); + const path = getPluginStatePath(pluginId); + const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); + try { + const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockLoading: true; blockWrites: true; atomicWrites: true }'; + const fv = Qt.createQmlObject(qml, root, "sf_" + pluginId); + const raw = fv.text(); + if (raw && raw.trim()) { + _stateCache[pluginId] = JSON.parse(raw); + } else { + _stateCache[pluginId] = {}; + } + _stateWriters[pluginId] = fv; + } catch (e) { + _stateCache[pluginId] = {}; + } + } + + function _flushStateToDisk(pluginId) { + _ensureStateDir(); + const content = JSON.stringify(_stateCache[pluginId] || {}, null, 2); + if (_stateWriters[pluginId]) { + _stateWriters[pluginId].setText(content); + return; + } + const path = getPluginStatePath(pluginId); + const escapedPath = path.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); + try { + const qml = 'import QtQuick; import Quickshell.Io; FileView { path: "' + escapedPath + '"; blockWrites: true; atomicWrites: true }'; + const fv = Qt.createQmlObject(qml, root, "sw_" + pluginId); + _stateWriters[pluginId] = fv; + fv.loaded.connect(function () { + fv.setText(content); + }); + fv.loadFailed.connect(function () { + fv.setText(content); + }); + } catch (e) { + console.warn("PluginService: Failed to write state for", pluginId, e.message); + } + } + + function _flushDirtyStates() { + const dirty = _stateDirtyPlugins; + _stateDirtyPlugins = {}; + for (const pluginId in dirty) + _flushStateToDisk(pluginId); + } + + function _cleanupPluginStateWriter(pluginId) { + if (!_stateWriters[pluginId]) + return; + _stateWriters[pluginId].destroy(); + delete _stateWriters[pluginId]; + } + + function scanPlugins() { + const userUrl = Paths.toFileUrl(root.pluginDirectory); + const systemUrl = Paths.toFileUrl(root.systemPluginDirectory); + userWatcher.folder = ""; + userWatcher.folder = userUrl; + systemWatcher.folder = ""; + systemWatcher.folder = systemUrl; + resyncDebounce.restart(); + } + + function forceRescanPlugin(pluginId) { + const plugin = availablePlugins[pluginId]; + if (plugin && plugin.manifestPath) { + const manifestPath = plugin.manifestPath; + const source = plugin.source || "user"; + delete knownManifests[manifestPath]; + const newMap = Object.assign({}, availablePlugins); + delete newMap[pluginId]; + availablePlugins = newMap; + loadPluginManifestFile(manifestPath, source, Date.now()); + } + } + + function createPluginDirectory() { + const mkdirProcess = Qt.createComponent("data:text/plain,import Quickshell.Io; Process { }"); + if (mkdirProcess.status === Component.Ready) { + const process = mkdirProcess.createObject(root); + process.command = ["mkdir", "-p", pluginDirectory]; + process.exited.connect(function (exitCode) { + if (exitCode !== 0) { + console.error("PluginService: Failed to create plugin directory, exit code:", exitCode); + } + process.destroy(); + }); + process.running = true; + return true; + } else { + console.error("PluginService: Failed to create mkdir process"); + return false; + } + } + + // Launcher plugin helper functions + function getLauncherPlugins() { + const launchers = {}; + + // Check plugins that have launcher components + for (const pluginId in pluginLauncherComponents) { + const plugin = availablePlugins[pluginId]; + if (plugin && plugin.loaded) { + launchers[pluginId] = plugin; + } + } + return launchers; + } + + function getLauncherPlugin(pluginId) { + const plugin = availablePlugins[pluginId]; + if (plugin && plugin.loaded && pluginLauncherComponents[pluginId]) { + return plugin; + } + return null; + } + + function getPluginTrigger(pluginId) { + const plugin = getLauncherPlugin(pluginId); + if (plugin) { + // Check if noTrigger is set (always active mode) + const noTrigger = SettingsData.getPluginSetting(pluginId, "noTrigger", false); + if (noTrigger) { + return ""; + } + // Otherwise load the custom trigger, defaulting to plugin manifest trigger + const customTrigger = SettingsData.getPluginSetting(pluginId, "trigger", plugin.trigger || "!"); + return customTrigger; + } + return null; + } + + function getAllPluginTriggers() { + const triggers = {}; + const launchers = getLauncherPlugins(); + + for (const pluginId in launchers) { + const trigger = getPluginTrigger(pluginId); + if (trigger && trigger.trim() !== "") { + triggers[trigger] = pluginId; + } + } + return triggers; + } + + function getPluginsWithEmptyTrigger() { + const plugins = []; + const launchers = getLauncherPlugins(); + + for (const pluginId in launchers) { + const trigger = getPluginTrigger(pluginId); + if (!trigger || trigger.trim() === "") { + plugins.push(pluginId); + } + } + return plugins; + } + + function getPluginViewPreference(pluginId) { + const plugin = availablePlugins[pluginId]; + if (!plugin) + return null; + + return { + mode: plugin.viewMode || null, + enforced: plugin.viewModeEnforced === true + }; + } + + function getGlobalVar(pluginId, varName, defaultValue) { + if (globalVars[pluginId] && varName in globalVars[pluginId]) { + return globalVars[pluginId][varName]; + } + return defaultValue; + } + + function setGlobalVar(pluginId, varName, value) { + const newGlobals = Object.assign({}, globalVars); + if (!newGlobals[pluginId]) { + newGlobals[pluginId] = {}; + } + newGlobals[pluginId] = Object.assign({}, newGlobals[pluginId]); + newGlobals[pluginId][varName] = value; + globalVars = newGlobals; + globalVarChanged(pluginId, varName); + } + + function checkPluginCompatibility(requiresDms) { + if (!requiresDms) + return true; + return SystemUpdateService.checkVersionRequirement(requiresDms, SystemUpdateService.getParsedShellVersion()); + } + + function getIncompatiblePlugins() { + const result = []; + for (const pluginId in availablePlugins) { + const plugin = availablePlugins[pluginId]; + if (plugin.loaded && plugin.requires_dms && !checkPluginCompatibility(plugin.requires_dms)) { + result.push(plugin); + } + } + return result; + } +} |