feat: support classic Stream Deck models, starting with the MK.2 - #1
Open
Micha0827 wants to merge 7 commits into
Open
feat: support classic Stream Deck models, starting with the MK.2#1Micha0827 wants to merge 7 commits into
Micha0827 wants to merge 7 commits into
Conversation
Add a device layer that reads key count, key resolution, grid position, input-only buttons, and the LCD segment from the control definitions a device reports, instead of assuming a Neo. Config gains an "infobar" key module and a layout preset per key count; the length of the keys array identifies the deck family, so an 8-key Neo layout and a 15-key MK.2 layout are both accepted while any other length still falls back to the default. Setup asks which deck a configuration is for and edits its keys row by row, or takes --keys=<n> to skip the question.
Key modules keep one 96x96 design canvas and are rendered at whatever pixel size the device reports. Below 80 pixels the two smallest hint lines are dropped rather than drawn illegibly. Decks without an LCD segment show the InfoBar across a run of adjacent keys. Slicing the 248x58 strip would cut words at the physical gaps, so each key instead carries one self-contained block of the current page: the first names the page, the rest carry its metrics. renderDeckBuffers now returns the keys in device index order followed by the InfoBar strip only when the device has an LCD, which leaves the Neo output byte-identical.
Open the first attached device whose controls describe drawable keys and render through its profile; pedals and docks report none and drop out on their own. Key writes follow the reported key indices and fillLcd runs only when an LCD segment exists. Page controls come from the profile's input-only buttons, which are the Neo touch points, so on a keypad deck indices 8 and 9 are ordinary keys and the InfoBar tiles take over paging.
npm run preview:15key renders the 15-key layout from the existing sample data, so the result can be checked without hardware. doctor now lists every Stream Deck it detects instead of only reporting on a Neo.
Profile derivation from synthetic control definitions, layout fitting and InfoBar spans, 15 keys rendered at 72x72 with no LCD buffer, distinct InfoBar tiles per page, paging from any tile, and config acceptance of a 15-key layout.
Verified on a Stream Deck MK.2: at 72px the scaled-down canvas was readable but tight. Where the hint line is dropped the freed space now goes to the rows that carry information, and a long InfoBar heading breaks at its space instead of shrinking to fit one line. The summary labels stay at their original size because the two sit side by side and collide above 8px. The Neo path renders byte-identically.
Micha0827
force-pushed
the
feat/classic-stream-deck-support
branch
from
July 23, 2026 15:32
9d4755b to
eefd27b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds support for Stream Deck models beyond the Neo, with the MK.2 as the concrete target. Instead of assuming a Neo, the device layer derives everything it needs from the control definitions the connected device already reports through
@elgato-stream-deck/node:feedbackTypeislcd(those carry apixelSize)lcd-segmentcontrol, if the device has oneNothing is keyed off a model name, so the Original, Original V2, MK.2, MK.2 Scissor, XL, Mini, and the 6/15/32 modules fall out of the same derivation. Devices that report no drawable keys, such as the Pedal, are skipped rather than opened.
The InfoBar on decks without an LCD segment
This was the one part that needed a design decision. My first attempt rendered the existing 248×58 strip across a run of keys and sliced it into tiles — but the physical gaps between keys cut words in half (
ALL AG|ENTS,OPE|N), which looked broken on hardware.What is in the PR instead: a run of adjacent
infobarkeys in one row each render a self-contained block of the current page. The first names the page, the rest carry its metrics. Tapping any tile advances the rotation, replacing what the touch points do on the Neo. On the 15-key default this maps cleanly — the four pages each have a heading plus up to three metrics, and the bottom row has exactly five keys for four tiles plus the page indicator.docs/images/15-key-preview.pngin this branch shows the result.Backwards compatibility
This mattered more to me than the new feature, so it is verified rather than asserted:
npm run previewbefore and after the change and compared checksums — same hash. Key modules keep a fixed 96×96 design canvas and are scaled to whatever pixel size the device reports, so the Neo simply renders at 1:1 as before.renderDeckBuffers(…, profile = NEO_PROFILE),actionForControl(…, profile = NEO_PROFILE), and the key renderers default to a 96×96 size.config.jsonfiles keep working. An 8-key layout stays valid; if it does not match the connected device, the recommended layout for that device is used instead.state.ts, anddashboard.tsare untouched — this is purely device, render, and input.18 new tests cover profile derivation from synthetic control definitions, layout fitting, InfoBar spans, 15 keys rendered at 72×72 with no LCD buffer, per-tile content, and paging from any tile.
npm run checkis green at 64 tests.Tested on hardware
Run on an actual Stream Deck MK.2 on macOS: device detection, all 15 keys at 72×72, the InfoBar tiles, and paging through all four pages from any tile. One thing that only showed up on the device: at 72px the scaled-down canvas was readable but tight, so where the smallest hint line is dropped the freed space now goes to the rows that carry information (last commit).
A note on scope
I am aware this widens what a project called Neo Agent Deck does, and that this is your call rather than a technical question. The name is untouched, the Neo behaviour is unchanged, and all device knowledge lives in one new file (
src/device.ts), so it should be straightforward to reject or reshape. Happy to split this into smaller PRs, drop the typography commit, or take a different approach to the InfoBar if you had something else in mind.Commits
feat(device)— derive deck geometry from control definitionsfeat(render)— scale modules to the device and put the InfoBar on keysfeat(deck)— drive any deck that reports drawable keysfeat(scripts)—npm run preview:15keyand multi-devicedoctortest— cover the 15-key layoutdocs— document support for classic modelsfix(render)— enlarge type on keys too small for the full layoutEach commit builds and tests green on its own.