* feat(plugins): expose IPC handlers for runtime plugin discovery
Follow-up to #1659. That issue landed hot-reload for settings.json via
FileView.watchChanges + a 1ms Timer to skirt the JSON parse race. It does
not cover plugin discovery in runtime: adding a new plugin directory to
~/.config/DankMaterialShell/plugins/ while the shell is running is not
consistently picked up by the existing FolderListModel watcher in
PluginService.qml, and there is no IPC handle for forcing a rescan from
outside the shell.
Adds an IpcHandler on PluginService with five small functions:
- scan(): wraps existing scanPlugins(), returns count snapshot
- rescan(pluginId): wraps existing forceRescanPlugin(id), validates id
- reload(pluginId): wraps existing reloadPlugin(id), validates id
- list(): newline-joined id\tloaded\ttype\tname for every known plugin
- status(pluginId): loaded\ttype\terror for one plugin
Scope intentionally small: no file-watcher changes, no new daemons, no
schema additions. Target string "plugins" does not collide with any
existing target in DMSShellIPC.qml.
Validation:
- qs ipc --pid <PID> call plugins list returns one row per known plugin
- qs ipc --pid <PID> call plugins scan returns SCAN_TRIGGERED with count
- qs ipc --pid <PID> call plugins rescan <id> returns RESCAN_TRIGGERED
- Empty-arg paths return ERROR strings instead of throwing
- git merge-tree against origin/master is clean
* hardening(plugins): fix 7 review findings in scan-ipc IPC handlers
Follow-up to commit 43603f56 which ported PR #2601 (AvengeMedia scan-ipc)
to the fork. The original port was functionally correct but had seven
review issues that would block upstream adoption. This patch addresses
each one with a minimal, focused change.
* B1 IPC target collision: renamed `target: "plugins"` to
`target: "plugin-scan"`. The original name collided with the
existing IpcHandler in DMSShellIPC.qml:1180 which already registers
enable/disable/toggle/list/status under "plugins". The split keeps
both APIs discoverable without one shadowing the other.
* H1 Fire-and-forget scan: documented that scan() returns the
pre-debounce count and that callers must poll list/status (or wait
~200ms) to observe the post-debounce state. A proper requestId +
await mechanism was considered and rejected for scope reasons.
* H2 TOCTOU in rescan(): the handler now reads availablePlugins[id]
inside forceRescanPlugin via the id string only — no captured
object reference. A parallel resyncDebounce tick can otherwise
mutate the entry between the read and the use.
* M1 list() cap: added a 256-entry cap and a leading header line
(`# count=N returned=M`) so callers can detect truncation. A
hostile / buggy plugin mass-creating entries could otherwise
allocate 80 KB+ per IPC call.
* M2 status() prefix: "unknown\t\t" became
`ERROR: unknown pluginId '...'` to match the rest of the
handlers' prefix convention. Empty trailing field means no error.
* M3 id sanitization: every handler that takes pluginId now
validates against `/^[a-zA-Z0-9_\-:]{1,64}$` before use. This
rejects shell-injection payloads ("foo\tmalicious") and prototype
pollution attempts ("__proto__", "constructor"). The list() and
status() handlers also sanitize \t/\n in name and error fields
so callers can rely on the TSV structure.
Verification: brace count balanced (252/252). Manual read of all
five handlers confirms no logic regression. QML runtime tests are
not part of the DMS test suite, so end-to-end validation requires
rebuilding the shell — deferred to the user.
Not pushed. Stage-local-first rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(plugins): strip inline comments per review feedback
Purian23 in PR #2611 review: 'let's address the amount of line
comments in the code, there's not a need for all of them to exist.'
Removed 48 comment lines. The substantive justification (why the
regex, why fire-and-forget, why re-read inside forceRescanPlugin,
why the 256 cap, why the target rename) now lives in the PR body
under 'Review-driven fixes in this iteration' and 'What changed'
where the reviewer already reads it.
No code logic changed. Brace count 252/252. Diff is -48/+0 on
quickshell/Services/PluginService.qml.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Move system update flow to GO, with a CLI (convenient AIO tool) and
server integration. All lifecycle, scheduling, execution occurs on
backend side.
Run some backends via pkexec, some via terminal like paru/yay.
Incorporate flatpak as an option to update.
Add terminal override setting in GUI, in addition to $TERMINAL env
variable.
fixes#2307fixes#822fixes#1102fixes#1812fixes#1087fixes#1743
- Aggregate plugins/extensions in new "all" tab
- Quick tab actions
- New tile mode for results
- Plugins can enforce/require view mode, or set preferred default
- Danksearch under "files" category
Add togglePlugin() function and IPC command to toggle plugin visibility,
particularly for slideout-capable daemon plugins like AI Assistant.
Implementation uses lazy instantiation for daemon plugins:
- Daemons remain uninstantiated on load (respecting lifecycle)
- First toggle() call instantiates the daemon on-demand
- Subsequent toggles use the existing instance
- Prevents duplicate instantiation while supporting toggle functionality
This approach preserves the fix from f9b9d986 (ensure daemon plugins
not instantiated twice) while enabling new toggle capabilities.
Changes:
- Add PluginService.togglePlugin() with lazy instantiation
- Add DMSShellIPC plugin.toggle() command
- Maintains compatibility with existing daemon plugins