1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

Add "daemon" type of plugins

This commit is contained in:
bbedward
2025-10-03 22:55:07 -04:00
parent c3d505cdad
commit 0d5c1bb3df
8 changed files with 250 additions and 17 deletions

View File

@@ -448,7 +448,13 @@ When modifying the shell:
### Creating Plugins
Plugins are external, dynamically-loaded components that extend DankBar functionality. Plugins are stored in `~/.config/DankMaterialShell/plugins/` and have their settings isolated from core DMS settings.
Plugins are external, dynamically-loaded components that extend DankMaterialShell functionality. Plugins are stored in `~/.config/DankMaterialShell/plugins/` and have their settings isolated from core DMS settings.
**Plugin Types:**
- **Widget plugins** (`"type": "widget"` or omit type field): Display UI components in DankBar
- **Daemon plugins** (`"type": "daemon"`): Run invisibly in the background without UI
#### Widget Plugins
1. **Create plugin directory**:
```bash
@@ -464,6 +470,7 @@ Plugins are external, dynamically-loaded components that extend DankBar function
"version": "1.0.0",
"author": "Your Name",
"icon": "extension",
"type": "widget",
"component": "./YourWidget.qml",
"settings": "./YourSettings.qml",
"permissions": ["settings_read", "settings_write"]
@@ -545,6 +552,65 @@ Plugins are external, dynamically-loaded components that extend DankBar function
- Toggle plugin to enable
- Add plugin ID to DankBar widget list
#### Daemon Plugins
Daemon plugins run invisibly in the background without any UI components. They're useful for monitoring system events, background tasks, or data synchronization.
1. **Create plugin directory**:
```bash
mkdir -p ~/.config/DankMaterialShell/plugins/YourDaemon
```
2. **Create manifest** (`plugin.json`):
```json
{
"id": "yourDaemon",
"name": "Your Daemon",
"description": "Background daemon description",
"version": "1.0.0",
"author": "Your Name",
"icon": "settings_applications",
"type": "daemon",
"component": "./YourDaemon.qml",
"permissions": ["settings_read", "settings_write"]
}
```
3. **Create daemon component** (`YourDaemon.qml`):
```qml
import QtQuick
import qs.Common
import qs.Services
Item {
id: root
property var pluginService: null
Connections {
target: SessionData
function onWallpaperPathChanged() {
console.log("Wallpaper changed:", SessionData.wallpaperPath)
if (pluginService) {
pluginService.savePluginData("yourDaemon", "lastEvent", Date.now())
}
}
}
Component.onCompleted: {
console.log("Daemon started")
}
}
```
4. **Enable daemon**:
- Open Settings → Plugins
- Click "Scan for Plugins"
- Toggle daemon to enable
- Daemon runs automatically in background
**Example**: See `PLUGINS/WallpaperWatcherDaemon/` for a complete daemon plugin that monitors wallpaper changes
**Plugin Directory Structure:**
```
~/.config/DankMaterialShell/

View File

