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:
68
CLAUDE.md
68
CLAUDE.md
@@ -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/
|
||||
|
||||
19
DMSShell.qml
19
DMSShell.qml
@@ -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 {
|
||||
|
||||
@@ -19,7 +19,7 @@ DankPopout {
|
||||
}
|
||||
|
||||
popupWidth: contentWidth
|
||||
popupHeight: popoutContent.item ? popoutContent.item.implicitHeight : contentHeight
|
||||
popupHeight: contentHeight
|
||||
screen: triggerScreen
|
||||
shouldBeVisible: false
|
||||
visible: shouldBeVisible
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
65
PLUGINS/WallpaperWatcherDaemon/WallpaperWatcherDaemon.qml
Normal file
65
PLUGINS/WallpaperWatcherDaemon/WallpaperWatcherDaemon.qml
Normal 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")
|
||||
}
|
||||
}
|
||||
24
PLUGINS/WallpaperWatcherDaemon/WallpaperWatcherSettings.qml
Normal file
24
PLUGINS/WallpaperWatcherDaemon/WallpaperWatcherSettings.qml
Normal 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: ""
|
||||
}
|
||||
}
|
||||
15
PLUGINS/WallpaperWatcherDaemon/plugin.json
Normal file
15
PLUGINS/WallpaperWatcherDaemon/plugin.json
Normal 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"
|
||||
]
|
||||
}
|
||||
@@ -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,12 +180,19 @@ 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()
|
||||
}
|
||||
delete pluginWidgetComponents[pluginId]
|
||||
if (isDaemon) {
|
||||
delete pluginDaemonComponents[pluginId]
|
||||
} else {
|
||||
delete pluginWidgetComponents[pluginId]
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -207,9 +216,15 @@ Singleton {
|
||||
return false
|
||||
}
|
||||
|
||||
var newComponents = Object.assign({}, pluginWidgetComponents)
|
||||
newComponents[pluginId] = component
|
||||
pluginWidgetComponents = newComponents
|
||||
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
|
||||
}
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user