feat: add dms-plugin-dev agent skill for Dank Material Shell plugin development

- Introduces a general-purpose opencode skill to help agents build, debug, and publish DMS plugins.
- Includes orientation, decision trees (plugin types), condensed cheat sheets, ecosystem map, and four generic vertical-slice starter templates (bar widget, popout widget, launcher, desktop widget).
- Skill is versioned here for this project while remaining available as a global opencode skill.
- Updated .gitignore to ignore globally-installed copies of the skill.

Written by AI agent working for @jtmorris. Model: grok-build-0.1.
This commit is contained in:
Vybe (Coding Agent) 2026-05-24 09:31:06 +00:00
parent bc864bb9d8
commit 01ac7e9041
21 changed files with 873 additions and 0 deletions

6
.gitignore vendored
View file

@ -1,2 +1,8 @@
# Ignore any directory named `.john` anywhere in the repository # Ignore any directory named `.john` anywhere in the repository
.john/ .john/
# dms-plugin-dev skill
# The canonical version is versioned inside this repository at skills/dms-plugin-dev/
# Ignore the globally-installed copy (in ~/.config/opencode/skills/) to prevent
# accidental commits of user-local installations.
**/.config/opencode/skills/dms-plugin-dev/

View file

@ -0,0 +1,43 @@
# dms-plugin-dev
A focused skill for agents that builds or maintains plugins for **Dank Material Shell (DMS)**.
## What It Provides
- Strong orientation and mental model for the DMS plugin system
- Decision trees for choosing the right plugin type
- Condensed cheat sheets for the highest-uncertainty surfaces (`plugin.json`, `PluginComponent`, `PluginService`, settings, popouts, permissions, launcher shape, etc.)
- Curated ecosystem map (official docs, first-party examples, monorepo examples, registry)
- Generic, modern vertical-slice starter templates (widget, popout widget, launcher, desktop)
## Philosophy
This skill is deliberately **thin and pointer-heavy**. The official DMS documentation (especially the Plugin Development guide) is excellent and comprehensive. This skill exists to reduce the "I don't know what I don't know" problem for agents and to give them good starting points (templates) that follow current conventions.
It does **not** duplicate large sections of the official docs.
## Location in the Skill
After the skill is loaded, you will have access to:
- `SKILL.md` (entry point)
- `docs/plugin-types.md`
- `docs/cheatsheets.md`
- `docs/ecosystem.md`
- `templates/` (copy these folders wholesale when starting a new plugin)
## When to Load This Skill
Any time the user says:
- "build a DMS plugin"
- "add a widget / launcher / desktop thing to Dank Material Shell"
- "debug this plugin.json / PluginComponent"
- "publish this to the DMS plugin registry"
- or mentions any of the core DMS plugin concepts
## Maintenance
Low-maintenance by design. When the official docs or best practices evolve significantly, update the pointers and the templates. Do not try to be a second source of truth.
---
Written by AI agent working for @jtmorris. Model: grok-build-0.1.

View file

