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
|
### 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**:
|
1. **Create plugin directory**:
|
||||||
```bash
|
```bash
|
||||||
@@ -464,6 +470,7 @@ Plugins are external, dynamically-loaded components that extend DankBar function
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"author": "Your Name",
|
"author": "Your Name",
|
||||||
"icon": "extension",
|
"icon": "extension",
|
||||||
|
"type": "widget",
|
||||||
"component": "./YourWidget.qml",
|
"component": "./YourWidget.qml",
|
||||||
"settings": "./YourSettings.qml",
|
"settings": "./YourSettings.qml",
|
||||||
"permissions": ["settings_read", "settings_write"]
|
"permissions": ["settings_read", "settings_write"]
|
||||||
@@ -545,6 +552,65 @@ Plugins are external, dynamically-loaded components that extend DankBar function
|
|||||||
- Toggle plugin to enable
|
- Toggle plugin to enable
|
||||||
- Add plugin ID to DankBar widget list
|
- 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:**
|
**Plugin Directory Structure:**
|
||||||
```
|
```
|
||||||
~/.config/DankMaterialShell/
|
~/.config/DankMaterialShell/
|
||||||
|
|||||||
19
DMSShell.qml
19
DMSShell.qml
@@ -42,6 +42,25 @@ ShellRoot {
|
|||||||
PluginService.pluginDirectory
|
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 {}
|
WallpaperBackground {}
|
||||||
|
|
||||||
Lock {
|
Lock {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ DankPopout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
popupWidth: contentWidth
|
popupWidth: contentWidth
|
||||||
popupHeight: popoutContent.item ? popoutContent.item.implicitHeight : contentHeight
|
popupHeight: contentHeight
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
visible: shouldBeVisible
|
visible: shouldBeVisible
|
||||||
|
|||||||
@@ -15,14 +15,25 @@ Column {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Component.onCompleted: {
|
function loadValue() {
|
||||||
const settings = findSettings()
|
const settings = findSettings()
|
||||||
if (settings) {
|
if (settings && settings.pluginService) {
|
||||||
value = settings.loadValue(settingKey, defaultValue)
|
value = settings.loadValue(settingKey, defaultValue)
|
||||||
textField.text = value
|
textField.text = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
loadValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
const settings = findSettings()
|
||||||
|
if (settings) {
|
||||||
|
settings.saveValue(settingKey, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function findSettings() {
|
function findSettings() {
|
||||||
let item = parent
|
let item = parent
|
||||||
while (item) {
|
while (item) {
|
||||||
@@ -54,11 +65,15 @@ Column {
|
|||||||
id: textField
|
id: textField
|
||||||
width: parent.width
|
width: parent.width
|
||||||
placeholderText: root.placeholder
|
placeholderText: root.placeholder
|
||||||
|
onTextEdited: {
|
||||||
|
root.value = text
|
||||||
|
}
|
||||||
onEditingFinished: {
|
onEditingFinished: {
|
||||||
root.value = text
|
root.value = text
|
||||||
const settings = findSettings()
|
}
|
||||||
if (settings) {
|
onActiveFocusChanged: {
|
||||||
settings.saveValue(settingKey, text)
|
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 availablePlugins: ({})
|
||||||
property var loadedPlugins: ({})
|
property var loadedPlugins: ({})
|
||||||
property var pluginWidgetComponents: ({})
|
property var pluginWidgetComponents: ({})
|
||||||
|
property var pluginDaemonComponents: ({})
|
||||||
property string pluginDirectory: {
|
property string pluginDirectory: {
|
||||||
var configDir = StandardPaths.writableLocation(StandardPaths.ConfigLocation)
|
var configDir = StandardPaths.writableLocation(StandardPaths.ConfigLocation)
|
||||||
var configDirStr = configDir.toString()
|
var configDirStr = configDir.toString()
|
||||||
@@ -153,6 +154,7 @@ Singleton {
|
|||||||
pluginInfo.componentPath = pluginDir + '/' + componentFile
|
pluginInfo.componentPath = pluginDir + '/' + componentFile
|
||||||
pluginInfo.settingsPath = settingsFile ? pluginDir + '/' + settingsFile : null
|
pluginInfo.settingsPath = settingsFile ? pluginDir + '/' + settingsFile : null
|
||||||
pluginInfo.loaded = false
|
pluginInfo.loaded = false
|
||||||
|
pluginInfo.type = manifest.type || "widget"
|
||||||
|
|
||||||
availablePlugins[manifest.id] = pluginInfo
|
availablePlugins[manifest.id] = pluginInfo
|
||||||
}
|
}
|
||||||
@@ -178,12 +180,19 @@ Singleton {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginWidgetComponents[pluginId]) {
|
var isDaemon = plugin.type === "daemon"
|
||||||
var oldComponent = pluginWidgetComponents[pluginId]
|
var componentMap = isDaemon ? pluginDaemonComponents : pluginWidgetComponents
|
||||||
|
|
||||||
|
if (componentMap[pluginId]) {
|
||||||
|
var oldComponent = componentMap[pluginId]
|
||||||
if (oldComponent) {
|
if (oldComponent) {
|
||||||
oldComponent.destroy()
|
oldComponent.destroy()
|
||||||
}
|
}
|
||||||
delete pluginWidgetComponents[pluginId]
|
if (isDaemon) {
|
||||||
|
delete pluginDaemonComponents[pluginId]
|
||||||
|
} else {
|
||||||
|
delete pluginWidgetComponents[pluginId]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -207,9 +216,15 @@ Singleton {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var newComponents = Object.assign({}, pluginWidgetComponents)
|
if (isDaemon) {
|
||||||
newComponents[pluginId] = component
|
var newDaemons = Object.assign({}, pluginDaemonComponents)
|
||||||
pluginWidgetComponents = newComponents
|
newDaemons[pluginId] = component
|
||||||
|
pluginDaemonComponents = newDaemons
|
||||||
|
} else {
|
||||||
|
var newComponents = Object.assign({}, pluginWidgetComponents)
|
||||||
|
newComponents[pluginId] = component
|
||||||
|
pluginWidgetComponents = newComponents
|
||||||
|
}
|
||||||
|
|
||||||
plugin.loaded = true
|
plugin.loaded = true
|
||||||
loadedPlugins[pluginId] = plugin
|
loadedPlugins[pluginId] = plugin
|
||||||
@@ -232,15 +247,25 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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]
|
var component = pluginWidgetComponents[pluginId]
|
||||||
if (component) {
|
if (component) {
|
||||||
component.destroy()
|
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
|
plugin.loaded = false
|
||||||
delete loadedPlugins[pluginId]
|
delete loadedPlugins[pluginId]
|
||||||
@@ -258,6 +283,10 @@ Singleton {
|
|||||||
return pluginWidgetComponents
|
return pluginWidgetComponents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getDaemonComponents() {
|
||||||
|
return pluginDaemonComponents
|
||||||
|
}
|
||||||
|
|
||||||
function getAvailablePlugins() {
|
function getAvailablePlugins() {
|
||||||
var result = []
|
var result = []
|
||||||
for (var key in availablePlugins) {
|
for (var key in availablePlugins) {
|
||||||
|
|||||||
Reference in New Issue
Block a user