@fcannizzaro/streamdeck-react 0.1.11 → 0.1.12
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/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/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/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 +3 -1
- package/dist/index.js +2 -1
- package/dist/plugin.js +69 -100
- 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/roots/registry.d.ts +5 -9
- package/dist/roots/registry.js +10 -27
- 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 +23 -19
- package/package.json +6 -4
- 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/dist/plugin.js
CHANGED
|
@@ -2,10 +2,11 @@ import { metrics } from "./render/metrics.js";
|
|
|
2
2
|
import { RootRegistry } from "./roots/registry.js";
|
|
3
3
|
import { RenderPool } from "./render/render-pool.js";
|
|
4
4
|
import { startDevtoolsServer } from "./devtools/index.js";
|
|
5
|
-
import
|
|
5
|
+
import { physicalDevice } from "./adapter/physical-device.js";
|
|
6
6
|
import { Renderer } from "@takumi-rs/core";
|
|
7
7
|
//#region src/plugin.ts
|
|
8
8
|
function createPlugin(config) {
|
|
9
|
+
const adapter = config.adapter ?? physicalDevice();
|
|
9
10
|
const renderer = new Renderer({ fonts: config.fonts.map((f) => ({
|
|
10
11
|
name: f.name,
|
|
11
12
|
data: f.data,
|
|
@@ -20,39 +21,39 @@ function createPlugin(config) {
|
|
|
20
21
|
devicePixelRatio: config.devicePixelRatio ?? 1,
|
|
21
22
|
debug: config.debug ?? process.env.NODE_ENV !== "production",
|
|
22
23
|
imageCacheMaxBytes: config.imageCacheMaxBytes ?? 16 * 1024 * 1024,
|
|
23
|
-
|
|
24
|
-
renderPool
|
|
25
|
-
touchstripImageFormat: config.touchstripImageFormat ?? "webp"
|
|
24
|
+
touchStripCacheMaxBytes: config.touchStripCacheMaxBytes ?? 8 * 1024 * 1024,
|
|
25
|
+
renderPool
|
|
26
26
|
};
|
|
27
|
-
const registry = new RootRegistry(renderConfig,
|
|
28
|
-
await
|
|
27
|
+
const registry = new RootRegistry(renderConfig, adapter, async (settings) => {
|
|
28
|
+
await adapter.setGlobalSettings(settings);
|
|
29
29
|
}, config.wrapper);
|
|
30
|
-
|
|
30
|
+
adapter.getGlobalSettings().then((gs) => {
|
|
31
31
|
registry.setGlobalSettings(gs);
|
|
32
32
|
}).catch((err) => {
|
|
33
33
|
console.error("[@fcannizzaro/streamdeck-react] Failed to load global settings:", err);
|
|
34
34
|
});
|
|
35
|
-
|
|
36
|
-
registry.setGlobalSettings(
|
|
35
|
+
adapter.onGlobalSettingsChanged((settings) => {
|
|
36
|
+
registry.setGlobalSettings(settings);
|
|
37
37
|
});
|
|
38
|
-
for (const definition of config.actions)
|
|
39
|
-
const singletonAction = createSingletonAction(definition, registry, config.onActionError);
|
|
40
|
-
streamDeck.actions.registerAction(singletonAction);
|
|
41
|
-
}
|
|
38
|
+
for (const definition of config.actions) registerActionWithAdapter(adapter, definition, registry, config.onActionError);
|
|
42
39
|
if (renderConfig.debug) metrics.enable();
|
|
43
40
|
if (config.devtools) startDevtoolsServer({
|
|
44
|
-
devtoolsName:
|
|
41
|
+
devtoolsName: adapter.pluginUUID,
|
|
45
42
|
registry,
|
|
46
43
|
renderConfig
|
|
47
44
|
});
|
|
48
45
|
return { async connect() {
|
|
49
46
|
if (renderPool != null) renderPool.initialize().catch(() => {});
|
|
50
|
-
await
|
|
47
|
+
await adapter.connect();
|
|
51
48
|
} };
|
|
52
49
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
function registerActionWithAdapter(adapter, definition, registry, onError) {
|
|
51
|
+
const handleError = (actionId, err) => {
|
|
52
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
53
|
+
console.error(`[@fcannizzaro/streamdeck-react] Error in action ${definition.uuid} (${actionId}):`, error);
|
|
54
|
+
onError?.(definition.uuid, actionId, error);
|
|
55
|
+
};
|
|
56
|
+
adapter.registerAction(definition.uuid, {
|
|
56
57
|
onWillAppear(ev) {
|
|
57
58
|
try {
|
|
58
59
|
const isEncoder = ev.payload.controller === "Encoder";
|
|
@@ -64,126 +65,94 @@ function createSingletonAction(definition, registry, onError) {
|
|
|
64
65
|
if (!component) return;
|
|
65
66
|
registry.create(ev, component, definition);
|
|
66
67
|
} catch (err) {
|
|
67
|
-
|
|
68
|
+
handleError(ev.action.id, err);
|
|
68
69
|
}
|
|
69
|
-
}
|
|
70
|
-
onWillDisappear(
|
|
70
|
+
},
|
|
71
|
+
onWillDisappear(actionId) {
|
|
71
72
|
try {
|
|
72
|
-
registry.destroy(
|
|
73
|
+
registry.destroy(actionId);
|
|
73
74
|
} catch (err) {
|
|
74
|
-
|
|
75
|
+
handleError(actionId, err);
|
|
75
76
|
}
|
|
76
|
-
}
|
|
77
|
-
onKeyDown(
|
|
77
|
+
},
|
|
78
|
+
onKeyDown(actionId, payload) {
|
|
78
79
|
try {
|
|
79
|
-
registry.dispatch(
|
|
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
|
-
});
|
|
80
|
+
registry.dispatch(actionId, "keyDown", payload);
|
|
85
81
|
} catch (err) {
|
|
86
|
-
|
|
82
|
+
handleError(actionId, err);
|
|
87
83
|
}
|
|
88
|
-
}
|
|
89
|
-
onKeyUp(
|
|
84
|
+
},
|
|
85
|
+
onKeyUp(actionId, payload) {
|
|
90
86
|
try {
|
|
91
|
-
registry.dispatch(
|
|
92
|
-
settings: ev.payload.settings,
|
|
93
|
-
isInMultiAction: ev.payload.isInMultiAction,
|
|
94
|
-
state: ev.payload.state,
|
|
95
|
-
userDesiredState: "userDesiredState" in ev.payload ? ev.payload.userDesiredState : void 0
|
|
96
|
-
});
|
|
87
|
+
registry.dispatch(actionId, "keyUp", payload);
|
|
97
88
|
} catch (err) {
|
|
98
|
-
|
|
89
|
+
handleError(actionId, err);
|
|
99
90
|
}
|
|
100
|
-
}
|
|
101
|
-
onDialRotate(
|
|
91
|
+
},
|
|
92
|
+
onDialRotate(actionId, payload) {
|
|
102
93
|
try {
|
|
103
|
-
registry.dispatch(
|
|
104
|
-
ticks: ev.payload.ticks,
|
|
105
|
-
pressed: ev.payload.pressed,
|
|
106
|
-
settings: ev.payload.settings
|
|
107
|
-
});
|
|
94
|
+
registry.dispatch(actionId, "dialRotate", payload);
|
|
108
95
|
} catch (err) {
|
|
109
|
-
|
|
96
|
+
handleError(actionId, err);
|
|
110
97
|
}
|
|
111
|
-
}
|
|
112
|
-
onDialDown(
|
|
98
|
+
},
|
|
99
|
+
onDialDown(actionId, payload) {
|
|
113
100
|
try {
|
|
114
|
-
registry.dispatch(
|
|
115
|
-
settings: ev.payload.settings,
|
|
116
|
-
controller: "Encoder"
|
|
117
|
-
});
|
|
101
|
+
registry.dispatch(actionId, "dialDown", payload);
|
|
118
102
|
} catch (err) {
|
|
119
|
-
|
|
103
|
+
handleError(actionId, err);
|
|
120
104
|
}
|
|
121
|
-
}
|
|
122
|
-
onDialUp(
|
|
105
|
+
},
|
|
106
|
+
onDialUp(actionId, payload) {
|
|
123
107
|
try {
|
|
124
|
-
registry.dispatch(
|
|
125
|
-
settings: ev.payload.settings,
|
|
126
|
-
controller: "Encoder"
|
|
127
|
-
});
|
|
108
|
+
registry.dispatch(actionId, "dialUp", payload);
|
|
128
109
|
} catch (err) {
|
|
129
|
-
|
|
110
|
+
handleError(actionId, err);
|
|
130
111
|
}
|
|
131
|
-
}
|
|
132
|
-
onTouchTap(
|
|
112
|
+
},
|
|
113
|
+
onTouchTap(actionId, payload) {
|
|
133
114
|
try {
|
|
134
|
-
registry.dispatch(
|
|
135
|
-
tapPos: ev.payload.tapPos,
|
|
136
|
-
hold: ev.payload.hold,
|
|
137
|
-
settings: ev.payload.settings
|
|
138
|
-
});
|
|
115
|
+
registry.dispatch(actionId, "touchTap", payload);
|
|
139
116
|
} catch (err) {
|
|
140
|
-
|
|
117
|
+
handleError(actionId, err);
|
|
141
118
|
}
|
|
142
|
-
}
|
|
143
|
-
onDidReceiveSettings(
|
|
119
|
+
},
|
|
120
|
+
onDidReceiveSettings(actionId, settings) {
|
|
144
121
|
try {
|
|
145
|
-
registry.updateSettings(
|
|
122
|
+
registry.updateSettings(actionId, settings);
|
|
146
123
|
} catch (err) {
|
|
147
|
-
|
|
124
|
+
handleError(actionId, err);
|
|
148
125
|
}
|
|
149
|
-
}
|
|
150
|
-
onSendToPlugin(
|
|
126
|
+
},
|
|
127
|
+
onSendToPlugin(actionId, payload) {
|
|
151
128
|
try {
|
|
152
|
-
registry.dispatch(
|
|
129
|
+
registry.dispatch(actionId, "sendToPlugin", payload);
|
|
153
130
|
} catch (err) {
|
|
154
|
-
|
|
131
|
+
handleError(actionId, err);
|
|
155
132
|
}
|
|
156
|
-
}
|
|
157
|
-
onPropertyInspectorDidAppear(
|
|
133
|
+
},
|
|
134
|
+
onPropertyInspectorDidAppear(actionId) {
|
|
158
135
|
try {
|
|
159
|
-
registry.dispatch(
|
|
136
|
+
registry.dispatch(actionId, "propertyInspectorDidAppear", void 0);
|
|
160
137
|
} catch (err) {
|
|
161
|
-
|
|
138
|
+
handleError(actionId, err);
|
|
162
139
|
}
|
|
163
|
-
}
|
|
164
|
-
onPropertyInspectorDidDisappear(
|
|
140
|
+
},
|
|
141
|
+
onPropertyInspectorDidDisappear(actionId) {
|
|
165
142
|
try {
|
|
166
|
-
registry.dispatch(
|
|
143
|
+
registry.dispatch(actionId, "propertyInspectorDidDisappear", void 0);
|
|
167
144
|
} catch (err) {
|
|
168
|
-
|
|
145
|
+
handleError(actionId, err);
|
|
169
146
|
}
|
|
170
|
-
}
|
|
171
|
-
onTitleParametersDidChange(
|
|
147
|
+
},
|
|
148
|
+
onTitleParametersDidChange(actionId, payload) {
|
|
172
149
|
try {
|
|
173
|
-
registry.dispatch(
|
|
174
|
-
title: ev.payload.title,
|
|
175
|
-
settings: ev.payload.settings
|
|
176
|
-
});
|
|
150
|
+
registry.dispatch(actionId, "titleParametersDidChange", payload);
|
|
177
151
|
} catch (err) {
|
|
178
|
-
|
|
152
|
+
handleError(actionId, err);
|
|
179
153
|
}
|
|
180
154
|
}
|
|
181
|
-
|
|
182
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
183
|
-
console.error(`[@fcannizzaro/streamdeck-react] Error in action ${definition.uuid} (${actionId}):`, error);
|
|
184
|
-
onError?.(definition.uuid, actionId, error);
|
|
185
|
-
}
|
|
186
|
-
}();
|
|
155
|
+
});
|
|
187
156
|
}
|
|
188
157
|
//#endregion
|
|
189
158
|
export { createPlugin };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ReactElement } from 'react';
|
|
2
1
|
export interface VNode {
|
|
3
2
|
type: string;
|
|
4
3
|
props: Record<string, unknown>;
|
|
@@ -41,4 +40,3 @@ export declare function clearParent(child: VNode): void;
|
|
|
41
40
|
export declare function createVNode(type: string, props: Record<string, unknown>): VNode;
|
|
42
41
|
export declare function createTextVNode(text: string): VNode;
|
|
43
42
|
export declare function createVContainer(renderCallback: () => void): VContainer;
|
|
44
|
-
export declare function vnodeToElement(node: VNode): ReactElement | string;
|
package/dist/reconciler/vnode.js
CHANGED
package/dist/render/cache.d.ts
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
1
|
import { VNode, VContainer } from '../reconciler/vnode';
|
|
2
|
-
/**
|
|
3
|
-
* Initialize the xxHash-wasm module. Call is idempotent — subsequent
|
|
4
|
-
* calls return the same promise. Resolves once `fnv1a()` will use the
|
|
5
|
-
* WASM fast path for large buffers.
|
|
6
|
-
*/
|
|
7
|
-
export declare function initBufferHasher(): Promise<void>;
|
|
8
|
-
/** Reset the xxHash singleton — for testing only. */
|
|
9
|
-
export declare function resetBufferHasher(): void;
|
|
10
2
|
/**
|
|
11
3
|
* Hash a raw byte buffer (Uint8Array or Buffer) or string.
|
|
12
4
|
*
|
|
13
|
-
* For buffers larger than {@link STRIDE_THRESHOLD} bytes
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
16
|
-
* 320 KB touchstrip frames, with superior hash distribution.
|
|
17
|
-
* - **Fallback path**: FNV-1a with strided sampling (every 16th byte)
|
|
18
|
-
* when WASM hasn't compiled yet (startup) or is unavailable.
|
|
5
|
+
* For buffers larger than {@link STRIDE_THRESHOLD} bytes, uses strided
|
|
6
|
+
* FNV-1a sampling (every 16th byte) — at 30fps this adds <1ms even
|
|
7
|
+
* for 320KB TouchStrip buffers.
|
|
19
8
|
*
|
|
20
|
-
* Strings and small buffers always use
|
|
21
|
-
* sizes, and avoids the overhead of calling into WASM for tiny inputs).
|
|
9
|
+
* Strings and small buffers always use full byte-by-byte FNV-1a.
|
|
22
10
|
*/
|
|
23
11
|
export declare function fnv1a(input: string | Uint8Array | Buffer): number;
|
|
24
12
|
/** Feed a string into a running FNV-1a hash. */
|
|
@@ -39,4 +27,4 @@ export declare function computeHash(node: VNode): number;
|
|
|
39
27
|
*/
|
|
40
28
|
export declare function computeTreeHash(container: VContainer): number;
|
|
41
29
|
export declare function computeCacheKey(treeHash: number, width: number, height: number, dpr: number, format: string): number;
|
|
42
|
-
export declare function
|
|
30
|
+
export declare function computeTouchStripSegmentCacheKey(treeHash: number, width: number, height: number, dpr: number, columns: number[]): number;
|
package/dist/render/cache.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { e } from "../node_modules/.bun/xxhash-wasm@1.1.0/node_modules/xxhash-wasm/esm/xxhash-wasm.js";
|
|
2
1
|
//#region src/render/cache.ts
|
|
3
2
|
var FNV_OFFSET_BASIS = 2166136261;
|
|
4
3
|
var FNV_PRIME = 16777619;
|
|
@@ -9,36 +8,16 @@ var SENTINEL_TRUE = 1414681925;
|
|
|
9
8
|
var SENTINEL_FALSE = 1178684499;
|
|
10
9
|
var SENTINEL_ARRAY = 1095914073;
|
|
11
10
|
var SENTINEL_OBJECT = 1329744468;
|
|
12
|
-
/** @internal WASM-accelerated buffer hash function, null before init. */
|
|
13
|
-
var bufferHashFn = null;
|
|
14
|
-
var xxHashInitPromise = null;
|
|
15
|
-
/**
|
|
16
|
-
* Initialize the xxHash-wasm module. Call is idempotent — subsequent
|
|
17
|
-
* calls return the same promise. Resolves once `fnv1a()` will use the
|
|
18
|
-
* WASM fast path for large buffers.
|
|
19
|
-
*/
|
|
20
|
-
function initBufferHasher() {
|
|
21
|
-
if (xxHashInitPromise != null) return xxHashInitPromise;
|
|
22
|
-
xxHashInitPromise = e().then((api) => {
|
|
23
|
-
bufferHashFn = api.h32Raw;
|
|
24
|
-
}).catch(() => {});
|
|
25
|
-
return xxHashInitPromise;
|
|
26
|
-
}
|
|
27
|
-
initBufferHasher();
|
|
28
11
|
var STRIDE_THRESHOLD = 4096;
|
|
29
12
|
var STRIDE = 16;
|
|
30
13
|
/**
|
|
31
14
|
* Hash a raw byte buffer (Uint8Array or Buffer) or string.
|
|
32
15
|
*
|
|
33
|
-
* For buffers larger than {@link STRIDE_THRESHOLD} bytes
|
|
34
|
-
* -
|
|
35
|
-
*
|
|
36
|
-
* 320 KB touchstrip frames, with superior hash distribution.
|
|
37
|
-
* - **Fallback path**: FNV-1a with strided sampling (every 16th byte)
|
|
38
|
-
* when WASM hasn't compiled yet (startup) or is unavailable.
|
|
16
|
+
* For buffers larger than {@link STRIDE_THRESHOLD} bytes, uses strided
|
|
17
|
+
* FNV-1a sampling (every 16th byte) — at 30fps this adds <1ms even
|
|
18
|
+
* for 320KB TouchStrip buffers.
|
|
39
19
|
*
|
|
40
|
-
* Strings and small buffers always use
|
|
41
|
-
* sizes, and avoids the overhead of calling into WASM for tiny inputs).
|
|
20
|
+
* Strings and small buffers always use full byte-by-byte FNV-1a.
|
|
42
21
|
*/
|
|
43
22
|
function fnv1a(input) {
|
|
44
23
|
let hash = FNV_OFFSET_BASIS;
|
|
@@ -47,7 +26,6 @@ function fnv1a(input) {
|
|
|
47
26
|
hash = Math.imul(hash, FNV_PRIME);
|
|
48
27
|
}
|
|
49
28
|
else if (input.length > STRIDE_THRESHOLD) {
|
|
50
|
-
if (bufferHashFn != null) return bufferHashFn(input);
|
|
51
29
|
hash = fnv1aU32(input.length, hash);
|
|
52
30
|
for (let i = 0; i < input.length; i += STRIDE) {
|
|
53
31
|
const end = Math.min(i + 4, input.length);
|
|
@@ -159,11 +137,11 @@ function computeCacheKey(treeHash, width, height, dpr, format) {
|
|
|
159
137
|
key = fnv1aString(format, key);
|
|
160
138
|
return key >>> 0;
|
|
161
139
|
}
|
|
162
|
-
function
|
|
163
|
-
let key = computeCacheKey(treeHash, width, height, dpr,
|
|
140
|
+
function computeTouchStripSegmentCacheKey(treeHash, width, height, dpr, columns) {
|
|
141
|
+
let key = computeCacheKey(treeHash, width, height, dpr, "png");
|
|
164
142
|
key = fnv1aU32(columns.length, key);
|
|
165
143
|
for (const col of columns) key = fnv1aU32(col, key);
|
|
166
144
|
return key >>> 0;
|
|
167
145
|
}
|
|
168
146
|
//#endregion
|
|
169
|
-
export { computeCacheKey,
|
|
147
|
+
export { computeCacheKey, computeTouchStripSegmentCacheKey, computeTreeHash, fnv1a };
|
|
@@ -15,7 +15,7 @@ export interface CacheStats {
|
|
|
15
15
|
* total byte size exceeds `maxBytes`.
|
|
16
16
|
*
|
|
17
17
|
* Generic over value type: use `string` for data URI caching (keys/dials),
|
|
18
|
-
* `Buffer` for raw RGBA caching (
|
|
18
|
+
* `Buffer` for raw RGBA caching (TouchStrip).
|
|
19
19
|
*/
|
|
20
20
|
export declare class ImageCache<V = string> {
|
|
21
21
|
private maxBytes;
|
|
@@ -40,14 +40,15 @@ export declare class ImageCache<V = string> {
|
|
|
40
40
|
}
|
|
41
41
|
/** Get or create the shared image cache for data URIs. */
|
|
42
42
|
export declare function getImageCache(maxBytes?: number): ImageCache<string>;
|
|
43
|
-
/** Get or create the shared
|
|
44
|
-
export declare function
|
|
43
|
+
/** Get or create the shared TouchStrip raw buffer cache. */
|
|
44
|
+
export declare function getTouchStripCache(maxBytes?: number): ImageCache<Buffer>;
|
|
45
45
|
/**
|
|
46
|
-
* Get or create the shared
|
|
46
|
+
* Get or create the shared TouchStrip segment URI cache.
|
|
47
47
|
* Stores sorted `[column, dataUri]` tuples per tree hash + column config.
|
|
48
|
-
* Uses the same default budget as the raw touchstrip cache since only one
|
|
49
|
-
* touchstrip rendering path is active at a time.
|
|
50
48
|
*/
|
|
51
|
-
export declare function
|
|
49
|
+
export declare function getTouchStripSegmentCache(maxBytes?: number): ImageCache<Array<[number, string]>>;
|
|
50
|
+
export declare function getImageCacheStats(): CacheStats;
|
|
51
|
+
export declare function getTouchStripCacheStats(): CacheStats;
|
|
52
|
+
export declare function getTouchStripSegmentCacheStats(): CacheStats;
|
|
52
53
|
/** Reset all caches (for testing or config changes). */
|
|
53
54
|
export declare function resetCaches(): void;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* total byte size exceeds `maxBytes`.
|
|
5
5
|
*
|
|
6
6
|
* Generic over value type: use `string` for data URI caching (keys/dials),
|
|
7
|
-
* `Buffer` for raw RGBA caching (
|
|
7
|
+
* `Buffer` for raw RGBA caching (TouchStrip).
|
|
8
8
|
*/
|
|
9
9
|
var ImageCache = class {
|
|
10
10
|
map = /* @__PURE__ */ new Map();
|
|
@@ -97,32 +97,48 @@ var ImageCache = class {
|
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
var DEFAULT_IMAGE_CACHE_MAX_BYTES = 16 * 1024 * 1024;
|
|
100
|
-
var
|
|
100
|
+
var DEFAULT_TOUCH_STRIP_CACHE_MAX_BYTES = 8 * 1024 * 1024;
|
|
101
101
|
/** Shared image cache for key/dial data URIs. */
|
|
102
102
|
var imageCache = null;
|
|
103
|
-
/** Shared raw buffer cache for
|
|
104
|
-
var
|
|
105
|
-
/** Shared segment URI cache for
|
|
106
|
-
var
|
|
103
|
+
/** Shared raw buffer cache for TouchStrip RGBA data. */
|
|
104
|
+
var touchStripCache = null;
|
|
105
|
+
/** Shared segment URI cache for TouchStrip shared-strip renders. */
|
|
106
|
+
var touchStripSegmentCache = null;
|
|
107
107
|
/** Get or create the shared image cache for data URIs. */
|
|
108
108
|
function getImageCache(maxBytes) {
|
|
109
109
|
if (imageCache == null) imageCache = new ImageCache(maxBytes ?? DEFAULT_IMAGE_CACHE_MAX_BYTES);
|
|
110
110
|
return imageCache;
|
|
111
111
|
}
|
|
112
|
-
/** Get or create the shared
|
|
113
|
-
function
|
|
114
|
-
if (
|
|
115
|
-
return
|
|
112
|
+
/** Get or create the shared TouchStrip raw buffer cache. */
|
|
113
|
+
function getTouchStripCache(maxBytes) {
|
|
114
|
+
if (touchStripCache == null) touchStripCache = new ImageCache(maxBytes ?? DEFAULT_TOUCH_STRIP_CACHE_MAX_BYTES);
|
|
115
|
+
return touchStripCache;
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
|
-
* Get or create the shared
|
|
118
|
+
* Get or create the shared TouchStrip segment URI cache.
|
|
119
119
|
* Stores sorted `[column, dataUri]` tuples per tree hash + column config.
|
|
120
|
-
* Uses the same default budget as the raw touchstrip cache since only one
|
|
121
|
-
* touchstrip rendering path is active at a time.
|
|
122
120
|
*/
|
|
123
|
-
function
|
|
124
|
-
if (
|
|
125
|
-
return
|
|
121
|
+
function getTouchStripSegmentCache(maxBytes) {
|
|
122
|
+
if (touchStripSegmentCache == null) touchStripSegmentCache = new ImageCache(maxBytes ?? DEFAULT_TOUCH_STRIP_CACHE_MAX_BYTES);
|
|
123
|
+
return touchStripSegmentCache;
|
|
124
|
+
}
|
|
125
|
+
function createEmptyStats(maxBytes) {
|
|
126
|
+
return {
|
|
127
|
+
entries: 0,
|
|
128
|
+
bytes: 0,
|
|
129
|
+
maxBytes,
|
|
130
|
+
hits: 0,
|
|
131
|
+
misses: 0
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function getImageCacheStats() {
|
|
135
|
+
return imageCache?.stats ?? createEmptyStats(DEFAULT_IMAGE_CACHE_MAX_BYTES);
|
|
136
|
+
}
|
|
137
|
+
function getTouchStripCacheStats() {
|
|
138
|
+
return touchStripCache?.stats ?? createEmptyStats(DEFAULT_TOUCH_STRIP_CACHE_MAX_BYTES);
|
|
139
|
+
}
|
|
140
|
+
function getTouchStripSegmentCacheStats() {
|
|
141
|
+
return touchStripSegmentCache?.stats ?? createEmptyStats(DEFAULT_TOUCH_STRIP_CACHE_MAX_BYTES);
|
|
126
142
|
}
|
|
127
143
|
//#endregion
|
|
128
|
-
export { getImageCache,
|
|
144
|
+
export { getImageCache, getImageCacheStats, getTouchStripCache, getTouchStripCacheStats, getTouchStripSegmentCache, getTouchStripSegmentCacheStats };
|
package/dist/render/metrics.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface RenderMetrics {
|
|
|
16
16
|
/** Image cache memory usage in bytes. */
|
|
17
17
|
imageCacheBytes: number;
|
|
18
18
|
/** TouchStrip cache memory usage in bytes. */
|
|
19
|
-
|
|
19
|
+
touchStripCacheBytes: number;
|
|
20
20
|
}
|
|
21
21
|
declare class MetricsCollector {
|
|
22
22
|
private _flushCount;
|
|
@@ -26,13 +26,12 @@ declare class MetricsCollector {
|
|
|
26
26
|
private _hashDedupCount;
|
|
27
27
|
private _totalRenderMs;
|
|
28
28
|
private _peakRenderMs;
|
|
29
|
-
private
|
|
30
|
-
private
|
|
31
|
-
private
|
|
32
|
-
private
|
|
33
|
-
private
|
|
34
|
-
private
|
|
35
|
-
private _cumPeakRenderMs;
|
|
29
|
+
private _lastFlush;
|
|
30
|
+
private _lastRender;
|
|
31
|
+
private _lastCacheHit;
|
|
32
|
+
private _lastDirtySkip;
|
|
33
|
+
private _lastHashDedup;
|
|
34
|
+
private _lastTotalRenderMs;
|
|
36
35
|
private _reportTimer;
|
|
37
36
|
private _enabled;
|
|
38
37
|
/** Enable periodic reporting. Call once during plugin init in debug mode. */
|
|
@@ -49,9 +48,9 @@ declare class MetricsCollector {
|
|
|
49
48
|
recordHashDedup(): void;
|
|
50
49
|
/** Record a completed render with its duration in milliseconds. */
|
|
51
50
|
recordRender(renderMs: number): void;
|
|
52
|
-
/** Get current snapshot of all metrics (cumulative
|
|
51
|
+
/** Get current snapshot of all metrics (cumulative). */
|
|
53
52
|
snapshot(): RenderMetrics;
|
|
54
|
-
/** Log a summary to console (called periodically). */
|
|
53
|
+
/** Log a summary to console (called periodically). Computes deltas since last report. */
|
|
55
54
|
private report;
|
|
56
55
|
}
|
|
57
56
|
export declare const metrics: MetricsCollector;
|
package/dist/render/metrics.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getImageCacheStats, getTouchStripCacheStats, getTouchStripSegmentCacheStats } from "./image-cache.js";
|
|
2
2
|
//#region src/render/metrics.ts
|
|
3
3
|
var REPORT_INTERVAL_MS = 1e4;
|
|
4
4
|
var MetricsCollector = class {
|
|
@@ -9,13 +9,12 @@ var MetricsCollector = class {
|
|
|
9
9
|
_hashDedupCount = 0;
|
|
10
10
|
_totalRenderMs = 0;
|
|
11
11
|
_peakRenderMs = 0;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
_cumPeakRenderMs = 0;
|
|
12
|
+
_lastFlush = 0;
|
|
13
|
+
_lastRender = 0;
|
|
14
|
+
_lastCacheHit = 0;
|
|
15
|
+
_lastDirtySkip = 0;
|
|
16
|
+
_lastHashDedup = 0;
|
|
17
|
+
_lastTotalRenderMs = 0;
|
|
19
18
|
_reportTimer = null;
|
|
20
19
|
_enabled = false;
|
|
21
20
|
/** Enable periodic reporting. Call once during plugin init in debug mode. */
|
|
@@ -38,62 +37,60 @@ var MetricsCollector = class {
|
|
|
38
37
|
/** Record a flush attempt (before any skip checks). */
|
|
39
38
|
recordFlush() {
|
|
40
39
|
this._flushCount++;
|
|
41
|
-
this._cumFlushCount++;
|
|
42
40
|
}
|
|
43
41
|
/** Record a dirty-skip (container was clean). */
|
|
44
42
|
recordDirtySkip() {
|
|
45
43
|
this._dirtySkipCount++;
|
|
46
|
-
this._cumDirtySkipCount++;
|
|
47
44
|
}
|
|
48
45
|
/** Record an image cache hit. */
|
|
49
46
|
recordCacheHit() {
|
|
50
47
|
this._cacheHitCount++;
|
|
51
|
-
this._cumCacheHitCount++;
|
|
52
48
|
}
|
|
53
49
|
/** Record a post-render hash dedup (identical output). */
|
|
54
50
|
recordHashDedup() {
|
|
55
51
|
this._hashDedupCount++;
|
|
56
|
-
this._cumHashDedupCount++;
|
|
57
52
|
}
|
|
58
53
|
/** Record a completed render with its duration in milliseconds. */
|
|
59
54
|
recordRender(renderMs) {
|
|
60
55
|
this._renderCount++;
|
|
61
|
-
this._cumRenderCount++;
|
|
62
56
|
this._totalRenderMs += renderMs;
|
|
63
|
-
this._cumTotalRenderMs += renderMs;
|
|
64
57
|
if (renderMs > this._peakRenderMs) this._peakRenderMs = renderMs;
|
|
65
|
-
if (renderMs > this._cumPeakRenderMs) this._cumPeakRenderMs = renderMs;
|
|
66
58
|
}
|
|
67
|
-
/** Get current snapshot of all metrics (cumulative
|
|
59
|
+
/** Get current snapshot of all metrics (cumulative). */
|
|
68
60
|
snapshot() {
|
|
69
|
-
const imageStats =
|
|
70
|
-
const
|
|
71
|
-
const
|
|
61
|
+
const imageStats = getImageCacheStats();
|
|
62
|
+
const touchStripStats = getTouchStripCacheStats();
|
|
63
|
+
const segmentStats = getTouchStripSegmentCacheStats();
|
|
72
64
|
return {
|
|
73
|
-
flushCount: this.
|
|
74
|
-
renderCount: this.
|
|
75
|
-
cacheHitCount: this.
|
|
76
|
-
dirtySkipCount: this.
|
|
77
|
-
hashDedupCount: this.
|
|
78
|
-
avgRenderMs: this.
|
|
79
|
-
peakRenderMs: this.
|
|
65
|
+
flushCount: this._flushCount,
|
|
66
|
+
renderCount: this._renderCount,
|
|
67
|
+
cacheHitCount: this._cacheHitCount,
|
|
68
|
+
dirtySkipCount: this._dirtySkipCount,
|
|
69
|
+
hashDedupCount: this._hashDedupCount,
|
|
70
|
+
avgRenderMs: this._renderCount > 0 ? this._totalRenderMs / this._renderCount : 0,
|
|
71
|
+
peakRenderMs: this._peakRenderMs,
|
|
80
72
|
imageCacheBytes: imageStats.bytes,
|
|
81
|
-
|
|
73
|
+
touchStripCacheBytes: touchStripStats.bytes + segmentStats.bytes
|
|
82
74
|
};
|
|
83
75
|
}
|
|
84
|
-
/** Log a summary to console (called periodically). */
|
|
76
|
+
/** Log a summary to console (called periodically). Computes deltas since last report. */
|
|
85
77
|
report() {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
92
|
-
this.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
78
|
+
const dFlush = this._flushCount - this._lastFlush;
|
|
79
|
+
if (dFlush === 0) return;
|
|
80
|
+
const dRender = this._renderCount - this._lastRender;
|
|
81
|
+
const dCacheHit = this._cacheHitCount - this._lastCacheHit;
|
|
82
|
+
const dDirtySkip = this._dirtySkipCount - this._lastDirtySkip;
|
|
83
|
+
const dHashDedup = this._hashDedupCount - this._lastHashDedup;
|
|
84
|
+
const dTotalMs = this._totalRenderMs - this._lastTotalRenderMs;
|
|
85
|
+
const skipRate = dFlush > 0 ? ((dDirtySkip + dCacheHit + dHashDedup) / dFlush * 100).toFixed(1) : "0";
|
|
86
|
+
const avgMs = dRender > 0 ? (dTotalMs / dRender).toFixed(1) : "0.0";
|
|
87
|
+
console.log(`[@fcannizzaro/streamdeck-react] Metrics (${REPORT_INTERVAL_MS / 1e3}s): flushes=${dFlush} renders=${dRender} cacheHits=${dCacheHit} dirtySkips=${dDirtySkip} hashDedups=${dHashDedup} skipRate=${skipRate}% avgRender=${avgMs}ms peak=${this._peakRenderMs.toFixed(1)}ms imgCache=${(getImageCacheStats().bytes / 1024).toFixed(0)}KB tbCache=${((getTouchStripCacheStats().bytes + getTouchStripSegmentCacheStats().bytes) / 1024).toFixed(0)}KB`);
|
|
88
|
+
this._lastFlush = this._flushCount;
|
|
89
|
+
this._lastRender = this._renderCount;
|
|
90
|
+
this._lastCacheHit = this._cacheHitCount;
|
|
91
|
+
this._lastDirtySkip = this._dirtySkipCount;
|
|
92
|
+
this._lastHashDedup = this._hashDedupCount;
|
|
93
|
+
this._lastTotalRenderMs = this._totalRenderMs;
|
|
97
94
|
}
|
|
98
95
|
};
|
|
99
96
|
var metrics = new MetricsCollector();
|