Add Omarchy shell plugin for the Stream Deck

QML bar widget + popout panel (dev.woodard.streamdeck) that drives
streamdeck-ctl: key grid shaped by the connected model, per-key editor,
brightness slider, and daemon controls. Themed via qs.Commons.

Makefile gains build-ctl, install-ctl, install-plugin, validate-plugin
and uninstall-plugin targets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjuZBBfzXqZgxRhxzJvkkC
This commit is contained in:
Levi Woodard
2026-09-27 18:21:33 -06:00
parent 8cff4f8418
commit 2aa4c65e42
11 changed files with 2097 additions and 1 deletions

169
omarchy-plugin/README.md Normal file
View File

@@ -0,0 +1,169 @@
# Stream Deck — Omarchy shell plugin
A bar widget for [Omarchy](https://omarchy.org) that controls the
[streamdeck-go](https://git.i0t.app/lwoodard/streamdeck-go) daemon: device and
daemon status, brightness, and a live 8×4 key grid you can edit in place.
Every colour and metric resolves through Omarchy's `qs.Commons` theme tokens
(`Color.*`, `Style.*`), so the widget follows the active theme rather than
carrying a palette of its own.
---
## What it does
**Bar icon** — a miniature deck that fills in proportionally to how much of the
deck is mapped, and dims when the deck is unplugged or the daemon is stopped.
**Panel**
| Section | Behaviour |
|---|---|
| Header | Title, status line, and an on/off switch for the `streamdeck-go` user service |
| Brightness | Slider writing `brightness:` into `config.yaml`; the daemon hot-reloads, so the deck dims as you drag |
| Facts | Device model, connection state, daemon state and uptime |
| Key grid | One cell per physical key, with icon previews, slot indices, and labels. Empty slots are shown so you can fill them |
| Editor | Right-click a key to edit its module/function, parameters, shell command, icon, label, and label colour |
| Actions | Restart the daemon, open `config.yaml`, open the icons folder, follow the log |
A red dot on a cell marks a `priv:` key. Those run through the privileged helper
and are deliberately **not** runnable from the panel — press them on the deck.
### Mouse and keyboard
| Input | Action |
|---|---|
| Left-click bar icon | Open/close the panel |
| Right-click bar icon | Refresh |
| Middle-click bar icon | Start/stop the daemon |
| Left-click a key | Run that key's command (fire-and-forget, like a physical press) |
| Right-click a key | Open the editor for that key |
| Arrow keys | Move the grid cursor |
| `Enter` | Edit the focused key |
| `Space` | Run the focused key |
| `e` | Edit the focused key |
| `r` | Refresh |
| `p` | Start/stop the daemon |
| `Esc` | Leave the field → close the editor → close the panel |
---
## Requirements
- Omarchy with `omarchy-shell` running
- `streamdeck-go` installed and its user service present
- **`streamdeck-ctl`** — the helper CLI the plugin drives. The panel refuses to
show controls without it and tells you how to install it.
The plugin never edits YAML or probes USB itself; it shells out to
`streamdeck-ctl`, which reuses the daemon's own config and device code. That
keeps one implementation of the config format rather than two.
---
## Install
From a checkout of the streamdeck-go repo:
```bash
make install-plugin # builds + installs streamdeck-ctl, links the plugin
omarchy plugin enable dev.woodard.streamdeck right
```
`make install-plugin` symlinks this directory into
`~/.config/omarchy/plugins/dev.woodard.streamdeck`, so edits to the QML are live
after a rescan:
```bash
omarchy-shell shell rescanPlugins
```
> Qt caches compiled QML in `~/.cache/quickshell/qmlcache`, and a running shell
> can keep serving a stale compiled unit across a rescan. If an edit doesn't seem
> to take effect, `omarchy restart shell`.
To validate before committing:
```bash
make validate-plugin
```
`omarchy plugin validate` refuses a symlinked plugin folder by design, so this
target validates the source directory rather than the installed link.
### Remove
```bash
make uninstall-plugin
```
---
## Settings
Configurable from the Omarchy bar widget settings:
| Setting | Default | Meaning |
|---|---|---|
| `refreshIntervalSec` | 10 | Poll cadence while the panel is open; closed, it relaxes to at most once a minute |
| `ctlPath` | *(empty)* | Explicit path to `streamdeck-ctl`; empty searches `~/.local/bin`, `~/go/bin`, `/usr/local/bin`, then `$PATH` |
| `configPath` | *(empty)* | Explicit `config.yaml`; empty uses `~/.config/streamdeck-go/config.yaml` |
| `hideWhenDisconnected` | false | Hide the bar icon entirely when no deck is connected |
---
## IPC
```bash
omarchy-shell dev.woodard.streamdeck open
omarchy-shell dev.woodard.streamdeck toggle
omarchy-shell dev.woodard.streamdeck status # "20 of 32 keys in use"
omarchy-shell dev.woodard.streamdeck refresh
omarchy-shell dev.woodard.streamdeck brightness 60
omarchy-shell dev.woodard.streamdeck press 3
```
Useful for Hyprland binds — e.g. a key that dims the deck without opening the bar.
---
## Security note
Module keys can reference secrets via `{{env "TOKEN"}}` in their exec templates.
`streamdeck-ctl status` never emits the rendered command — the panel shows only
`module · function` for module keys — so tokens can't leak into the UI or into
anything scraping the CLI's JSON. On Linux, `key press` merges the systemd user
manager's environment over the caller's before running, so a token imported with
`systemctl --user import-environment` reaches panel presses exactly as it reaches
hardware presses. (macOS keeps the caller's environment — launchd has no
equivalent bulk query.)
## A note on config formatting
Saving from the editor rewrites `config.yaml` through a YAML node-level editor.
Comments and blank lines are preserved, but two normalisations happen on any
save:
- trailing whitespace on values is trimmed
- a comment block dangling at the end of a key entry may be re-indented to match
the block it attaches to
Both are cosmetic. Values, ordering, and comment text are preserved.
---
## Files
```
manifest.json plugin metadata + settings schema
Panel.qml bar icon and popout panel (entry point)
Service.qml owns every streamdeck-ctl invocation
KeyGrid.qml the 8x4 (or 5x3) key grid
KeyEditor.qml per-key editor
StreamDeckIcon.qml drawn deck icon, takes its colour from the caller
Model.js parsing and formatting helpers
```
## License
MIT — see [LICENSE](LICENSE).