You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary
Opts callers into Elgato's six built-in touchstrip layouts (`$X1` /
`$A0` / `$A1` / `$B1` / `$B2` / `$C1`) via a new `encoder_layout` field
on `streamdeck_write_page`. Unlocks progress bars ($B1/$B2), icon+value
readouts ($A1), dual-row ($C1), full-canvas image ($A0), and
centered-icon ($X1) composition on the Stream Deck + / + XL touchstrip.
The default action (no layout declared) is unchanged — callers who rely
on full-strip `Controllers[Encoder].Background` show-through keep that
behavior unless they explicitly pick a layout variant. Picking a variant
forgoes show-through by Elgato design (a declared layout replaces the
default composition).
## Design
Static multi-UUID action variants. Each of the six layouts gets its own
action UUID in the bundled plugin manifest with `Encoder.layout`
declared — the Elgato-documented, durable site. No plugin-JS changes
(stays the no-op registration shell from #20). No per-instance
`Controllers[Encoder].Layout` writes (undocumented; stripping risk à la
PR #20's `Resources.Encoder.*` lesson).
| Action UUID suffix | Layout | Effect |
|---|---|---|
| `.dial` (default) | *none* | Elgato default composition, full-strip
show-through |
| `.dial.x1` | `$X1` | Title + centered icon |
| `.dial.a0` | `$A0` | Title + full-width image canvas |
| `.dial.a1` | `$A1` | Title + icon + value slot |
| `.dial.b1` | `$B1` | Title + icon + progress bar |
| `.dial.b2` | `$B2` | Title + icon + gradient progress |
| `.dial.c1` | `$C1` | Dual icon/progress rows |
Plugin manifest version bumped `0.1.0 → 0.2.0`.
## API
```jsonc
{
"controller": "encoder",
"key": 0,
"title": "Volume",
"icon_path": "/path/to/72x72.png",
"encoder_layout": "$A1" // ← new, optional, enum
}
```
- `encoder_layout` is encoder-only (keypad buttons reject it).
- Unknown values rejected with the supported-enum in the error message.
- Cannot combine with `path`/`action_type`/`plugin_uuid`/`action_uuid` —
it's a convenience field for the built-in MCP dial; advanced callers who
specify their own plugin/action should declare layout in their own
plugin manifest.
## Out of scope (deliberately deferred)
- **Custom JSON layouts.** Clean support needs either (a) a generic
action with JS that reads `Settings.layoutPath` — which reintroduces the
JS-logic coupling #20 deliberately avoided — or (b) one action UUID per
shipped custom layout, premature without a concrete layout in hand.
- **Runtime `setFeedbackLayout`.** Overlaps with the future
`setFeedback` dynamic-value experiment. Build once there's a live-value
use case.
## Test plan
- [x] `uv run pytest tests/` — 69 passing (64 + 5 new)
- [x] `uv run ruff check .` — clean
- [x] `uv build --wheel` — all 7 action entries ship inside the plugin
bundle, plugin manifest version 0.2.0
- [x] On-device (Stream Deck + XL): write one page with six encoder
buttons (one per layout variant), visually confirm each renders with the
declared composition, quit + relaunch Elgato app, confirm all layouts
persist
- [x] On-device: confirm default (no `encoder_layout`) still shows
full-strip background show-through
New tests:
- `test_write_page_encoder_layout_routes_to_variant_uuid` — `$A1` →
`.dial.a1` UUID
- `test_write_page_encoder_layout_default_uses_default_uuid` — omit →
`.dial` UUID
- `test_write_page_rejects_unknown_encoder_layout` — `$Z9` raises with
supported enum
- `test_write_page_rejects_encoder_layout_on_keypad` — parallel to
`strip_background_path` keypad-rejection
- `test_write_page_auto_installs_mcp_plugin_for_layout_variant` —
variant triggers plugin install
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jack-arturo <13076544+jack-arturo@users.noreply.github.com>
Copy file name to clipboardExpand all lines: skills/streamdeck-profile/SKILL.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -81,16 +81,21 @@ These are the source of truth for manifest schemas, image dimensions, and touchs
81
81
82
82
### Built-in touchstrip layouts
83
83
84
+
Pass `encoder_layout: "$A1"` (etc.) on an encoder button in `streamdeck_write_page` to opt into a layout. Omit `encoder_layout` for the default (Elgato default composition with full-strip background show-through). Picking a variant forgoes show-through — the declared layout replaces the default composition.
85
+
84
86
| Layout | Semantics |
85
87
|--------|-----------|
88
+
|*(omit)*| Default: icon over `Encoder.background`, full-strip `Controllers[Encoder].Background` shows through |
86
89
|`$X1`| Title top, icon centered |
87
90
|`$A0`| Title top, full-width image canvas center |
88
91
|`$A1`| Title top, icon left, text value right |
89
92
|`$B1`| Title top, icon left, text + progress bar right |
90
93
|`$B2`| Title top, icon left, text + gradient progress bar |
91
94
|`$C1`| Title top, dual icon-left/progress-right rows |
92
95
93
-
Custom layouts are supported as JSON shipped with the plugin.
96
+
Each variant is a separate action UUID in the bundled plugin (`io.github.verygoodplugins.streamdeck-mcp.dial.<x1|a0|a1|b1|b2|c1>`) with `Encoder.layout` statically declared — the only Elgato-documented, durable way to set a layout.
97
+
98
+
Custom layouts (JSON shipped with the plugin) are not yet supported; deferred until a concrete layout requires it.
94
99
95
100
### Touchstrip custom art goes in the profile manifest, not `setFeedback`
0 commit comments