@@ -42,6 +42,25 @@ ShellRoot {
PluginService.pluginDirectory
}
Instantiator {
id: daemonPluginInstantiator
model: Object.keys(PluginService.pluginDaemonComponents)
delegate: Loader {
id: daemonLoader
property string pluginId: modelData
sourceComponent: PluginService.pluginDaemonComponents[pluginId]
onLoaded: {
if (item) {
item.pluginService = PluginService
item.pluginId = pluginId
console.log("Daemon plugin loaded:", pluginId)
}
}
}
}
WallpaperBackground {}
Lock {

View File

@@ -19,7 +19,7 @@ DankPopout {
}
popupWidth: contentWidth
popupHeight: popoutContent.item ? popoutContent.item.implicitHeight : contentHeight
popupHeight: contentHeight
screen: triggerScreen
shouldBeVisible: false
visible: shouldBeVisible

View File

@@ -15,14 +15,25 @@ Column {
width: parent.width
spacing: Theme.spacingS
Component.onCompleted: {
function loadValue() {
const settings = findSettings()
if (settings) {
if (settings && settings.pluginService) {
value = settings.loadValue(settingKey, defaultValue)
textField.text = value
}
}
Component.onCompleted: {
loadValue()
}
onValueChanged: {
const settings = findSettings()
if (settings) {
settings.saveValue(settingKey, value)
}
}
function findSettings() {
let item = parent
while (item) {
@@ -54,11 +65,15 @@ Column {
id: textField
width: parent.width
placeholderText: root.placeholder
onTextEdited: {
root.value = text
}
onEditingFinished: {
root.value = text
const settings = findSettings()
if (settings) {
settings.saveValue(settingKey, text)
}
onActiveFocusChanged: {
if (!activeFocus) {
root.value = text
}
}
}

View File

@@ -0,0 +1,65 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Modules.Plugins
PluginComponent {
id: root
property string scriptPath: pluginData.scriptPath || ""
Connections {
target: SessionData
function onWallpaperPathChanged() {
if (scriptPath) {
var scriptProcess = scriptProcessComponent.createObject(root, {
wallpaperPath: SessionData.wallpaperPath
})
scriptProcess.running = true
}
}
}
Component {
id: scriptProcessComponent
Process {
property string wallpaperPath: ""
command: [scriptPath, wallpaperPath]
stdout: StdioCollector {
onStreamFinished: {
if (text.trim()) {
console.log("WallpaperWatcherDaemon script output:", text.trim())
}
}
}
stderr: StdioCollector {
onStreamFinished: {
if (text.trim()) {
ToastService.showError("Wallpaper Change Script Error", text.trim())
}
}
}
onExited: (exitCode) => {
if (exitCode !== 0) {
ToastService.showError("Wallpaper Change Script Error", "Script exited with code: " + exitCode)
}
destroy()
}
}
}
Component.onCompleted: {
console.log("WallpaperWatcherDaemon: Started monitoring wallpaper changes")
}
Component.onDestruction: {
console.log("WallpaperWatcherDaemon: Stopped monitoring wallpaper changes")
}
}

View File

@@ -0,0 +1,24 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "wallpaperWatcherDaemon"
StyledText {
text: "Wallpaper Change Hook"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
}
StringSetting {
settingKey: "scriptPath"
label: "Script Path"
description: "Path to a script that will be executed when the wallpaper changes. The new wallpaper path will be passed as the first argument."
placeholder: "/path/to/your/script.sh"
defaultValue: ""
}
}

View File

@@ -0,0 +1,15 @@
{
"id": "wallpaperWatcherDaemon",
"name": "Wallpaper Watcher Daemon",
"description": "Background daemon that monitors wallpaper changes and logs them",
"version": "1.0.0",
"author": "DankMaterialShell",
"icon": "wallpaper",
"type": "daemon",
"component": "./WallpaperWatcherDaemon.qml",
"settings": "./WallpaperWatcherSettings.qml",
"permissions": [
"settings_read",
"settings_write"
]
}

View File

@@ -14,6 +14,7 @@ Singleton {
property var availablePlugins: ({})
property var loadedPlugins: ({})
property var pluginWidgetComponents: ({})
property var pluginDaemonComponents: ({})
property string pluginDirectory: {
var configDir = StandardPaths.writableLocation(StandardPaths.ConfigLocation)
var configDirStr = configDir.toString()
@@ -153,6 +154,7 @@ Singleton {
pluginInfo.componentPath = pluginDir + '/' + componentFile
pluginInfo.settingsPath = settingsFile ? pluginDir + '/' + settingsFile : null
pluginInfo.loaded = false
pluginInfo.type = manifest.type || "widget"
availablePlugins[manifest.id] = pluginInfo
}
@@ -178,13 +180,20 @@ Singleton {
return true
}
if (pluginWidgetComponents[pluginId]) {
var oldComponent = pluginWidgetComponents[pluginId]
var isDaemon = plugin.type === "daemon"
var componentMap = isDaemon ? pluginDaemonComponents : pluginWidgetComponents
if (componentMap[pluginId]) {
var oldComponent = componentMap[pluginId]
if (oldComponent) {
oldComponent.destroy()
}
if (isDaemon) {
delete pluginDaemonComponents[pluginId]
} else {
delete pluginWidgetComponents[pluginId]
}
}
try {
var componentUrl = "file://" + plugin.componentPath
@@ -207,9 +216,15 @@ Singleton {
return false
}
if (isDaemon) {
var newDaemons = Object.assign({}, pluginDaemonComponents)
newDaemons[pluginId] = component
pluginDaemonComponents = newDaemons
} else {
var newComponents = Object.assign({}, pluginWidgetComponents)
newComponents[pluginId] = component
pluginWidgetComponents = newComponents
}
plugin.loaded = true
loadedPlugins[pluginId] = plugin
@@ -232,15 +247,25 @@ Singleton {
}
try {
if (pluginWidgetComponents[pluginId]) {
var isDaemon = plugin.type === "daemon"
if (isDaemon && pluginDaemonComponents[pluginId]) {
var daemonComponent = pluginDaemonComponents[pluginId]
if (daemonComponent) {
daemonComponent.destroy()
}
var newDaemons = Object.assign({}, pluginDaemonComponents)
delete newDaemons[pluginId]
pluginDaemonComponents = newDaemons
} else if (pluginWidgetComponents[pluginId]) {
var component = pluginWidgetComponents[pluginId]
if (component) {
component.destroy()
}
}
var newComponents = Object.assign({}, pluginWidgetComponents)
delete newComponents[pluginId]
pluginWidgetComponents = newComponents
}
plugin.loaded = false
delete loadedPlugins[pluginId]
@@ -258,6 +283,10 @@ Singleton {
return pluginWidgetComponents
}
function getDaemonComponents() {
return pluginDaemonComponents
}
function getAvailablePlugins() {
var result = []
for (var key in availablePlugins) {