From 98bfcb50be5db59e20066e87a376fcc146cb1e96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 19:43:45 +0000 Subject: [PATCH 1/2] Bump @elgato/streamdeck from 2.1.2 to 3.0.0 Bumps [@elgato/streamdeck](https://github.com/elgatosf/streamdeck) from 2.1.2 to 3.0.0. - [Commits](https://github.com/elgatosf/streamdeck/compare/@elgato/streamdeck@2.1.2...@elgato/streamdeck@3.0.0) --- updated-dependencies: - dependency-name: "@elgato/streamdeck" dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 19 ++++++------------- package.json | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff0d34f..f7d8ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.2.4", "license": "MIT", "dependencies": { - "@elgato/streamdeck": "2.1.2" + "@elgato/streamdeck": "3.0.0" }, "devDependencies": { "@elgato/cli": "1.10.0", @@ -57,26 +57,19 @@ "node": ">=20.1.0" } }, - "node_modules/@elgato/cli/node_modules/@elgato/schemas": { + "node_modules/@elgato/schemas": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@elgato/schemas/-/schemas-0.5.0.tgz", "integrity": "sha512-Mltoh5L8M7cQuLIlDAsBq4IfdPrt0KMcwKCpl75OcgqILti7XBb/DlgxtULSnze/RXrH4iiwMbKdmMYeKn4RaQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@elgato/schemas": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/@elgato/schemas/-/schemas-0.4.15.tgz", - "integrity": "sha512-UpELAqazf52PFer+hqg/hvEhYoPHCH3R0af6P7Ih3Hk6xCCJENn7cA5TIpXAIGEWe6dIteHuo/e/iPGLUP7yXA==", "license": "MIT" }, "node_modules/@elgato/streamdeck": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@elgato/streamdeck/-/streamdeck-2.1.2.tgz", - "integrity": "sha512-qj6NuDyYj8bE+XFnr+O5ynB1zojwugfWiK3oQYwweaOzOFs7fEE21v9z75EPC8wQr3cuuzI7m557TQLn5k/4mA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@elgato/streamdeck/-/streamdeck-3.0.0.tgz", + "integrity": "sha512-Y7vfZH8YB442+iYleey/lNF4WRY8y5OKlY3e0FxKlDDl9Ztc9NLSwiwIy6PZRK87NrJVeq7IXoFxH6PkcZQLkA==", "license": "MIT", "dependencies": { - "@elgato/schemas": "^0.4.14", + "@elgato/schemas": "^0.5.0", "@elgato/utils": "0.6.0", "ws": "^8.19.0" }, diff --git a/package.json b/package.json index 41d3587..e1ec96e 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "test:fast": "vitest run --project unit" }, "dependencies": { - "@elgato/streamdeck": "2.1.2" + "@elgato/streamdeck": "3.0.0" }, "devDependencies": { "@elgato/cli": "1.10.0", From 1e591a124001c6d7fae41394a8994cec2b68e147 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 28 Sep 2026 19:44:22 +0000 Subject: [PATCH 2/2] Adapt plugin actions to Stream Deck SDK v3 types. showAlert is now only on keys and dials, Action requires a settings type argument, and getSettings is no longer generic. Keep the 3.0.0 bump. Co-authored-by: Todd Dailey --- src/actions/agent-navigator.ts | 3 ++- src/actions/agent-status.ts | 3 +-- src/actions/command.ts | 3 ++- src/actions/context.ts | 10 ++++++---- src/actions/health.ts | 10 ++++++---- src/actions/keycap.ts | 4 ++-- src/actions/model.ts | 5 +++-- src/actions/reasoning.ts | 5 +++-- src/actions/usage.ts | 10 ++++++---- src/actions/workflow.ts | 3 ++- src/lib/streamdeck-alert.ts | 16 ++++++++++++++++ test/helpers/fake-streamdeck-action.ts | 7 +++++-- 12 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 src/lib/streamdeck-alert.ts diff --git a/src/actions/agent-navigator.ts b/src/actions/agent-navigator.ts index 99c76de..e3ed84a 100644 --- a/src/actions/agent-navigator.ts +++ b/src/actions/agent-navigator.ts @@ -10,6 +10,7 @@ import { openNewChat, openThread } from "../lib/automation.js"; import { codexStore } from "../lib/codex-store.js"; import { statusIndicator } from "../lib/visuals.js"; import { renderFeedback } from "../lib/render-cache.js"; +import { showActionAlert } from "../lib/streamdeck-alert.js"; type NavigatorSettings = { index?: number; @@ -70,7 +71,7 @@ export class AgentNavigatorAction extends SingletonAction { } if (actionInstance.isDial()) await this.draw(actionInstance); } catch { - await actionInstance.showAlert(); + await showActionAlert(actionInstance); } } diff --git a/src/actions/agent-status.ts b/src/actions/agent-status.ts index 20fbcbe..79fb8da 100644 --- a/src/actions/agent-status.ts +++ b/src/actions/agent-status.ts @@ -59,8 +59,7 @@ export class AgentStatusAction extends SingletonAction { receivedSettings?: AgentSettings, ): Promise { if (!actionInstance.isKey()) return; - const settings = - receivedSettings ?? (await actionInstance.getSettings()); + const settings = receivedSettings ?? (await actionInstance.getSettings()); const slot = this.slotFrom( settings, (actionInstance.coordinates?.column ?? 0) + 1, diff --git a/src/actions/command.ts b/src/actions/command.ts index ff77b0c..9619191 100644 --- a/src/actions/command.ts +++ b/src/actions/command.ts @@ -31,6 +31,7 @@ import { import { codexStore } from "../lib/codex-store.js"; import { pickerFailureLabel } from "../lib/codex-ui-control.js"; import { renderFeedback, renderKey } from "../lib/render-cache.js"; +import { showActionAlert } from "../lib/streamdeck-alert.js"; type CommandSettings = { commandId?: string; @@ -179,7 +180,7 @@ export class CommandAction extends SingletonAction { title: pickerFailureLabel(error), }); } - await actionInstance.showAlert(); + await showActionAlert(actionInstance); } } diff --git a/src/actions/context.ts b/src/actions/context.ts index 0ba39b9..ae2a710 100644 --- a/src/actions/context.ts +++ b/src/actions/context.ts @@ -10,17 +10,19 @@ import { toggleContextView, type ContextView } from "../lib/context.js"; import { contextKeySvg, svgDataUrl } from "../lib/visuals.js"; import { renderKey } from "../lib/render-cache.js"; +type ContextSettings = Record; + @action({ UUID: "com.todd.streamdeckcodex.context" }) -export class ContextAction extends SingletonAction { +export class ContextAction extends SingletonAction { readonly #mode = new Map(); - async onWillAppear(event: WillAppearEvent): Promise { + async onWillAppear(event: WillAppearEvent): Promise { if (!event.action.isKey()) return; this.#mode.set(event.action.id, "remaining"); await this.draw(event.action); } - async onKeyDown(event: KeyDownEvent): Promise { + async onKeyDown(event: KeyDownEvent): Promise { const current = this.#mode.get(event.action.id) ?? "remaining"; this.#mode.set(event.action.id, toggleContextView(current)); await this.draw(event.action); @@ -34,7 +36,7 @@ export class ContextAction extends SingletonAction { ); } - private async draw(actionInstance: Action): Promise { + private async draw(actionInstance: Action): Promise { if (!actionInstance.isKey()) return; await renderKey( actionInstance, diff --git a/src/actions/health.ts b/src/actions/health.ts index d7a0a55..072a3b3 100644 --- a/src/actions/health.ts +++ b/src/actions/health.ts @@ -15,17 +15,19 @@ import { import { renderKey } from "../lib/render-cache.js"; import { healthKeySvg, svgDataUrl } from "../lib/visuals.js"; +type HealthSettings = Record; + @action({ UUID: "com.todd.streamdeckcodex.health" }) -export class HealthAction extends SingletonAction { +export class HealthAction extends SingletonAction { readonly #selection = new Map(); - async onWillAppear(event: WillAppearEvent): Promise { + async onWillAppear(event: WillAppearEvent): Promise { if (!event.action.isKey()) return; this.#selection.set(event.action.id, 0); await this.draw(event.action); } - async onKeyDown(event: KeyDownEvent): Promise { + async onKeyDown(event: KeyDownEvent): Promise { const next = ((this.#selection.get(event.action.id) ?? 0) + 1) % HEALTH_COMPONENTS.length; @@ -41,7 +43,7 @@ export class HealthAction extends SingletonAction { ); } - private async draw(actionInstance: Action): Promise { + private async draw(actionInstance: Action): Promise { if (!actionInstance.isKey()) return; const snapshot = collectHealth(codexStore); const component = HEALTH_COMPONENTS[ diff --git a/src/actions/keycap.ts b/src/actions/keycap.ts index 5e6339a..a2d819e 100644 --- a/src/actions/keycap.ts +++ b/src/actions/keycap.ts @@ -38,7 +38,7 @@ export class KeycapAction extends SingletonAction { } async onKeyDown(event: KeyDownEvent): Promise { - const settings = await event.action.getSettings(); + const settings = await event.action.getSettings(); const actionKind = settings.action ?? "info"; try { if (actionKind === "new-chat") await openNewChat(); @@ -83,7 +83,7 @@ export class KeycapAction extends SingletonAction { private async draw(actionInstance: Action): Promise { if (!actionInstance.isKey()) return; - const settings = await actionInstance.getSettings(); + const settings = await actionInstance.getSettings(); await renderKey( actionInstance, svgDataUrl( diff --git a/src/actions/model.ts b/src/actions/model.ts index 9aa99ba..eb739e2 100644 --- a/src/actions/model.ts +++ b/src/actions/model.ts @@ -16,6 +16,7 @@ import { type ModelDialState, } from "../lib/model.js"; import { renderFeedback } from "../lib/render-cache.js"; +import { showActionAlert } from "../lib/streamdeck-alert.js"; import { dialFailureFeedback } from "../lib/visuals.js"; type ModelSettings = { @@ -115,7 +116,7 @@ export class ModelAction extends SingletonAction { streamDeck.logger.warn( `Ignored unavailable model ${current.selected || "(none)"}`, ); - await actionInstance.showAlert(); + await showActionAlert(actionInstance); return; } @@ -158,7 +159,7 @@ export class ModelAction extends SingletonAction { dialFailureFeedback(pickerFailureLabel(error)), ); } - await actionInstance.showAlert(); + await showActionAlert(actionInstance); } } diff --git a/src/actions/reasoning.ts b/src/actions/reasoning.ts index 3a88d77..7c2db20 100644 --- a/src/actions/reasoning.ts +++ b/src/actions/reasoning.ts @@ -17,6 +17,7 @@ import { type ReasoningDialState, } from "../lib/reasoning.js"; import { renderFeedback } from "../lib/render-cache.js"; +import { showActionAlert } from "../lib/streamdeck-alert.js"; import { dialFailureFeedback } from "../lib/visuals.js"; type ReasoningSettings = { @@ -121,7 +122,7 @@ export class ReasoningAction extends SingletonAction { streamDeck.logger.warn( `Ignored unsupported reasoning level ${current.selected} for ${snapshot.model ?? "current model"}`, ); - await actionInstance.showAlert(); + await showActionAlert(actionInstance); return; } await this.apply(context, actionInstance, current.selected); @@ -176,7 +177,7 @@ export class ReasoningAction extends SingletonAction { dialFailureFeedback(pickerFailureLabel(error)), ); } - await actionInstance.showAlert(); + await showActionAlert(actionInstance); } } diff --git a/src/actions/usage.ts b/src/actions/usage.ts index c934002..daa627c 100644 --- a/src/actions/usage.ts +++ b/src/actions/usage.ts @@ -10,17 +10,19 @@ import { toggleUsageView, type UsageView } from "../lib/usage.js"; import { svgDataUrl, usageKeySvg } from "../lib/visuals.js"; import { renderKey } from "../lib/render-cache.js"; +type UsageSettings = Record; + @action({ UUID: "com.todd.streamdeckcodex.usage" }) -export class UsageAction extends SingletonAction { +export class UsageAction extends SingletonAction { readonly #mode = new Map(); - async onWillAppear(event: WillAppearEvent): Promise { + async onWillAppear(event: WillAppearEvent): Promise { if (!event.action.isKey()) return; this.#mode.set(event.action.id, "weekly"); await this.draw(event.action); } - async onKeyDown(event: KeyDownEvent): Promise { + async onKeyDown(event: KeyDownEvent): Promise { const current = this.#mode.get(event.action.id) ?? "weekly"; this.#mode.set(event.action.id, toggleUsageView(current)); await this.draw(event.action); @@ -34,7 +36,7 @@ export class UsageAction extends SingletonAction { ); } - private async draw(actionInstance: Action): Promise { + private async draw(actionInstance: Action): Promise { if (!actionInstance.isKey()) return; const snapshot = codexStore.usageSnapshotCached(); await renderKey( diff --git a/src/actions/workflow.ts b/src/actions/workflow.ts index d0334c0..bd06388 100644 --- a/src/actions/workflow.ts +++ b/src/actions/workflow.ts @@ -12,6 +12,7 @@ import { launchWorkflow } from "../lib/automation.js"; import { codexStore } from "../lib/codex-store.js"; import { commandKeySvg, svgDataUrl } from "../lib/visuals.js"; import { renderFeedback, renderKey } from "../lib/render-cache.js"; +import { showActionAlert } from "../lib/streamdeck-alert.js"; import { workflowAt, WORKFLOWS, @@ -106,7 +107,7 @@ export class WorkflowAction extends SingletonAction { ); if (actionInstance.isKey()) await actionInstance.showOk(); } catch { - await actionInstance.showAlert(); + await showActionAlert(actionInstance); } } diff --git a/src/lib/streamdeck-alert.ts b/src/lib/streamdeck-alert.ts new file mode 100644 index 0000000..1593d31 --- /dev/null +++ b/src/lib/streamdeck-alert.ts @@ -0,0 +1,16 @@ +import type { Action } from "@elgato/streamdeck"; + +type JsonObject = { + [key: string]: JsonValue; +}; +type JsonValue = + boolean | number | string | null | undefined | JsonObject | JsonValue[]; + +/** + * SDK v3 keeps `showAlert` on keys and dials only; Neo Infobar actions omit it. + */ +export async function showActionAlert( + action: Action, +): Promise { + if (action.isKey() || action.isDial()) await action.showAlert(); +} diff --git a/test/helpers/fake-streamdeck-action.ts b/test/helpers/fake-streamdeck-action.ts index 35473b4..f0adea4 100644 --- a/test/helpers/fake-streamdeck-action.ts +++ b/test/helpers/fake-streamdeck-action.ts @@ -17,9 +17,12 @@ export class FakeStreamDeckAction< isDial(): boolean { return this.kind === "dial"; } - async getSettings(): Promise { + isNeoInfobar(): boolean { + return false; + } + async getSettings(): Promise { this.calls.push({ method: "getSettings" }); - return this.settings as unknown as U; + return this.settings; } async setSettings(value: T): Promise { this.settings = value;