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:
parent
bc864bb9d8
commit
01ac7e9041
21 changed files with 873 additions and 0 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -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/
|
||||||
|
|
|
||||||
43
skills/dms-plugin-dev/README.md
Normal file
43
skills/dms-plugin-dev/README.md
Normal 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.
|
||||||
103
skills/dms-plugin-dev/SKILL.md
Normal file
103
skills/dms-plugin-dev/SKILL.md
Normal 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.
|
||||||
142
skills/dms-plugin-dev/docs/cheatsheets.md
Normal file
142
skills/dms-plugin-dev/docs/cheatsheets.md
Normal 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.
|
||||||
64
skills/dms-plugin-dev/docs/ecosystem.md
Normal file
64
skills/dms-plugin-dev/docs/ecosystem.md
Normal 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."
|
||||||
44
skills/dms-plugin-dev/docs/plugin-types.md
Normal file
44
skills/dms-plugin-dev/docs/plugin-types.md
Normal 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.
|
||||||
43
skills/dms-plugin-dev/templates/desktop/DesktopWidget.qml
Normal file
43
skills/dms-plugin-dev/templates/desktop/DesktopWidget.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
skills/dms-plugin-dev/templates/desktop/README.md
Normal file
19
skills/dms-plugin-dev/templates/desktop/README.md
Normal 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`).
|
||||||
32
skills/dms-plugin-dev/templates/desktop/Settings.qml
Normal file
32
skills/dms-plugin-dev/templates/desktop/Settings.qml
Normal 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: "%"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
skills/dms-plugin-dev/templates/desktop/plugin.json
Normal file
14
skills/dms-plugin-dev/templates/desktop/plugin.json
Normal 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"
|
||||||
|
}
|
||||||
63
skills/dms-plugin-dev/templates/launcher/Launcher.qml
Normal file
63
skills/dms-plugin-dev/templates/launcher/Launcher.qml
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
20
skills/dms-plugin-dev/templates/launcher/README.md
Normal file
20
skills/dms-plugin-dev/templates/launcher/README.md
Normal 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.
|
||||||
14
skills/dms-plugin-dev/templates/launcher/plugin.json
Normal file
14
skills/dms-plugin-dev/templates/launcher/plugin.json
Normal 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"]
|
||||||
|
}
|
||||||
40
skills/dms-plugin-dev/templates/widget-bar/README.md
Normal file
40
skills/dms-plugin-dev/templates/widget-bar/README.md
Normal 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.
|
||||||
33
skills/dms-plugin-dev/templates/widget-bar/Settings.qml
Normal file
33
skills/dms-plugin-dev/templates/widget-bar/Settings.qml
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
53
skills/dms-plugin-dev/templates/widget-bar/Widget.qml
Normal file
53
skills/dms-plugin-dev/templates/widget-bar/Widget.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
skills/dms-plugin-dev/templates/widget-bar/plugin.json
Normal file
13
skills/dms-plugin-dev/templates/widget-bar/plugin.json
Normal 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"]
|
||||||
|
}
|
||||||
18
skills/dms-plugin-dev/templates/widget-popout/README.md
Normal file
18
skills/dms-plugin-dev/templates/widget-popout/README.md
Normal 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.).
|
||||||
23
skills/dms-plugin-dev/templates/widget-popout/Settings.qml
Normal file
23
skills/dms-plugin-dev/templates/widget-popout/Settings.qml
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
74
skills/dms-plugin-dev/templates/widget-popout/Widget.qml
Normal file
74
skills/dms-plugin-dev/templates/widget-popout/Widget.qml
Normal 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
|
||||||
|
}
|
||||||
12
skills/dms-plugin-dev/templates/widget-popout/plugin.json
Normal file
12
skills/dms-plugin-dev/templates/widget-popout/plugin.json
Normal 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"]
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue