@fcannizzaro/streamdeck-react 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/dist/action.js +0 -1
- package/dist/adapter/index.d.ts +2 -0
- package/dist/adapter/physical-device.d.ts +2 -0
- package/dist/adapter/physical-device.js +153 -0
- package/dist/adapter/types.d.ts +127 -0
- package/dist/bundler-shared.d.ts +10 -0
- package/dist/bundler-shared.js +28 -1
- package/dist/devtools/bridge.d.ts +2 -2
- package/dist/devtools/bridge.js +7 -8
- package/dist/devtools/highlight.d.ts +1 -2
- package/dist/devtools/highlight.js +4 -3
- package/dist/devtools/types.d.ts +5 -5
- package/dist/font-inline.js +1 -1
- package/dist/google-font.d.ts +61 -0
- package/dist/google-font.js +124 -0
- package/dist/hooks/animation.d.ts +1 -1
- package/dist/hooks/animation.js +2 -2
- package/dist/hooks/events.js +1 -1
- package/dist/hooks/sdk.js +11 -11
- package/dist/hooks/utility.js +3 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/plugin.js +102 -124
- package/dist/reconciler/vnode.d.ts +0 -2
- package/dist/reconciler/vnode.js +0 -1
- package/dist/render/cache.d.ts +5 -17
- package/dist/render/cache.js +7 -29
- package/dist/render/image-cache.d.ts +8 -7
- package/dist/render/image-cache.js +33 -17
- package/dist/render/metrics.d.ts +9 -10
- package/dist/render/metrics.js +36 -39
- package/dist/render/pipeline.d.ts +4 -14
- package/dist/render/pipeline.js +47 -111
- package/dist/render/png.d.ts +0 -9
- package/dist/render/png.js +5 -8
- package/dist/render/render-pool.d.ts +0 -2
- package/dist/render/render-pool.js +1 -12
- package/dist/rollup.d.ts +1 -1
- package/dist/rollup.js +3 -1
- package/dist/roots/registry.d.ts +5 -9
- package/dist/roots/registry.js +30 -47
- package/dist/roots/root.d.ts +7 -34
- package/dist/roots/root.js +23 -90
- package/dist/roots/touchstrip-root.d.ts +6 -32
- package/dist/roots/touchstrip-root.js +61 -181
- package/dist/types.d.ts +38 -20
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +3 -1
- package/package.json +14 -8
- package/dist/node_modules/.bun/xxhash-wasm@1.1.0/node_modules/xxhash-wasm/esm/xxhash-wasm.js +0 -3157
- package/dist/roots/flush-coordinator.d.ts +0 -18
- package/dist/roots/flush-coordinator.js +0 -38
package/README.md
CHANGED
|
@@ -63,11 +63,13 @@ export const counterAction = defineAction({
|
|
|
63
63
|
Register it in your plugin entrypoint:
|
|
64
64
|
|
|
65
65
|
```ts
|
|
66
|
-
import { createPlugin } from "@fcannizzaro/streamdeck-react";
|
|
66
|
+
import { createPlugin, googleFont } from "@fcannizzaro/streamdeck-react";
|
|
67
67
|
import { counterAction } from "./actions/counter.tsx";
|
|
68
68
|
|
|
69
|
+
const inter = await googleFont("Inter");
|
|
70
|
+
|
|
69
71
|
const plugin = createPlugin({
|
|
70
|
-
fonts: [],
|
|
72
|
+
fonts: [inter],
|
|
71
73
|
actions: [counterAction],
|
|
72
74
|
});
|
|
73
75
|
|
|
@@ -83,12 +85,13 @@ await plugin.connect();
|
|
|
83
85
|
|
|
84
86
|
## Samples
|
|
85
87
|
|
|
86
|
-
- `samples/counter/`
|
|
87
|
-
- `samples/zustand/`
|
|
88
|
-
- `samples/jotai/`
|
|
89
|
-
- `samples/pokemon/`
|
|
90
|
-
- `samples/
|
|
91
|
-
- `samples/
|
|
88
|
+
- `samples/counter/` — local state, persisted settings, dial interaction
|
|
89
|
+
- `samples/zustand/` — shared state across keys via a module-scope Zustand store
|
|
90
|
+
- `samples/jotai/` — shared atom state with a plugin-level Jotai Provider wrapper
|
|
91
|
+
- `samples/pokemon/` — data fetching with TanStack Query and remote image rendering
|
|
92
|
+
- `samples/animation/` — spring bounce, fade-slide, and spring dial animations
|
|
93
|
+
- `samples/snake/` — snake game on the Stream Deck+ TouchStrip using dial controls and touch tap
|
|
94
|
+
- `samples/weather/` — weather forecast dials with animated detail panels and a shared Zustand store
|
|
92
95
|
|
|
93
96
|
## DevTools
|
|
94
97
|
|
package/dist/action.js
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { StreamDeckAdapter, AdapterActionHandle, AdapterActionCallbacks, AdapterWillAppearEvent, AdapterActionDevice, AdapterController, AdapterCoordinates, AdapterSize, AdapterTriggerDescription, AdapterKeyDownPayload, AdapterKeyUpPayload, AdapterDialRotatePayload, AdapterDialPressPayload, AdapterTouchTapPayload, } from './types';
|
|
2
|
+
export { physicalDevice } from './physical-device';
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import streamDeck, { SingletonAction } from "@elgato/streamdeck";
|
|
2
|
+
//#region src/adapter/physical-device.ts
|
|
3
|
+
function wrapSdkAction(action) {
|
|
4
|
+
return {
|
|
5
|
+
id: action.id,
|
|
6
|
+
device: {
|
|
7
|
+
id: action.device.id,
|
|
8
|
+
type: action.device.type,
|
|
9
|
+
size: action.device.size,
|
|
10
|
+
name: action.device.name
|
|
11
|
+
},
|
|
12
|
+
controllerType: action.controllerType,
|
|
13
|
+
coordinates: "coordinates" in action ? action.coordinates : void 0,
|
|
14
|
+
async setImage(dataUri) {
|
|
15
|
+
if ("setImage" in action) await action.setImage(dataUri);
|
|
16
|
+
},
|
|
17
|
+
async setTitle(title) {
|
|
18
|
+
if ("setTitle" in action) await action.setTitle(title);
|
|
19
|
+
},
|
|
20
|
+
async showOk() {
|
|
21
|
+
if ("showOk" in action) await action.showOk();
|
|
22
|
+
},
|
|
23
|
+
async showAlert() {
|
|
24
|
+
await action.showAlert();
|
|
25
|
+
},
|
|
26
|
+
async setSettings(settings) {
|
|
27
|
+
await action.setSettings(settings);
|
|
28
|
+
},
|
|
29
|
+
async setFeedback(payload) {
|
|
30
|
+
if ("setFeedback" in action) await action.setFeedback(payload);
|
|
31
|
+
},
|
|
32
|
+
async setFeedbackLayout(layout) {
|
|
33
|
+
if ("setFeedbackLayout" in action) await action.setFeedbackLayout(layout);
|
|
34
|
+
},
|
|
35
|
+
async setTriggerDescription(hints) {
|
|
36
|
+
if ("setTriggerDescription" in action) await action.setTriggerDescription({
|
|
37
|
+
rotate: hints.rotate,
|
|
38
|
+
push: hints.push,
|
|
39
|
+
touch: hints.touch,
|
|
40
|
+
longTouch: hints.longTouch
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function physicalDevice() {
|
|
46
|
+
return {
|
|
47
|
+
pluginUUID: streamDeck.info.plugin.uuid,
|
|
48
|
+
async connect() {
|
|
49
|
+
await streamDeck.connect();
|
|
50
|
+
},
|
|
51
|
+
async getGlobalSettings() {
|
|
52
|
+
return streamDeck.settings.getGlobalSettings();
|
|
53
|
+
},
|
|
54
|
+
async setGlobalSettings(settings) {
|
|
55
|
+
await streamDeck.settings.setGlobalSettings(settings);
|
|
56
|
+
},
|
|
57
|
+
onGlobalSettingsChanged(callback) {
|
|
58
|
+
streamDeck.settings.onDidReceiveGlobalSettings((ev) => {
|
|
59
|
+
callback(ev.settings);
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
registerAction(uuid, callbacks) {
|
|
63
|
+
const singletonAction = new class extends SingletonAction {
|
|
64
|
+
manifestId = uuid;
|
|
65
|
+
onWillAppear(ev) {
|
|
66
|
+
callbacks.onWillAppear({
|
|
67
|
+
action: wrapSdkAction(ev.action),
|
|
68
|
+
payload: {
|
|
69
|
+
settings: ev.payload.settings,
|
|
70
|
+
controller: ev.payload.controller,
|
|
71
|
+
isInMultiAction: ev.payload.isInMultiAction
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
onWillDisappear(ev) {
|
|
76
|
+
callbacks.onWillDisappear(ev.action.id);
|
|
77
|
+
}
|
|
78
|
+
onKeyDown(ev) {
|
|
79
|
+
callbacks.onKeyDown(ev.action.id, {
|
|
80
|
+
settings: ev.payload.settings,
|
|
81
|
+
isInMultiAction: ev.payload.isInMultiAction,
|
|
82
|
+
state: ev.payload.state,
|
|
83
|
+
userDesiredState: "userDesiredState" in ev.payload ? ev.payload.userDesiredState : void 0
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
onKeyUp(ev) {
|
|
87
|
+
callbacks.onKeyUp(ev.action.id, {
|
|
88
|
+
settings: ev.payload.settings,
|
|
89
|
+
isInMultiAction: ev.payload.isInMultiAction,
|
|
90
|
+
state: ev.payload.state,
|
|
91
|
+
userDesiredState: "userDesiredState" in ev.payload ? ev.payload.userDesiredState : void 0
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
onDialRotate(ev) {
|
|
95
|
+
callbacks.onDialRotate(ev.action.id, {
|
|
96
|
+
ticks: ev.payload.ticks,
|
|
97
|
+
pressed: ev.payload.pressed,
|
|
98
|
+
settings: ev.payload.settings
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
onDialDown(ev) {
|
|
102
|
+
callbacks.onDialDown(ev.action.id, {
|
|
103
|
+
settings: ev.payload.settings,
|
|
104
|
+
controller: "Encoder"
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
onDialUp(ev) {
|
|
108
|
+
callbacks.onDialUp(ev.action.id, {
|
|
109
|
+
settings: ev.payload.settings,
|
|
110
|
+
controller: "Encoder"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
onTouchTap(ev) {
|
|
114
|
+
callbacks.onTouchTap(ev.action.id, {
|
|
115
|
+
tapPos: ev.payload.tapPos,
|
|
116
|
+
hold: ev.payload.hold,
|
|
117
|
+
settings: ev.payload.settings
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
onDidReceiveSettings(ev) {
|
|
121
|
+
callbacks.onDidReceiveSettings(ev.action.id, ev.payload.settings);
|
|
122
|
+
}
|
|
123
|
+
onSendToPlugin(ev) {
|
|
124
|
+
callbacks.onSendToPlugin(ev.action.id, ev.payload);
|
|
125
|
+
}
|
|
126
|
+
onPropertyInspectorDidAppear(ev) {
|
|
127
|
+
callbacks.onPropertyInspectorDidAppear(ev.action.id);
|
|
128
|
+
}
|
|
129
|
+
onPropertyInspectorDidDisappear(ev) {
|
|
130
|
+
callbacks.onPropertyInspectorDidDisappear(ev.action.id);
|
|
131
|
+
}
|
|
132
|
+
onTitleParametersDidChange(ev) {
|
|
133
|
+
callbacks.onTitleParametersDidChange(ev.action.id, {
|
|
134
|
+
title: ev.payload.title,
|
|
135
|
+
settings: ev.payload.settings
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}();
|
|
139
|
+
streamDeck.actions.registerAction(singletonAction);
|
|
140
|
+
},
|
|
141
|
+
async openUrl(url) {
|
|
142
|
+
await streamDeck.system.openUrl(url);
|
|
143
|
+
},
|
|
144
|
+
async switchToProfile(deviceId, profile) {
|
|
145
|
+
await streamDeck.profiles.switchToProfile(deviceId, profile);
|
|
146
|
+
},
|
|
147
|
+
async sendToPropertyInspector(payload) {
|
|
148
|
+
await streamDeck.ui.sendToPropertyInspector(payload);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { physicalDevice };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { JsonObject, JsonValue } from '@elgato/utils';
|
|
2
|
+
/** Controller surface type. Matches SDK Controller values exactly. */
|
|
3
|
+
export type AdapterController = "Keypad" | "Encoder";
|
|
4
|
+
/** Grid coordinates for a key or encoder on a device. */
|
|
5
|
+
export interface AdapterCoordinates {
|
|
6
|
+
readonly column: number;
|
|
7
|
+
readonly row: number;
|
|
8
|
+
}
|
|
9
|
+
/** Device grid size (number of key columns and rows). */
|
|
10
|
+
export interface AdapterSize {
|
|
11
|
+
readonly columns: number;
|
|
12
|
+
readonly rows: number;
|
|
13
|
+
}
|
|
14
|
+
/** Hint labels for dial/encoder trigger zones shown on the Stream Deck LCD. */
|
|
15
|
+
export interface AdapterTriggerDescription {
|
|
16
|
+
rotate?: string;
|
|
17
|
+
push?: string;
|
|
18
|
+
touch?: string;
|
|
19
|
+
longTouch?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AdapterActionHandle {
|
|
22
|
+
readonly id: string;
|
|
23
|
+
readonly device: AdapterActionDevice;
|
|
24
|
+
readonly controllerType: AdapterController;
|
|
25
|
+
readonly coordinates?: AdapterCoordinates;
|
|
26
|
+
/** Push a rendered data URI to the key display. No-op on encoder surfaces. */
|
|
27
|
+
setImage(dataUri: string): Promise<void>;
|
|
28
|
+
/** Set the key title overlay. No-op on encoder surfaces. */
|
|
29
|
+
setTitle(title: string): Promise<void>;
|
|
30
|
+
/** Flash the OK checkmark on the key. No-op on encoder surfaces. */
|
|
31
|
+
showOk(): Promise<void>;
|
|
32
|
+
/** Flash the alert triangle on the action. */
|
|
33
|
+
showAlert(): Promise<void>;
|
|
34
|
+
/** Persist action settings to the Stream Deck. */
|
|
35
|
+
setSettings(settings: JsonObject): Promise<void>;
|
|
36
|
+
/** Push dial/touchstrip feedback payload. No-op on key surfaces. */
|
|
37
|
+
setFeedback(payload: Record<string, unknown>): Promise<void>;
|
|
38
|
+
/** Set the encoder feedback layout (JSON or layout ID). No-op on key surfaces. */
|
|
39
|
+
setFeedbackLayout(layout: string | Record<string, unknown>): Promise<void>;
|
|
40
|
+
/** Set dial hint text (rotate, push, touch labels). No-op on key surfaces. */
|
|
41
|
+
setTriggerDescription(hints: AdapterTriggerDescription): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
/** Device info as seen from an action event. */
|
|
44
|
+
export interface AdapterActionDevice {
|
|
45
|
+
readonly id: string;
|
|
46
|
+
/**
|
|
47
|
+
* Numeric device type matching Elgato DeviceType enum values.
|
|
48
|
+
* Using `number` instead of the SDK's enum avoids coupling to specific
|
|
49
|
+
* device models while remaining compatible with the KEY_SIZES lookup
|
|
50
|
+
* table in registry.ts.
|
|
51
|
+
*/
|
|
52
|
+
readonly type: number;
|
|
53
|
+
readonly size: AdapterSize;
|
|
54
|
+
readonly name: string;
|
|
55
|
+
}
|
|
56
|
+
/** Event payload provided to onWillAppear callbacks. */
|
|
57
|
+
export interface AdapterWillAppearEvent {
|
|
58
|
+
action: AdapterActionHandle;
|
|
59
|
+
payload: {
|
|
60
|
+
settings: JsonObject;
|
|
61
|
+
controller: AdapterController;
|
|
62
|
+
isInMultiAction: boolean;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export interface AdapterKeyDownPayload {
|
|
66
|
+
settings: JsonObject;
|
|
67
|
+
isInMultiAction: boolean;
|
|
68
|
+
state?: number;
|
|
69
|
+
userDesiredState?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface AdapterKeyUpPayload {
|
|
72
|
+
settings: JsonObject;
|
|
73
|
+
isInMultiAction: boolean;
|
|
74
|
+
state?: number;
|
|
75
|
+
userDesiredState?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface AdapterDialRotatePayload {
|
|
78
|
+
ticks: number;
|
|
79
|
+
pressed: boolean;
|
|
80
|
+
settings: JsonObject;
|
|
81
|
+
}
|
|
82
|
+
export interface AdapterDialPressPayload {
|
|
83
|
+
settings: JsonObject;
|
|
84
|
+
controller: "Encoder";
|
|
85
|
+
}
|
|
86
|
+
export interface AdapterTouchTapPayload {
|
|
87
|
+
tapPos: [x: number, y: number];
|
|
88
|
+
hold: boolean;
|
|
89
|
+
settings: JsonObject;
|
|
90
|
+
}
|
|
91
|
+
export interface AdapterActionCallbacks {
|
|
92
|
+
onWillAppear(ev: AdapterWillAppearEvent): void;
|
|
93
|
+
onWillDisappear(actionId: string): void;
|
|
94
|
+
onKeyDown(actionId: string, payload: AdapterKeyDownPayload): void;
|
|
95
|
+
onKeyUp(actionId: string, payload: AdapterKeyUpPayload): void;
|
|
96
|
+
onDialRotate(actionId: string, payload: AdapterDialRotatePayload): void;
|
|
97
|
+
onDialDown(actionId: string, payload: AdapterDialPressPayload): void;
|
|
98
|
+
onDialUp(actionId: string, payload: AdapterDialPressPayload): void;
|
|
99
|
+
onTouchTap(actionId: string, payload: AdapterTouchTapPayload): void;
|
|
100
|
+
onDidReceiveSettings(actionId: string, settings: JsonObject): void;
|
|
101
|
+
onSendToPlugin(actionId: string, payload: JsonValue): void;
|
|
102
|
+
onPropertyInspectorDidAppear(actionId: string): void;
|
|
103
|
+
onPropertyInspectorDidDisappear(actionId: string): void;
|
|
104
|
+
onTitleParametersDidChange(actionId: string, payload: {
|
|
105
|
+
title: string;
|
|
106
|
+
settings: JsonObject;
|
|
107
|
+
}): void;
|
|
108
|
+
}
|
|
109
|
+
export interface StreamDeckAdapter {
|
|
110
|
+
/** Plugin UUID, used for devtools identification. */
|
|
111
|
+
readonly pluginUUID: string;
|
|
112
|
+
/** Initialize the adapter and connect to the backend. */
|
|
113
|
+
connect(): Promise<void>;
|
|
114
|
+
/** Retrieve plugin-wide global settings. */
|
|
115
|
+
getGlobalSettings<T extends JsonObject = JsonObject>(): Promise<T>;
|
|
116
|
+
/** Persist plugin-wide global settings. */
|
|
117
|
+
setGlobalSettings<T extends JsonObject = JsonObject>(settings: T): Promise<void>;
|
|
118
|
+
/** Subscribe to external global settings changes (e.g. from Property Inspector). */
|
|
119
|
+
onGlobalSettingsChanged(callback: (settings: JsonObject) => void): void;
|
|
120
|
+
registerAction(uuid: string, callbacks: AdapterActionCallbacks): void;
|
|
121
|
+
/** Open a URL in the user's default browser. */
|
|
122
|
+
openUrl(url: string): Promise<void>;
|
|
123
|
+
/** Switch the active Stream Deck profile. */
|
|
124
|
+
switchToProfile(deviceId: string, profile: string): Promise<void>;
|
|
125
|
+
/** Send a payload to the Property Inspector. */
|
|
126
|
+
sendToPropertyInspector(payload: JsonValue): Promise<void>;
|
|
127
|
+
}
|
package/dist/bundler-shared.d.ts
CHANGED
|
@@ -4,8 +4,16 @@ export interface StreamDeckTarget {
|
|
|
4
4
|
platform: StreamDeckPlatform;
|
|
5
5
|
arch: StreamDeckArch;
|
|
6
6
|
}
|
|
7
|
+
/** Takumi renderer backend selection. Mirrors the runtime `TakumiBackend` type for build-time configuration. */
|
|
8
|
+
export type TakumiBackend = "native-binding" | "wasm";
|
|
7
9
|
export interface StreamDeckTargetOptions {
|
|
8
10
|
targets?: StreamDeckTarget[];
|
|
11
|
+
/**
|
|
12
|
+
* Takumi renderer backend. When `"wasm"`, native `.node` binding
|
|
13
|
+
* copying is skipped entirely during the build.
|
|
14
|
+
* @default "native-binding"
|
|
15
|
+
*/
|
|
16
|
+
takumi?: TakumiBackend;
|
|
9
17
|
}
|
|
10
18
|
export interface ResolvedTarget extends StreamDeckTarget {
|
|
11
19
|
pkg: string;
|
|
@@ -17,6 +25,8 @@ export declare function isArch(value: string): value is StreamDeckArch;
|
|
|
17
25
|
export declare function isDevelopmentMode(watchMode: boolean | undefined): boolean;
|
|
18
26
|
export declare const NOOP_DEVTOOLS_ID = "\0streamdeck-react:noop-devtools";
|
|
19
27
|
export declare const NOOP_DEVTOOLS_CODE = "export function startDevtoolsServer() {}";
|
|
28
|
+
export declare const TAKUMI_NATIVE_LOADER_ID = "\0streamdeck-react:takumi-native";
|
|
29
|
+
export declare const TAKUMI_NATIVE_LOADER_CODE: string;
|
|
20
30
|
/**
|
|
21
31
|
* Returns true when devtools should be stripped from the bundle.
|
|
22
32
|
* Only strips in explicit production mode (NODE_ENV=production) to avoid
|
package/dist/bundler-shared.js
CHANGED
|
@@ -40,6 +40,32 @@ function isDevelopmentMode(watchMode) {
|
|
|
40
40
|
var NOOP_DEVTOOLS_ID = "\0streamdeck-react:noop-devtools";
|
|
41
41
|
var NOOP_DEVTOOLS_CODE = "export function startDevtoolsServer() {}";
|
|
42
42
|
var DEVTOOLS_IMPORT_SOURCE = "./devtools/index.js";
|
|
43
|
+
var TAKUMI_NATIVE_LOADER_ID = "\0streamdeck-react:takumi-native";
|
|
44
|
+
var TAKUMI_NATIVE_LOADER_CODE = `
|
|
45
|
+
import { createRequire } from "node:module";
|
|
46
|
+
const require = createRequire(import.meta.url);
|
|
47
|
+
let binding = null;
|
|
48
|
+
if (process.platform === "darwin") {
|
|
49
|
+
if (process.arch === "arm64") {
|
|
50
|
+
try { binding = require("./core.darwin-arm64.node"); } catch {}
|
|
51
|
+
} else if (process.arch === "x64") {
|
|
52
|
+
try { binding = require("./core.darwin-x64.node"); } catch {}
|
|
53
|
+
}
|
|
54
|
+
} else if (process.platform === "win32") {
|
|
55
|
+
if (process.arch === "arm64") {
|
|
56
|
+
try { binding = require("./core.win32-arm64-msvc.node"); } catch {}
|
|
57
|
+
} else if (process.arch === "x64") {
|
|
58
|
+
try { binding = require("./core.win32-x64-msvc.node"); } catch {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!binding) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"[@fcannizzaro/streamdeck-react] Failed to load @takumi-rs/core native binding for " +
|
|
64
|
+
process.platform + "-" + process.arch
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
export const { Renderer, OutputFormat, DitheringAlgorithm, AnimationOutputFormat, extractResourceUrls } = binding;
|
|
68
|
+
`.trim();
|
|
43
69
|
/**
|
|
44
70
|
* Returns true when devtools should be stripped from the bundle.
|
|
45
71
|
* Only strips in explicit production mode (NODE_ENV=production) to avoid
|
|
@@ -92,6 +118,7 @@ function expandTargets(targets) {
|
|
|
92
118
|
* In production: missing bindings throw an error (the plugin won't work).
|
|
93
119
|
*/
|
|
94
120
|
function copyNativeBindings(outDir, isDevelopment, options, warn) {
|
|
121
|
+
if (options.takumi === "wasm") return;
|
|
95
122
|
try {
|
|
96
123
|
const requestedTargets = normalizeTargetRequests(options, isDevelopment);
|
|
97
124
|
if (requestedTargets.length === 0) {
|
|
@@ -129,4 +156,4 @@ function copyNativeBindings(outDir, isDevelopment, options, warn) {
|
|
|
129
156
|
}
|
|
130
157
|
}
|
|
131
158
|
//#endregion
|
|
132
|
-
export { NOOP_DEVTOOLS_CODE, NOOP_DEVTOOLS_ID, copyNativeBindings, isDevelopmentMode, isLibraryDevtoolsImport, shouldStripDevtools };
|
|
159
|
+
export { NOOP_DEVTOOLS_CODE, NOOP_DEVTOOLS_ID, TAKUMI_NATIVE_LOADER_CODE, TAKUMI_NATIVE_LOADER_ID, copyNativeBindings, isDevelopmentMode, isLibraryDevtoolsImport, shouldStripDevtools };
|
|
@@ -63,7 +63,7 @@ export declare class DevtoolsBridge implements RegistryObserver {
|
|
|
63
63
|
private static readonly TB_PREFIX;
|
|
64
64
|
private handleHighlight;
|
|
65
65
|
/**
|
|
66
|
-
* Restore a highlighted action or
|
|
66
|
+
* Restore a highlighted action or TouchStrip to its normal state.
|
|
67
67
|
* Un-suppresses hardware pushes and restores the original image(s).
|
|
68
68
|
*/
|
|
69
69
|
private restoreHighlight;
|
|
@@ -73,7 +73,7 @@ export declare class DevtoolsBridge implements RegistryObserver {
|
|
|
73
73
|
private broadcastHighlightRender;
|
|
74
74
|
/**
|
|
75
75
|
* Clear highlight URIs for the given actionId.
|
|
76
|
-
* For
|
|
76
|
+
* For TouchStrip IDs, clears all per-segment keys (touchStrip:*:seg:N).
|
|
77
77
|
* For regular actions, clears the single actionId key.
|
|
78
78
|
*/
|
|
79
79
|
private broadcastHighlightClear;
|
package/dist/devtools/bridge.js
CHANGED
|
@@ -141,7 +141,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
141
141
|
columns: /* @__PURE__ */ new Map()
|
|
142
142
|
});
|
|
143
143
|
this.eventBusOwners.set(root.eventBus, {
|
|
144
|
-
actionId: `
|
|
144
|
+
actionId: `touchStrip:${deviceId}`,
|
|
145
145
|
uuid: ""
|
|
146
146
|
});
|
|
147
147
|
}
|
|
@@ -339,8 +339,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
339
339
|
/** Convert internal RenderProfile to wire-protocol ProfileData. */
|
|
340
340
|
toProfileData(profile) {
|
|
341
341
|
return {
|
|
342
|
-
|
|
343
|
-
fromJsxMs: profile.fromJsxMs,
|
|
342
|
+
vnodeConversionMs: profile.vnodeConversionMs,
|
|
344
343
|
takumiRenderMs: profile.takumiRenderMs,
|
|
345
344
|
hashMs: profile.hashMs,
|
|
346
345
|
base64Ms: profile.base64Ms,
|
|
@@ -363,7 +362,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
363
362
|
});
|
|
364
363
|
}
|
|
365
364
|
const msg = {
|
|
366
|
-
type: "render:
|
|
365
|
+
type: "render:touchStrip",
|
|
367
366
|
ts: Date.now(),
|
|
368
367
|
deviceId,
|
|
369
368
|
canvas: {
|
|
@@ -377,7 +376,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
377
376
|
};
|
|
378
377
|
this.server.broadcast(msg);
|
|
379
378
|
}
|
|
380
|
-
static TB_PREFIX = "
|
|
379
|
+
static TB_PREFIX = "touchStrip:";
|
|
381
380
|
async handleHighlight(actionId, nodeId) {
|
|
382
381
|
try {
|
|
383
382
|
const prevId = this.highlightedActionId;
|
|
@@ -419,7 +418,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
419
418
|
} catch {}
|
|
420
419
|
}
|
|
421
420
|
/**
|
|
422
|
-
* Restore a highlighted action or
|
|
421
|
+
* Restore a highlighted action or TouchStrip to its normal state.
|
|
423
422
|
* Un-suppresses hardware pushes and restores the original image(s).
|
|
424
423
|
*/
|
|
425
424
|
async restoreHighlight(id) {
|
|
@@ -454,7 +453,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
454
453
|
const segmentWidth = 200;
|
|
455
454
|
const segmentHeight = 100;
|
|
456
455
|
const fullWidth = (Math.max(...columns) + 1) * segmentWidth;
|
|
457
|
-
const result = await renderTouchStripWithHighlight(tb.root.vcontainer, fullWidth, segmentHeight, columns, segmentWidth, this.renderConfig
|
|
456
|
+
const result = await renderTouchStripWithHighlight(tb.root.vcontainer, fullWidth, segmentHeight, columns, segmentWidth, this.renderConfig, nodeId);
|
|
458
457
|
if (result && this.highlightedActionId === actionId && this.highlightedNodeId === nodeId) {
|
|
459
458
|
await tb.root.pushSegmentImages(result.segmentUris);
|
|
460
459
|
for (const [col, uri] of result.segmentUris) this.broadcastHighlightRender(`${actionId}:seg:${col}`, uri);
|
|
@@ -473,7 +472,7 @@ var DevtoolsBridge = class DevtoolsBridge {
|
|
|
473
472
|
}
|
|
474
473
|
/**
|
|
475
474
|
* Clear highlight URIs for the given actionId.
|
|
476
|
-
* For
|
|
475
|
+
* For TouchStrip IDs, clears all per-segment keys (touchStrip:*:seg:N).
|
|
477
476
|
* For regular actions, clears the single actionId key.
|
|
478
477
|
*/
|
|
479
478
|
broadcastHighlightClear(id) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { OutputFormat } from '@takumi-rs/core';
|
|
2
1
|
import { VContainer } from '../reconciler/vnode';
|
|
3
2
|
import { RenderConfig } from '../render/pipeline';
|
|
4
3
|
export declare function renderWithHighlight(container: VContainer, width: number, height: number, config: RenderConfig, targetNid: number): Promise<string | null>;
|
|
@@ -6,4 +5,4 @@ export interface TouchStripHighlightResult {
|
|
|
6
5
|
/** Per-column data URIs for ALL segments. */
|
|
7
6
|
segmentUris: Map<number, string>;
|
|
8
7
|
}
|
|
9
|
-
export declare function renderTouchStripWithHighlight(container: VContainer, fullWidth: number, segmentHeight: number, columns: number[], segmentWidth: number,
|
|
8
|
+
export declare function renderTouchStripWithHighlight(container: VContainer, fullWidth: number, segmentHeight: number, columns: number[], segmentWidth: number, config: RenderConfig, targetNid: number): Promise<TouchStripHighlightResult | null>;
|
|
@@ -18,7 +18,8 @@ async function renderWithHighlight(container, width, height, config, targetNid)
|
|
|
18
18
|
devicePixelRatio: config.devicePixelRatio
|
|
19
19
|
}), config.imageFormat);
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
var TOUCHSTRIP_HIGHLIGHT_FORMAT = "png";
|
|
22
|
+
async function renderTouchStripWithHighlight(container, fullWidth, segmentHeight, columns, segmentWidth, config, targetNid) {
|
|
22
23
|
if (container.children.length === 0) return null;
|
|
23
24
|
const effectiveNid = resolveTargetNid(container, targetNid);
|
|
24
25
|
const rootNode = buildTakumiRoot(container);
|
|
@@ -50,10 +51,10 @@ async function renderTouchStripWithHighlight(container, fullWidth, segmentHeight
|
|
|
50
51
|
const segBuffer = await config.renderer.render(clipNode, {
|
|
51
52
|
width: segmentWidth,
|
|
52
53
|
height: segmentHeight,
|
|
53
|
-
format,
|
|
54
|
+
format: TOUCHSTRIP_HIGHLIGHT_FORMAT,
|
|
54
55
|
devicePixelRatio: config.devicePixelRatio
|
|
55
56
|
});
|
|
56
|
-
segmentUris.set(column, bufferToDataUri(segBuffer,
|
|
57
|
+
segmentUris.set(column, bufferToDataUri(segBuffer, TOUCHSTRIP_HIGHLIGHT_FORMAT));
|
|
57
58
|
});
|
|
58
59
|
await Promise.all(segmentPromises);
|
|
59
60
|
return { segmentUris };
|
package/dist/devtools/types.d.ts
CHANGED
|
@@ -150,7 +150,7 @@ export interface RenderMessage extends BaseMessage {
|
|
|
150
150
|
profile?: ProfileData;
|
|
151
151
|
}
|
|
152
152
|
export interface TouchStripRenderMessage extends BaseMessage {
|
|
153
|
-
type: "render:
|
|
153
|
+
type: "render:touchStrip";
|
|
154
154
|
deviceId: string;
|
|
155
155
|
canvas: {
|
|
156
156
|
width: number;
|
|
@@ -179,7 +179,7 @@ export interface LifecycleMessage extends BaseMessage {
|
|
|
179
179
|
event: "appear" | "disappear";
|
|
180
180
|
actionId: string;
|
|
181
181
|
actionUuid: string;
|
|
182
|
-
surface: "key" | "dial" | "touch" | "
|
|
182
|
+
surface: "key" | "dial" | "touch" | "touchStrip";
|
|
183
183
|
device: {
|
|
184
184
|
id: string;
|
|
185
185
|
type: number;
|
|
@@ -203,8 +203,8 @@ export interface HighlightRenderMessage extends BaseMessage {
|
|
|
203
203
|
}
|
|
204
204
|
/** Per-render pipeline timing data, embedded in RenderMessage. */
|
|
205
205
|
export interface ProfileData {
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
/** Time to convert VNode tree to Takumi node tree (ms). */
|
|
207
|
+
vnodeConversionMs: number;
|
|
208
208
|
takumiRenderMs: number;
|
|
209
209
|
hashMs: number;
|
|
210
210
|
base64Ms: number;
|
|
@@ -234,7 +234,7 @@ export interface MetricsData {
|
|
|
234
234
|
/** Image cache memory usage in bytes. */
|
|
235
235
|
imageCacheBytes: number;
|
|
236
236
|
/** TouchStrip cache memory usage in bytes. */
|
|
237
|
-
|
|
237
|
+
touchStripCacheBytes: number;
|
|
238
238
|
}
|
|
239
239
|
export interface MetricsMessage extends BaseMessage {
|
|
240
240
|
type: "metrics";
|
package/dist/font-inline.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
1
2
|
import { dirname, resolve } from "node:path";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import { realpathSync } from "node:fs";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
5
5
|
//#region src/font-inline.ts
|
|
6
6
|
var FONT_RE = /\.(ttf|otf|woff2?)$/;
|
|
7
7
|
/**
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { FontConfig } from './types';
|
|
2
|
+
type FontWeight = FontConfig["weight"];
|
|
3
|
+
type FontStyle = FontConfig["style"];
|
|
4
|
+
export interface GoogleFontVariant {
|
|
5
|
+
weight?: FontWeight;
|
|
6
|
+
style?: FontStyle;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fetch a Google Font as a ready-to-use `FontConfig`.
|
|
10
|
+
*
|
|
11
|
+
* Downloads TTF font data directly from Google Fonts — no npm package
|
|
12
|
+
* needed. The returned config can be passed straight into
|
|
13
|
+
* `createPlugin({ fonts: [...] })`.
|
|
14
|
+
*
|
|
15
|
+
* Font files are cached to `.google-fonts/` in the current working
|
|
16
|
+
* directory. Subsequent calls read from disk without network access.
|
|
17
|
+
*
|
|
18
|
+
* @param family - The Google Font family name (e.g. `"Inter"`, `"Roboto"`).
|
|
19
|
+
* @returns A `FontConfig` with weight 400 and normal style.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const inter = await googleFont("Inter");
|
|
24
|
+
* createPlugin({ fonts: [inter], actions: [...] });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function googleFont(family: string): Promise<FontConfig>;
|
|
28
|
+
/**
|
|
29
|
+
* Fetch a specific weight/style variant of a Google Font.
|
|
30
|
+
*
|
|
31
|
+
* @param family - The Google Font family name.
|
|
32
|
+
* @param variant - Weight and/or style to fetch.
|
|
33
|
+
* @returns A single `FontConfig`.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* const bold = await googleFont("Inter", { weight: 700 });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function googleFont(family: string, variant: GoogleFontVariant): Promise<FontConfig>;
|
|
41
|
+
/**
|
|
42
|
+
* Fetch multiple weight/style variants of a Google Font in one call.
|
|
43
|
+
*
|
|
44
|
+
* All variants are fetched in parallel for maximum throughput.
|
|
45
|
+
*
|
|
46
|
+
* @param family - The Google Font family name.
|
|
47
|
+
* @param variants - Array of weight/style combinations to fetch.
|
|
48
|
+
* @returns An array of `FontConfig` objects, one per variant.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const fonts = await googleFont("Inter", [
|
|
53
|
+
* { weight: 400 },
|
|
54
|
+
* { weight: 700 },
|
|
55
|
+
* { weight: 700, style: "italic" },
|
|
56
|
+
* ]);
|
|
57
|
+
* createPlugin({ fonts, actions: [...] });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function googleFont(family: string, variants: GoogleFontVariant[]): Promise<FontConfig[]>;
|
|
61
|
+
export {};
|