@ -0,0 +1,103 @@
---
name: dms-plugin-dev
description: Guidance, decision trees, cheat sheets, ecosystem map, and generic vertical-slice starter templates for building plugins for Dank Material Shell (DMS / DankMaterialShell) using Quickshell QML. Use when the user asks to create, extend, debug, review, or publish a DMS plugin, or mentions plugin.json, PluginComponent, DankBar widgets, launcher plugins, desktop widgets, or the DMS plugin registry.
license: MIT
compatibility: opencode
---
# DMS Plugin Development
## Mental Model (Read This First)
Dank Material Shell plugins are **small, self-contained directories** dropped into `~/.config/DankMaterialShell/plugins/<YourPluginId>/`.
Each plugin declares what it is in `plugin.json` (type, capabilities, entry component, permissions, dependencies).
The shell discovers them, loads the QML components into its running context, and injects services (`PluginService`, `Theme`, `ToastService`, etc.).
Plugins do **not** run in isolation like a normal QML app. They participate in the larger DMS object tree.
There are four primary plugin types (see `docs/plugin-types.md` for the decision tree):
- **widget** — Appears in the DankBar or Control Center (most common).
- **launcher** — Extends the Spotlight-style launcher with searchable items (root is a `QtObject`, not an `Item`).
- **daemon** — Background logic, no UI (react to events, run automation).
- **desktop** — Free-floating, user-positionable and resizable widgets on the desktop layer (uses `DesktopPluginComponent`).
## Development Loop (Fastest Feedback)
1. Create your plugin directory anywhere (e.g. `~/src/my-dms-plugin`).
2. Symlink it: `ln -s ~/src/my-dms-plugin ~/.config/DankMaterialShell/plugins/MyPlugin`.
3. Work in the source tree.
4. After changes: `dms ipc call plugins reload myPlugin` (uses the `id` from plugin.json).
5. For popout widgets, set `layerNamespacePlugin: "something-unique"` in your `PluginComponent`.
6. Set up qmlls for autocomplete (see official docs; create `.qmlls.ini` in a DMS checkout).
7. Use the `Proc` singleton (from `qs.Common`) for any external command whose output you need to capture. It handles debouncing and auto-cleanup.
Never restart the whole shell while iterating on a plugin unless absolutely necessary.
## Must-Read Official Sources
This skill is deliberately a **thin orientation + index + templates layer**. The canonical, detailed, and up-to-date reference is the official documentation:
- Plugin Overview: https://danklinux.com/docs/dankmaterialshell/plugins-overview
- Plugin Development (the big one): https://danklinux.com/docs/dankmaterialshell/plugin-development
- Plugin schema (validation): Look for `plugin-schema.json` in the DMS repo or registry.
**Always** check the version notes at the top of those pages and match them to the DMS version the user is running.
## High-Signal Cheat Sheets in This Skill
- `docs/plugin-types.md` — Decision tree for choosing the right plugin shape.
- `docs/cheatsheets.md` — Condensed surfaces for `plugin.json`, `PluginComponent`, `PluginService`, settings components, popouts, and permissions.
- `docs/ecosystem.md` — Where to find real examples (first-party plugins, monorepo PLUGINS/, registry, community).
## Generic Vertical-Slice Templates
Located in the `templates/` directory of this skill. Each is a minimal, correct, modern starter for one plugin type.
**How to use them**:
1. Copy the entire template folder to `~/.config/DankMaterialShell/plugins/YourPluginName/`.
2. Rename files and update `plugin.json` (especially `id`, `name`, `author`, paths).
3. Run `dms ipc call plugins reload <id>` (or restart).
4. Enable in DMS Settings → Plugins.
Current templates:
- `widget-bar/` — Simple DankBar widget with settings.
- `widget-popout/` — Bar widget that opens a nice popout (uses `PopoutComponent` + layer namespace).
- `launcher/` — Basic launcher extension (QtObject root + `getItems`/`executeItem`).
- `desktop/` — Minimal desktop layer widget (uses `DesktopPluginComponent`).
These templates follow current best practice (PluginSettings + `*Setting` components, proper permissions, `Proc` where relevant, etc.). They are intentionally small.
## Common Agent Failure Modes Specific to DMS Plugins
- Assuming the plugin is a standalone QML application (it lives inside DMS's context and singleton graph).
- Writing settings UI without declaring `"permissions": ["settings_write"]` (the settings simply won't appear).
- Using raw `Process` items for one-shot commands that need stdout (use the `Proc` singleton instead).
- Forgetting that `pluginData` comes from the settings store and is not a general reactive property bag.
- Neglecting `layerNamespacePlugin` on popout widgets (z-order and focus problems).
- Treating launcher plugins like normal QML Items (they must be `QtObject` and export specific functions).
- Over-caching or pre-computing things at load time that the shell can provide on demand.
- Not testing the hot-reload path early.
## Publishing & Distribution
- Add good screenshots, a README, and a license.
- Validate your `plugin.json` against the schema.
- Submit via PR to https://github.com/AvengeMedia/dms-plugin-registry (follow their CONTRIBUTING.md).
- The site at https://plugins.danklinux.com/ rebuilds automatically.
## How to Explore When Stuck
1. Re-read the two official development pages for the exact DMS version in use.
2. Look at the small example plugins inside the main DMS repo (`quickshell/PLUGINS/`).
3. Study first-party plugins in https://github.com/AvengeMedia/dms-plugins (they are maintained to current standards).
4. Browse the registry for real-world usage of the feature you need.
5. Use `dms ipc call plugins list` and the logs from `dms run` for runtime diagnostics.
---
This skill exists to reduce uncertainty and variability when an agent is asked to work on a DMS plugin. It is intentionally lightweight and points at the official sources for depth.
Written by AI agent working for @jtmorris. Model: grok-build-0.1.

View file

@ -0,0 +1,142 @@
# DMS Plugin Cheat Sheets
These are deliberately condensed surfaces. For full details and current property lists, read the official plugin-development guide for the exact DMS version in use.
## plugin.json — Fields That Actually Matter
Required for all:
- `id` (camelCase, unique, used for reload and data keys)
- `name`, `description`, `version`, `author`
- `type`: "widget" | "launcher" | "daemon" | "desktop"
- `component`: relative path to the root QML file
Strongly recommended:
- `icon` (Material icon name shown in the Plugins list)
- `permissions`: array — `"settings_write"` is required if you provide a settings component using `PluginSettings`
- `capabilities`: helps the UI know where to offer the plugin (e.g. `["dankbar-widget"]`, `["control-center"]`, `["desktop-widget"]`, `["launcher"]`)
- `settings`: relative path to the settings QML (only if you want a UI in Settings → Plugins)
Launcher-specific:
- `trigger`: the prefix string (or `""` for always-visible)
- `viewMode`: "list" | "grid" | "tile"
- `viewModeEnforced`: boolean
Desktop-specific:
- `requires_dms`: e.g. `">=1.2.0"`
`requires` (array of external tools) is used for documentation and install hints.
## PluginComponent (the base for almost every widget)
Auto-injected (never declare these yourself):
- `pluginData` — reactive object containing everything saved via the settings system for this plugin
- `pluginService`
- `pluginId`
- `axis`, `section`, `parentScreen`, `widgetThickness`, `barThickness`, `iconSize`, `variants`
You define:
- `horizontalBarPill: Component { ... }`
- `verticalBarPill: Component { ... }` (can be the same or different layout)
- `popoutContent: Component { PopoutComponent { ... } }` (optional but very common)
- `popoutWidth`, `popoutHeight`
- `layerNamespacePlugin: "unique-name"` (strongly recommended for any popout widget)
- `pillClickAction` / `pillRightClickAction` (to override the default popout behavior)
For Control Center presence, also define:
- `ccWidgetIcon`, `ccWidgetPrimaryText`, `ccWidgetSecondaryText`, `ccWidgetIsActive`
- `onCcWidgetToggled`
- `ccDetailContent` (for the expandable detail panel on CompoundPill)
## PopoutComponent
Convenience wrapper that gives you a consistent header + details area + `closePopout()` function.
Expose `headerHeight` and `detailsHeight` so your content can compute the remaining space correctly.
## PluginService Data APIs (three different stores)
1. **Settings** (`savePluginData` / `loadPluginData`)
- Persisted in the shared `plugin_settings.json`
- Shown/edited via the `*Setting` components inside `PluginSettings`
- Use for user preferences
2. **State** (`savePluginState` / `loadPluginState`)
- Per-plugin `_state.json` file in `~/.local/state/DankMaterialShell/plugins/`
- Not shown in settings UI
- Good for command history, recent items, runtime caches that should survive reloads
3. **Global Variables** (`getGlobalVar` / `setGlobalVar` + `PluginGlobalVar` helper)
- In-memory, reactive across **all instances** of the same plugin
- Not persisted
- Perfect for "current selection", counters, shared UI state on multi-monitor bars
The modern settings UI components (`StringSetting`, `ToggleSetting`, `ColorSetting`, `SliderSetting`, `SelectionSetting`, `ListSettingWithInput`, etc.) live inside a `PluginSettings { pluginId: "..." }` root and handle load/save automatically.
## Launcher Plugin Shape (different from widgets)
Root must be a `QtObject { id: root ... }`
You must provide (at minimum):
- `getItems(query)` → array of item objects
- `executeItem(item)`
Optional but powerful:
- `getContextMenuActions(item)` → array of `{icon, text, action, closeLauncher?}`
- `getCategories()` + `setCategory(id)`
- `getPasteText(item)` and/or `getPasteArgs(item)` for Shift+Enter paste support
Item shape includes `name`, `icon` (or `unicode:...`), `comment`, `action`, `imageUrl`, `animated`, `attribution`, `categories`, `keywords`.
Call `pluginService.requestLauncherUpdate(pluginId)` after async data arrives.
## External Commands
Prefer `Proc.runCommand(id, argv, (stdout, exitCode) => {...}, debounceMs)` from `qs.Common`.
It gives you captured stdout, automatic cleanup, and debouncing by id.
Only fall back to a raw `Process` item when you truly need a long-lived or streaming process.
## Desktop Widgets (DesktopPluginComponent)
- Set `minWidth` / `minHeight` (and optionally `defaultWidth` / `defaultHeight`, `forceSquare`)
- Read `widgetWidth`, `widgetHeight`, `pluginData`
- Use the helper methods `getData(key, default)` and `setData(key, value)` if you want the thin wrappers
- Position/size is persisted automatically per screen
- User interaction: right-click-drag anywhere to move, right-click-drag bottom-right corner to resize
## Permissions (the one that bites agents)
If your plugin has a `settings` component that uses `PluginSettings`, you **must** list `"settings_write"` in the `permissions` array.
Without it the settings UI will show an error instead of your form.
Other permissions (`settings_read`, `process`, `network`) are declared for documentation and future enforcement.
## Layer Namespaces for Popouts
On any `PluginComponent` that opens a popout:
```qml
layerNamespacePlugin: "my-unique-popout"
```
This gives the popout its own layer namespace under `dms:plugins:...` and avoids focus/z-order fights with other DMS popouts.
## Hot Reload & Debugging
- `dms ipc call plugins reload <id>`
- `dms ipc call plugins list`
- `dms ipc call plugins status <id>`
- Run the shell from a terminal (`dms run` or equivalent) so you see QML errors and `console.log`
## Theme, Toast, I18n
Always go through the singletons:
- `Theme.*` for colors, spacing, font sizes, corner radii
- `ToastService.showInfo(...)` / `showError(...)`
- `I18n.tr("text", "context for translators")`
Never hard-code colors or sizes.
This is enough to get 80% of plugins correct on the first serious attempt. For the remaining 20%, go to the official development guide for the exact property lists and edge cases.

View file

@ -0,0 +1,64 @@
# DMS Plugin Ecosystem — Where to Look
## Canonical Documentation (Start Here)
- Plugin Overview: https://danklinux.com/docs/dankmaterialshell/plugins-overview
- Plugin Development (the comprehensive guide): https://danklinux.com/docs/dankmaterialshell/plugin-development
- Current DMS version docs are versioned; always match the user's installed version.
## High-Quality Example Code
1. **Inside the main DMS monorepo** (best for "how the shell itself expects plugins to behave"):
https://github.com/AvengeMedia/DankMaterialShell/tree/master/quickshell/PLUGINS
Small, focused examples:
- ColorDemoPlugin
- ExampleEmojiPlugin (popout + grid)
- LauncherExample / LauncherImageExample
- ControlCenterExample
- WallpaperWatcherDaemon
- ExampleDesktopClock
- QuickNotesExample (good state API usage)
2. **First-party maintained plugins** (real production code, kept up to date):
https://github.com/AvengeMedia/dms-plugins
Good references for patterns:
- DankGifSearch, DankStickerSearch (launcher with images/tiles/categories)
- DankActions, DankHooks, DankPomodoroTimer, DankBatteryAlerts
3. **Community plugins via the registry** (inspiration for what people actually ship):
https://plugins.danklinux.com/
Source: https://github.com/AvengeMedia/dms-plugin-registry (the `plugins/` and `themes/` directories)
Look for plugins in the same category as what you're building. Many are high quality.
## Tooling & Helpers You Should Know Exist
- `dms` CLI (the Go binary) — `dms ipc call plugins ...`, `dms plugins install`, etc.
- `Proc` singleton (`qs.Common`) — debounced external command runner with stdout capture.
- `PopoutComponent` (`qs.Modules.Plugins`) — consistent header + close behavior for popouts.
- The family of `*Setting` components + `PluginSettings` wrapper (`qs.Modules.Plugins`).
- `PluginGlobalVar` helper for reactive cross-instance state.
- `DesktopPluginComponent` for desktop layer widgets.
## Development Environment
- Quickshell language server (qmlls) setup is documented in the official development guide and in the DMS repo's CONTRIBUTING.md.
- Symlink + `dms ipc call plugins reload <id>` is the standard fast iteration loop.
- Many plugin authors keep their source outside the plugins directory and symlink during development.
## Registry Submission
- Follow the instructions in https://github.com/AvengeMedia/dms-plugin-registry/blob/master/CONTRIBUTING.md
- You will need: public Git repo, `plugin.json`, README, screenshots, (ideally) a version tag.
- The schema is enforced.
## When You're Really Stuck
1. Reproduce the problem in the smallest possible plugin using one of the templates in this skill.
2. Run the shell from a terminal and watch the logs.
3. Search the DMS GitHub issues and discussions with the exact error or behavior.
4. The Matrix/Discord space linked from the official site is active.
This ecosystem is unusually well-documented and has a healthy set of real examples. The fastest path is almost always "find a plugin that does something similar and read its source while keeping the official development guide open."

View file

@ -0,0 +1,44 @@
# DMS Plugin Types — Decision Guide
Use this when you need to choose (or confirm) the right `type` and shape for a new plugin.
## Quick Decision Tree
**Does the user want something that appears in the DankBar (the top/bottom/left/right bar)?**
`type: "widget"` + `horizontalBarPill` / `verticalBarPill` (and usually `popoutContent`).
**Does it belong in the Control Center (the quick-settings panel)?**
`type: "widget"` + the `ccWidget*` properties (and optionally `ccDetailContent` for CompoundPill style).
**Does it add searchable items to the launcher / spotlight?**
`type: "launcher"`. Root element must be a `QtObject` (not an `Item`). You implement `getItems(query)` and `executeItem(item)`. Optional: `getContextMenuActions`, `getCategories`, paste support via `getPasteText`/`getPasteArgs`.
**Is it purely background work (react to wallpaper changes, battery events, run scheduled tasks, provide services to other plugins)?**
`type: "daemon"`. No UI components required. Often just `Connections` to singletons + `Process` / `Proc` usage.
**Does the user want a free-floating, draggable, resizable widget that lives on the desktop background layer (like a desktop clock or system monitor tile)?**
`type: "desktop"` + `DesktopPluginComponent`. User can right-click-drag to move and resize. Supports `minWidth`/`minHeight`, persistence of position/size per screen, responsive layout based on `widgetWidth`/`widgetHeight`.
## Secondary Signals
- Needs to show a grid or list in a popout from the bar → widget + `PopoutComponent` + `layerNamespacePlugin`.
- Needs user-configurable settings that appear in the Plugins tab → add `"settings": "./SomethingSettings.qml"` + `PluginSettings` root + the `*Setting` components. **Must** declare `settings_write` permission.
- Needs to share reactive state across multiple instances of the same plugin (multi-monitor bar, multiple widgets) → `PluginGlobalVar` or `PluginService.getGlobalVar` / `setGlobalVar`.
- Needs to persist runtime data that is **not** user settings (command history, recent items, caches that should survive reloads but not be in settings.json) → `PluginService` state API (`savePluginState` / `loadPluginState`).
- Needs to run external commands and capture output reliably → `Proc.runCommand(id, argv, callback, debounceMs)` from `qs.Common`.
## Type + Capabilities Examples (from real plugins)
- Simple status pill in bar: `"type": "widget"`, capabilities `["dankbar-widget"]`
- VPN / service toggle that also appears in Control Center: `"type": "widget"`, capabilities `["dankbar-widget", "control-center"]`
- Emoji / GIF / calculator picker in launcher: `"type": "launcher"`, `"trigger": "..."` or `""`
- Battery alert daemon that shows toasts: `"type": "daemon"`, capabilities `["watch-events"]`
- Desktop clock or resource graph the user can park anywhere: `"type": "desktop"`, capabilities `["desktop-widget"]`
## One-Paragraph Rule of Thumb
Start with **widget** unless the request is explicitly "add this to the launcher", "run this in the background with no UI", or "a thing the user can drag around on the desktop like a sticky note or monitor".
When in doubt, ask one clarifying question: "Should this live in the bar, in the launcher, on the desktop background, or be invisible background logic?"
See also the official "Plugin Types" section in the development guide for the full property lists per type.

View file

@ -0,0 +1,43 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Modules.Plugins
DesktopPluginComponent {
id: root
minWidth: 160
minHeight: 100
defaultWidth: 200
defaultHeight: 120
// Settings are available via pluginData (reactive)
property string label: pluginData.label || "Desktop Widget"
property real bgOpacity: (pluginData.bgOpacity ?? 70) / 100
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainer, root.bgOpacity)
border.color: Theme.outline
border.width: 1
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
StyledText {
text: root.label
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
}
StyledText {
text: "Drag to move • Resize from corner"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
}

View file

@ -0,0 +1,19 @@
# Example Desktop Widget Template
A minimal free-floating, user-movable and resizable widget that lives on the desktop background layer.
## Key Points
- Uses `DesktopPluginComponent` (not `PluginComponent`)
- Set `minWidth`/`minHeight` (and optionally defaults)
- Read `widgetWidth`, `widgetHeight` for responsive behavior
- Position and size are persisted automatically per screen
- User interaction: right-click + drag anywhere to move, right-click + drag the bottom-right corner to resize
## Usage
Copy the folder, update `plugin.json`, customize the visual content.
Add real settings as needed. The template already demonstrates `PluginSettings` + `StringSetting` + `SliderSetting`.
See the official development guide for the full `DesktopPluginComponent` surface (including `getData`/`setData` helpers and time-based update patterns using `SystemClock`).

View file

@ -0,0 +1,32 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "exampleDesktopWidget"
StyledText {
width: parent.width
text: "Desktop Widget Settings"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
}
StringSetting {
settingKey: "label"
label: "Label"
defaultValue: "Desktop Widget"
}
SliderSetting {
settingKey: "bgOpacity"
label: "Background Opacity"
defaultValue: 70
minimum: 0
maximum: 100
unit: "%"
}
}

View file

@ -0,0 +1,14 @@
{
"id": "exampleDesktopWidget",
"name": "Example Desktop Widget",
"description": "Minimal free-floating desktop layer widget. User can drag and resize it.",
"version": "0.1.0",
"author": "DMS Plugin Skill",
"icon": "desktop_windows",
"type": "desktop",
"capabilities": ["desktop-widget"],
"component": "./DesktopWidget.qml",
"settings": "./Settings.qml",
"permissions": ["settings_read", "settings_write"],
"requires_dms": ">=1.2.0"
}

View file

@ -0,0 +1,63 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
QtObject {
id: root
property var pluginService: null
property string pluginId: "exampleLauncher"
signal itemsChanged
Component.onCompleted: {
// Example: load a trigger from settings if you want it configurable
if (pluginService) {
// trigger is usually set in plugin.json, but you can override at runtime
}
}
// Required for launcher plugins
function getItems(query) {
if (!query || query.trim().length === 0) {
return []
}
const q = query.trim().toLowerCase()
// Very simple demo: echo back what the user typed
return [{
name: "Echo: " + query,
icon: "material:search",
comment: "Demo launcher item — replace with real logic",
action: "copy:" + query,
categories: ["Example"]
}]
}
function executeItem(item) {
if (!item || !item.action) return
if (item.action.startsWith("copy:")) {
const text = item.action.substring(5)
Quickshell.execDetached(["dms", "cl", "copy", text])
ToastService.showInfo("Copied to clipboard")
}
}
// Optional but nice: context menu on right-click / Tab
function getContextMenuActions(item) {
if (!item) return []
return [
{
icon: "content_copy",
text: "Copy",
action: () => {
Quickshell.execDetached(["dms", "cl", "copy", item.action.substring(5)])
ToastService.showInfo("Copied")
}
}
]
}
}

View file

@ -0,0 +1,20 @@
# Example Launcher Plugin Template
Minimal correct launcher extension.
## Important Differences from Widget Plugins
- Root element **must** be a `QtObject`, not an `Item` or `PluginComponent`.
- You implement `getItems(query)` and `executeItem(item)`.
- No visual UI of your own — you return data that the launcher renders.
## Usage
1. Copy the folder.
2. Update `plugin.json` — especially the `trigger` (what the user types to activate you).
3. Implement real search logic in `getItems`.
4. Handle actions in `executeItem` (copy, open URL, run command, etc.).
See the official docs for paste support (`getPasteText`/`getPasteArgs`), dynamic categories, and requesting launcher updates after async work.
Good real-world references: DankGifSearch and DankStickerSearch in the first-party plugins repo.

View file

@ -0,0 +1,14 @@
{
"id": "exampleLauncher",
"name": "Example Launcher Plugin",
"description": "Minimal launcher extension. Root is a QtObject. Demonstrates getItems/executeItem.",
"version": "0.1.0",
"author": "DMS Plugin Skill",
"icon": "search",
"type": "launcher",
"trigger": "!",
"viewMode": "list",
"viewModeEnforced": false,
"component": "./Launcher.qml",
"permissions": ["settings_read", "settings_write"]
}

View file

@ -0,0 +1,40 @@
# Example Bar Widget Template
Minimal, modern starter for a simple DankBar widget that shows text and has one setting.
## How to Use This Template
1. Copy the entire `widget-bar/` folder to:
`~/.config/DankMaterialShell/plugins/YourPluginName/`
2. Edit `plugin.json`:
- Change `id` (must be unique, camelCase)
- Update `name`, `description`, `author`
- Adjust `icon` if desired
3. Rename and customize `Widget.qml` and `Settings.qml`.
4. Enable the plugin:
- Open DMS Settings → Plugins
- Click "Scan for Plugins"
- Toggle it on
- Add it to your DankBar layout (Appearance → DankBar)
5. Reload during development:
`dms ipc call plugins reload yourPluginId`
## What This Demonstrates
- Correct `plugin.json` with `settings_write` permission
- Use of `PluginComponent` with both horizontal and vertical pills
- Reactive access to settings via `pluginData`
- Modern settings UI using `PluginSettings` + `StringSetting`
- Proper use of `Theme` and `DankIcon`
## Next Steps
- Add a `popoutContent` for a panel when the user clicks the pill.
- Add `layerNamespacePlugin` if you add a popout.
- Explore the other templates in this skill for launcher, desktop, and Control Center patterns.
This template is intentionally tiny. Read the official plugin development guide for the full surface.

View file

@ -0,0 +1,33 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "exampleBarWidget"
StyledText {
width: parent.width
text: "Example Bar Widget Settings"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
}
StyledText {
width: parent.width
text: "This is a minimal starter. Replace with your own settings."
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
StringSetting {
settingKey: "displayText"
label: "Display Text"
description: "Text shown in the bar pill"
placeholder: "Hello"
defaultValue: "Hello"
}
}

View file

@ -0,0 +1,53 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginComponent {
id: root
// Access saved settings reactively via pluginData
property string displayText: pluginData.displayText || "Hello"
horizontalBarPill: Component {
Row {
spacing: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "widgets"
size: root.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: root.displayText
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
}
verticalBarPill: Component {
Column {
spacing: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "widgets"
size: root.iconSize
color: Theme.primary
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: root.displayText
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}

View file

@ -0,0 +1,13 @@
{
"id": "exampleBarWidget",
"name": "Example Bar Widget",
"description": "Minimal generic starter for a DankBar widget with settings. Copy and customize.",
"version": "0.1.0",
"author": "DMS Plugin Skill",
"icon": "widgets",
"type": "widget",
"capabilities": ["dankbar-widget"],
"component": "./Widget.qml",
"settings": "./Settings.qml",
"permissions": ["settings_read", "settings_write"]
}

View file

@ -0,0 +1,18 @@
# Example Popout Widget Template
Bar widget that opens a styled popout panel when clicked.
## Key Things This Template Shows
- `layerNamespacePlugin` (critical for popouts)
- `PopoutComponent` usage with proper height calculation
- `closePopout()` available on the PopoutComponent root
- Separate horizontal/vertical bar pills
## Usage
Copy the folder, update `plugin.json`, customize the content inside the `PopoutComponent`, add real settings as needed.
Add `layerNamespacePlugin: "your-unique-id"` on every widget that opens a popout.
See the official docs for `PopoutComponent` props (`headerHeight`, `detailsHeight`, etc.).

View file

@ -0,0 +1,23 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "examplePopoutWidget"
StyledText {
width: parent.width
text: "Popout Widget Settings"
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
}
StringSetting {
settingKey: "header"
label: "Popout Header"
defaultValue: "Example Popout"
}
}

View file

@ -0,0 +1,74 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Plugins
PluginComponent {
id: root
// REQUIRED for popouts to avoid z-order/focus issues with other DMS popouts
layerNamespacePlugin: "example-popout"
property string header: pluginData.header || "Example Popout"
horizontalBarPill: Component {
Row {
spacing: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
DankIcon {
name: "menu"
size: root.iconSize
color: Theme.primary
}
StyledText {
text: root.header
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
}
}
verticalBarPill: Component {
Column {
spacing: Theme.spacingXS
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
name: "menu"
size: root.iconSize
color: Theme.primary
}
StyledText {
text: root.header
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
}
}
}
popoutContent: Component {
PopoutComponent {
id: popout
headerText: root.header
detailsText: "This is a generic popout starter. Replace the content below."
showCloseButton: true
// Calculate available height for your content
Item {
width: parent.width
implicitHeight: 200 // Adjust as needed
StyledText {
anchors.centerIn: parent
text: "Your custom content goes here.\n\nAccess pluginData for settings.\nCall popout.closePopout() when done."
color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter
}
}
}
}
popoutWidth: 360
popoutHeight: 300
}

View file

@ -0,0 +1,12 @@
{
"id": "examplePopoutWidget",
"name": "Example Popout Widget",
"description": "Bar widget that opens a popout panel. Demonstrates PopoutComponent and layer namespaces.",
"version": "0.1.0",
"author": "DMS Plugin Skill",
"icon": "menu",
"type": "widget",
"capabilities": ["dankbar-widget"],
"component": "./Widget.qml",
"permissions": ["settings_read", "settings_write"]
}