Skip to content

Stream Deck: size the Row-1 builder window from placed keys — a builder can be selected while shown on none #1465

Description

@amrmelsayed

Problem: a builder can be selected while rendering on no key

Row-1 selection pages by a hardcoded constant, not by how many builder keys exist. ROW1_WINDOW_SIZE = 4 (apps/streamdeck/src/store.ts:28), builderWindowStart() returns floor(cursor.builder / 4) * 4, and windowedBuilder(slotIndex) returns builders()[windowStart + slotIndex]. The page step is fixed at four and is independent of how many BuilderAction keys the user actually placed.

The consequence is a correctness bug, not a sizing preference. Place three builder keys instead of four and every builder at index ≡ 3 (mod 4) renders on no key at all — while the Select dial still walks onto it, so it becomes the selected builder, drives Row 2's entire palette and both review dials, and shows no accent ring anywhere on the board. The reviewer is acting on a builder the deck never displays. (There is no gap only while the fleet is no larger than the number of placed slots.)

It is also wrong for most hardware: Mini is 3×2, Standard 5×3, XL 8×4, and the SD+ keypad 4×2, so a constant of four fits exactly one device.

Why now

The trigger is concrete rather than hypothetical: #1463 adds an Open Architect key that the owner intends to place on a Row-1 key (safe, because its Main mode is selection-independent), which leaves three builder slots against a fixed page of four — the exact configuration that hides every fourth builder. The fix belongs to Row-1 windowing, not to the architect key, so #1463 ships with the fixed-4 behaviour documented and this issue carries the change.

Feasibility (verified against @elgato/streamdeck@2.1.0)

There is no profile-structure API — plugin/profiles.d.ts states verbatim that plugins "may only switch to profiles distributed with the plugin, as defined within the manifest, and cannot access user-defined profiles", and switchToProfile is the only profile call. So the layout cannot be read directly.

It can be derived from the lifecycle, which is the workable path: every visible BuilderAction fires willAppear carrying KeyAction.coordinates ({column, row}) and its device, and streamDeck.devices exposes Device.size ({columns, rows}) and type.

Proposed shape

  • Window size = the number of currently visible BuilderAction instances, replacing the constant.
  • Slot order derived by sorting visible instances by (row, column), which could retire the manual slot PI field or keep it as an override.
  • Recompute on willAppear / willDisappear, debounced so page-load settling does not thrash, and skipping instances whose coordinates are undefined (multi-action).
  • Semantics are per visible page, which is correct: the window should match what is on screen.

Scope

store.ts (window math), actions.ts (BuilderAction lifecycle and slot resolution), builder-action.html (PI), cursor paging, and tests. Pairs naturally with the larger-profile thread (#1381). Protocol: PIR — the payoff is on hardware, across different key counts and at least one non-SD+ device if available.

Credit

Researched and proposed by the pir-1463 builder, surfaced by the owner during that lane's plan-approval discussion; SDK claims and the windowing math independently verified against source before filing.

Activity

  1. added
    area/streamdeckStream Deck plugin — apps/streamdeck, Elgato channel (profiles, Maker Console, packaging)
    on Aug 15, 2026
  2. added a commit that references this issue on Aug 15, 2026
  3. amrmelsayed commented on Aug 15, 2026

    @amrmelsayed
    CollaboratorAuthor

    On it! Working on this with the PIR protocol (plan + dev-approval gates before PR).

  4. added 13 commits that reference this issue on Aug 15, 2026
  5. added 2 commits that reference this issue on Aug 15, 2026
  6. amrmelsayed commented on Aug 15, 2026

    @amrmelsayed
    CollaboratorAuthor

    Shipping record: PR #1468 merged 2026-08-15T10:58:40Z (47477ca76), seven checks green against a stationary head.

    What shipped: the Row 1 selector window now sizes itself from the Builder Action keys actually placed, ordered left-to-right and top-row-first from the coordinates each key reports, retiring the manual slot field. The owner-directed README layout change rides with it — Open Architect Terminal anchored in slot 1, the builder window filling whatever remains — which is only correct because of this fix.

    Why the two belonged in one lane: a three-key Row 1 against the old hardcoded page-of-four is exactly the defect. Documenting that layout first would have published a recommended board that hides every builder at index ≡ 3 (mod 4) — selected, driving the action row and both dials, and lit nowhere.

    Consultation returned gemini APPROVE with codex and claude both REQUEST_CHANGES. All three findings were real, all were documentation or coverage rather than the core change, and all were fixed on-branch and then verified at source by the architect (PIR is single-pass, so post-verdict fixes get no independent re-review).

    One of those "documentation" findings deserves to be read as more than a nit, and it is the reason this record exists: the README's Open Architect Terminal bullet still carried the fixed-4 caveat shipped with #1463 — text that was true when written and made false by this very lane. Left in, it would have shipped actively wrong guidance alongside the fix that invalidated it. Neither reviewer had reason to distrust the core change; consult caught it by re-reading what had already been approved in the light of what changed since, which is precisely the job a single-pass protocol cannot do for itself. The same shape has now been observed three times this week from three directions (an approval invalidated by a later rename, a review artifact needing improvement after its gates were spent, and this), each caught by a different mechanism — so there is no reliable detector for it, only three lucky ones.

    Release notes: UNRELEASED entry added; no apps/vscode/CHANGELOG.md entry, since that file is the extension's own and this lane touches apps/streamdeck plus docs only.

  7. added a commit that references this issue on Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/streamdeckStream Deck plugin — apps/streamdeck, Elgato channel (profiles, Maker Console, packaging)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions