@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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
//#region src/google-font.ts
|
|
4
|
+
var TTF_USER_AGENT = "Mozilla/4.0";
|
|
5
|
+
var GOOGLE_FONTS_CSS2_BASE = "https://fonts.googleapis.com/css2";
|
|
6
|
+
var CACHE_DIR = ".google-fonts";
|
|
7
|
+
function sanitizeName(family) {
|
|
8
|
+
return family.toLowerCase().replace(/\s+/g, "-");
|
|
9
|
+
}
|
|
10
|
+
function cacheFilePath(family, weight, style) {
|
|
11
|
+
return join(process.cwd(), CACHE_DIR, `${sanitizeName(family)}-${weight}-${style}.ttf`);
|
|
12
|
+
}
|
|
13
|
+
async function fileExists(path) {
|
|
14
|
+
try {
|
|
15
|
+
await access(path);
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function ensureCacheDir() {
|
|
22
|
+
await mkdir(join(process.cwd(), CACHE_DIR), { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
async function readCached(family, weight, style) {
|
|
25
|
+
const path = cacheFilePath(family, weight, style);
|
|
26
|
+
if (!await fileExists(path)) return null;
|
|
27
|
+
return {
|
|
28
|
+
name: family,
|
|
29
|
+
data: (await readFile(path)).buffer,
|
|
30
|
+
weight,
|
|
31
|
+
style
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function writeCache(family, weight, style, data) {
|
|
35
|
+
await ensureCacheDir();
|
|
36
|
+
await writeFile(cacheFilePath(family, weight, style), Buffer.from(data));
|
|
37
|
+
}
|
|
38
|
+
function buildCss2Url(family, variants) {
|
|
39
|
+
const hasItalic = variants.some((v) => v.style === "italic");
|
|
40
|
+
const encodedFamily = encodeURIComponent(family);
|
|
41
|
+
if (hasItalic) {
|
|
42
|
+
const specs = variants.map((v) => {
|
|
43
|
+
return `${v.style === "italic" ? 1 : 0},${v.weight ?? 400}`;
|
|
44
|
+
});
|
|
45
|
+
specs.sort();
|
|
46
|
+
return `${GOOGLE_FONTS_CSS2_BASE}?family=${encodedFamily}:ital,wght@${specs.join(";")}`;
|
|
47
|
+
}
|
|
48
|
+
const weights = variants.map((v) => v.weight ?? 400);
|
|
49
|
+
weights.sort((a, b) => a - b);
|
|
50
|
+
return `${GOOGLE_FONTS_CSS2_BASE}?family=${encodedFamily}:wght@${weights.join(";")}`;
|
|
51
|
+
}
|
|
52
|
+
function parseFontFaces(css) {
|
|
53
|
+
const results = [];
|
|
54
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55
|
+
const blockRegex = /@font-face\s*\{([^}]+)\}/g;
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = blockRegex.exec(css)) !== null) {
|
|
58
|
+
const block = match[1];
|
|
59
|
+
const urlMatch = block.match(/url\(([^)]+)\)/);
|
|
60
|
+
const weightMatch = block.match(/font-weight:\s*(\d+)/);
|
|
61
|
+
const styleMatch = block.match(/font-style:\s*(normal|italic)/);
|
|
62
|
+
if (!urlMatch?.[1]) continue;
|
|
63
|
+
const weight = Number(weightMatch?.[1] ?? 400);
|
|
64
|
+
const style = styleMatch?.[1] ?? "normal";
|
|
65
|
+
const key = `${weight}:${style}`;
|
|
66
|
+
if (seen.has(key)) continue;
|
|
67
|
+
seen.add(key);
|
|
68
|
+
results.push({
|
|
69
|
+
url: urlMatch[1],
|
|
70
|
+
weight,
|
|
71
|
+
style
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
async function fetchGoogleFonts(family, variants) {
|
|
77
|
+
const results = [];
|
|
78
|
+
const uncached = [];
|
|
79
|
+
for (const v of variants) {
|
|
80
|
+
const weight = v.weight ?? 400;
|
|
81
|
+
const style = v.style ?? "normal";
|
|
82
|
+
const cached = await readCached(family, weight, style);
|
|
83
|
+
if (cached) results.push(cached);
|
|
84
|
+
else uncached.push({
|
|
85
|
+
weight,
|
|
86
|
+
style
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (uncached.length === 0) return results;
|
|
90
|
+
const url = buildCss2Url(family, uncached);
|
|
91
|
+
const cssResponse = await fetch(url, { headers: { "User-Agent": TTF_USER_AGENT } });
|
|
92
|
+
if (!cssResponse.ok) throw new Error(`Google Fonts: failed to fetch CSS for "${family}" (HTTP ${cssResponse.status})`);
|
|
93
|
+
const faces = parseFontFaces(await cssResponse.text());
|
|
94
|
+
if (faces.length === 0) throw new Error(`Google Fonts: no font faces found in CSS response for "${family}". Verify the family name is correct at https://fonts.google.com`);
|
|
95
|
+
const downloaded = await Promise.all(faces.map(async (face) => {
|
|
96
|
+
const fontResponse = await fetch(face.url);
|
|
97
|
+
if (!fontResponse.ok) throw new Error(`Google Fonts: failed to download font file for "${family}" weight ${face.weight} (HTTP ${fontResponse.status})`);
|
|
98
|
+
const data = await fontResponse.arrayBuffer();
|
|
99
|
+
writeCache(family, face.weight, face.style, data).catch(() => {});
|
|
100
|
+
return {
|
|
101
|
+
name: family,
|
|
102
|
+
data,
|
|
103
|
+
weight: face.weight,
|
|
104
|
+
style: face.style
|
|
105
|
+
};
|
|
106
|
+
}));
|
|
107
|
+
return [...results, ...downloaded];
|
|
108
|
+
}
|
|
109
|
+
async function googleFont(family, variantOrVariants) {
|
|
110
|
+
if (variantOrVariants === void 0) return (await fetchGoogleFonts(family, [{
|
|
111
|
+
weight: 400,
|
|
112
|
+
style: "normal"
|
|
113
|
+
}]))[0];
|
|
114
|
+
if (!Array.isArray(variantOrVariants)) return (await fetchGoogleFonts(family, [{
|
|
115
|
+
weight: variantOrVariants.weight ?? 400,
|
|
116
|
+
style: variantOrVariants.style ?? "normal"
|
|
117
|
+
}]))[0];
|
|
118
|
+
return fetchGoogleFonts(family, variantOrVariants.map((v) => ({
|
|
119
|
+
weight: v.weight ?? 400,
|
|
120
|
+
style: v.style ?? "normal"
|
|
121
|
+
})));
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { googleFont };
|
|
@@ -35,7 +35,7 @@ export interface TweenConfig {
|
|
|
35
35
|
duration: number;
|
|
36
36
|
/** Easing function name or custom (t: number) => number. @default "easeOut" */
|
|
37
37
|
easing: EasingName | EasingFn;
|
|
38
|
-
/** Target FPS for the animation tick loop. @default
|
|
38
|
+
/** Target FPS for the animation tick loop. @default 30 */
|
|
39
39
|
fps: number;
|
|
40
40
|
}
|
|
41
41
|
export interface TweenResult<T extends AnimationTarget> {
|
package/dist/hooks/animation.js
CHANGED
|
@@ -12,7 +12,7 @@ var SPRING_DEFAULTS = {
|
|
|
12
12
|
var TWEEN_DEFAULTS = {
|
|
13
13
|
duration: 300,
|
|
14
14
|
easing: "easeOut",
|
|
15
|
-
fps:
|
|
15
|
+
fps: 30
|
|
16
16
|
};
|
|
17
17
|
/** Max dt cap to prevent spring explosion after long pauses.
|
|
18
18
|
* If the process is suspended (debugger, GC pause), dt could be
|
|
@@ -196,7 +196,7 @@ function useSpring(target, config) {
|
|
|
196
196
|
config?.displacementThreshold,
|
|
197
197
|
config?.clamp
|
|
198
198
|
]);
|
|
199
|
-
const fps = config?.fps ??
|
|
199
|
+
const fps = config?.fps ?? 30;
|
|
200
200
|
const isObject = typeof target === "object" && target !== null;
|
|
201
201
|
const stateRef = useRef(null);
|
|
202
202
|
if (stateRef.current === null) stateRef.current = initializeSpringState(target);
|
package/dist/hooks/events.js
CHANGED
|
@@ -38,7 +38,7 @@ function useTouchTap(callback) {
|
|
|
38
38
|
function useDialHint(hints) {
|
|
39
39
|
const { action } = useContext(StreamDeckContext);
|
|
40
40
|
useEffect(() => {
|
|
41
|
-
|
|
41
|
+
action.setTriggerDescription({
|
|
42
42
|
rotate: hints.rotate,
|
|
43
43
|
push: hints.press,
|
|
44
44
|
touch: hints.touch,
|
package/dist/hooks/sdk.js
CHANGED
|
@@ -3,23 +3,23 @@ import { useCallbackRef } from "./internal/useCallbackRef.js";
|
|
|
3
3
|
import { useCallback, useContext, useEffect } from "react";
|
|
4
4
|
//#region src/hooks/sdk.ts
|
|
5
5
|
function useOpenUrl() {
|
|
6
|
-
const {
|
|
6
|
+
const { adapter } = useContext(StreamDeckContext);
|
|
7
7
|
return useCallback(async (url) => {
|
|
8
|
-
await
|
|
9
|
-
}, [
|
|
8
|
+
await adapter.openUrl(url);
|
|
9
|
+
}, [adapter]);
|
|
10
10
|
}
|
|
11
11
|
function useSwitchProfile() {
|
|
12
|
-
const {
|
|
12
|
+
const { adapter, action } = useContext(StreamDeckContext);
|
|
13
13
|
return useCallback(async (profile, deviceId) => {
|
|
14
14
|
const devId = deviceId ?? action.device.id;
|
|
15
|
-
await
|
|
16
|
-
}, [
|
|
15
|
+
await adapter.switchToProfile(devId, profile);
|
|
16
|
+
}, [adapter, action]);
|
|
17
17
|
}
|
|
18
18
|
function useSendToPI() {
|
|
19
|
-
const {
|
|
19
|
+
const { adapter } = useContext(StreamDeckContext);
|
|
20
20
|
return useCallback(async (payload) => {
|
|
21
|
-
await
|
|
22
|
-
}, [
|
|
21
|
+
await adapter.sendToPropertyInspector(payload);
|
|
22
|
+
}, [adapter]);
|
|
23
23
|
}
|
|
24
24
|
function usePropertyInspector(callback) {
|
|
25
25
|
const bus = useContext(EventBusContext);
|
|
@@ -41,13 +41,13 @@ function useShowAlert() {
|
|
|
41
41
|
function useShowOk() {
|
|
42
42
|
const { action } = useContext(StreamDeckContext);
|
|
43
43
|
return useCallback(async () => {
|
|
44
|
-
|
|
44
|
+
await action.showOk();
|
|
45
45
|
}, [action]);
|
|
46
46
|
}
|
|
47
47
|
function useTitle() {
|
|
48
48
|
const { action } = useContext(StreamDeckContext);
|
|
49
49
|
return useCallback(async (title) => {
|
|
50
|
-
|
|
50
|
+
await action.setTitle(title);
|
|
51
51
|
}, [action]);
|
|
52
52
|
}
|
|
53
53
|
//#endregion
|
package/dist/hooks/utility.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useCallbackRef } from "./internal/useCallbackRef.js";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
//#region src/hooks/utility.ts
|
|
4
|
-
var DEFAULT_TICK_FPS =
|
|
4
|
+
var DEFAULT_TICK_FPS = 30;
|
|
5
|
+
var MAX_TICK_FPS = 30;
|
|
5
6
|
function toTickIntervalMs(fps) {
|
|
6
7
|
if (!Number.isFinite(fps) || fps <= 0) return Math.round(1e3 / DEFAULT_TICK_FPS);
|
|
7
|
-
return Math.max(1, Math.round(1e3 / fps));
|
|
8
|
+
return Math.max(1, Math.round(1e3 / Math.min(fps, MAX_TICK_FPS)));
|
|
8
9
|
}
|
|
9
10
|
function useInterval(callback, delayMs) {
|
|
10
11
|
const callbackRef = useCallbackRef(callback);
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export { defineAction } from './action';
|
|
|
3
3
|
export type { RenderProfile } from './render/pipeline';
|
|
4
4
|
export type { CacheStats } from './render/image-cache';
|
|
5
5
|
export type { RenderMetrics } from './render/metrics';
|
|
6
|
+
export { physicalDevice } from './adapter/physical-device';
|
|
7
|
+
export type { StreamDeckAdapter, AdapterActionHandle, AdapterActionCallbacks, AdapterWillAppearEvent, AdapterActionDevice, AdapterController, AdapterCoordinates, AdapterSize, AdapterTriggerDescription, } from './adapter/types';
|
|
6
8
|
export { useKeyDown, useKeyUp, useDialRotate, useDialDown, useDialUp, useTouchTap, useDialHint, } from './hooks/events';
|
|
7
9
|
export { useTap, useLongPress, useDoubleTap } from './hooks/gestures';
|
|
8
10
|
export { useSettings, useGlobalSettings } from './hooks/settings';
|
|
@@ -21,7 +23,9 @@ export { ProgressBar } from './components/ProgressBar';
|
|
|
21
23
|
export { CircularGauge } from './components/CircularGauge';
|
|
22
24
|
export { ErrorBoundary } from './components/ErrorBoundary';
|
|
23
25
|
export { tw } from './tw/index';
|
|
24
|
-
export
|
|
26
|
+
export { googleFont } from './google-font';
|
|
27
|
+
export type { GoogleFontVariant } from './google-font';
|
|
28
|
+
export type { PluginConfig, Plugin, FontConfig, ActionConfig, ActionConfigInput, ActionDefinition, ActionUUID, ManifestActions, EncoderLayout, WrapperComponent, TakumiBackend, Controller, Coordinates, Size, DeviceInfo, ActionInfo, CanvasInfo, TouchStripLayout, TouchStripLayoutItem, KeyDownPayload, KeyUpPayload, DialRotatePayload, DialPressPayload, TouchTapPayload, DialHints, StreamDeckAccess, TouchStripInfo, TouchStripTapPayload, TouchStripDialRotatePayload, TouchStripDialPressPayload, } from './types';
|
|
25
29
|
export type { TapOptions, LongPressOptions, DoubleTapOptions } from './hooks/gestures';
|
|
26
30
|
export type { BoxProps } from './components/Box';
|
|
27
31
|
export type { TextProps } from './components/Text';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { physicalDevice } from "./adapter/physical-device.js";
|
|
1
2
|
import { createPlugin } from "./plugin.js";
|
|
2
3
|
import { defineAction } from "./action.js";
|
|
3
4
|
import { useDialDown, useDialHint, useDialRotate, useDialUp, useKeyDown, useKeyUp, useTouchTap } from "./hooks/events.js";
|
|
@@ -17,4 +18,5 @@ import { ProgressBar } from "./components/ProgressBar.js";
|
|
|
17
18
|
import { CircularGauge } from "./components/CircularGauge.js";
|
|
18
19
|
import { ErrorBoundary } from "./components/ErrorBoundary.js";
|
|
19
20
|
import { tw } from "./tw/index.js";
|
|
20
|
-
|
|
21
|
+
import { googleFont } from "./google-font.js";
|
|
22
|
+
export { Box, CircularGauge, Easings, ErrorBoundary, Icon, Image, ProgressBar, SpringPresets, Text, createPlugin, defineAction, googleFont, physicalDevice, tw, useAction, useCanvas, useDevice, useDialDown, useDialHint, useDialRotate, useDialUp, useDoubleTap, useGlobalSettings, useInterval, useKeyDown, useKeyUp, useLongPress, useOpenUrl, usePrevious, usePropertyInspector, useSendToPI, useSettings, useShowAlert, useShowOk, useSpring, useStreamDeck, useSwitchProfile, useTap, useTick, useTimeout, useTitle, useTouchStrip, useTouchStripDialDown, useTouchStripDialRotate, useTouchStripDialUp, useTouchStripTap, useTouchTap, useTween, useWillAppear, useWillDisappear };
|
package/dist/plugin.js
CHANGED
|
@@ -2,57 +2,67 @@ 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
|
|
9
|
+
const adapter = config.adapter ?? physicalDevice();
|
|
10
|
+
return { async connect() {
|
|
11
|
+
const takumiMode = config.takumi ?? "native-binding";
|
|
12
|
+
const renderPool = (takumiMode === "wasm" ? false : config.useWorker !== false) ? new RenderPool(config.fonts) : null;
|
|
13
|
+
const renderConfig = {
|
|
14
|
+
renderer: await initializeRenderer(takumiMode, config.fonts),
|
|
15
|
+
imageFormat: config.imageFormat ?? "png",
|
|
16
|
+
caching: config.caching ?? true,
|
|
17
|
+
devicePixelRatio: config.devicePixelRatio ?? 1,
|
|
18
|
+
debug: config.debug ?? process.env.NODE_ENV !== "production",
|
|
19
|
+
imageCacheMaxBytes: config.imageCacheMaxBytes ?? 16 * 1024 * 1024,
|
|
20
|
+
touchStripCacheMaxBytes: config.touchStripCacheMaxBytes ?? 8 * 1024 * 1024,
|
|
21
|
+
renderPool
|
|
22
|
+
};
|
|
23
|
+
const registry = new RootRegistry(renderConfig, adapter, async (settings) => {
|
|
24
|
+
await adapter.setGlobalSettings(settings);
|
|
25
|
+
}, config.wrapper);
|
|
26
|
+
adapter.getGlobalSettings().then((gs) => {
|
|
27
|
+
registry.setGlobalSettings(gs);
|
|
28
|
+
}).catch((err) => {
|
|
29
|
+
console.error("[@fcannizzaro/streamdeck-react] Failed to load global settings:", err);
|
|
30
|
+
});
|
|
31
|
+
adapter.onGlobalSettingsChanged((settings) => {
|
|
32
|
+
registry.setGlobalSettings(settings);
|
|
33
|
+
});
|
|
34
|
+
for (const definition of config.actions) registerActionWithAdapter(adapter, definition, registry, config.onActionError);
|
|
35
|
+
if (renderConfig.debug) metrics.enable();
|
|
36
|
+
if (config.devtools) startDevtoolsServer({
|
|
37
|
+
devtoolsName: adapter.pluginUUID,
|
|
38
|
+
registry,
|
|
39
|
+
renderConfig
|
|
40
|
+
});
|
|
41
|
+
if (renderPool != null) renderPool.initialize().catch(() => {});
|
|
42
|
+
await adapter.connect();
|
|
43
|
+
} };
|
|
44
|
+
}
|
|
45
|
+
async function initializeRenderer(mode, fonts) {
|
|
46
|
+
const fontData = fonts.map((f) => ({
|
|
10
47
|
name: f.name,
|
|
11
48
|
data: f.data,
|
|
12
49
|
weight: f.weight,
|
|
13
50
|
style: f.style
|
|
14
|
-
}))
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
caching: config.caching ?? true,
|
|
20
|
-
devicePixelRatio: config.devicePixelRatio ?? 1,
|
|
21
|
-
debug: config.debug ?? process.env.NODE_ENV !== "production",
|
|
22
|
-
imageCacheMaxBytes: config.imageCacheMaxBytes ?? 16 * 1024 * 1024,
|
|
23
|
-
touchstripCacheMaxBytes: config.touchstripCacheMaxBytes ?? 8 * 1024 * 1024,
|
|
24
|
-
renderPool,
|
|
25
|
-
touchstripImageFormat: config.touchstripImageFormat ?? "webp"
|
|
26
|
-
};
|
|
27
|
-
const registry = new RootRegistry(renderConfig, config.renderDebounceMs ?? 16, streamDeck, async (settings) => {
|
|
28
|
-
await streamDeck.settings.setGlobalSettings(settings);
|
|
29
|
-
}, config.wrapper);
|
|
30
|
-
streamDeck.settings.getGlobalSettings().then((gs) => {
|
|
31
|
-
registry.setGlobalSettings(gs);
|
|
32
|
-
}).catch((err) => {
|
|
33
|
-
console.error("[@fcannizzaro/streamdeck-react] Failed to load global settings:", err);
|
|
34
|
-
});
|
|
35
|
-
streamDeck.settings.onDidReceiveGlobalSettings((ev) => {
|
|
36
|
-
registry.setGlobalSettings(ev.settings);
|
|
37
|
-
});
|
|
38
|
-
for (const definition of config.actions) {
|
|
39
|
-
const singletonAction = createSingletonAction(definition, registry, config.onActionError);
|
|
40
|
-
streamDeck.actions.registerAction(singletonAction);
|
|
51
|
+
}));
|
|
52
|
+
if (mode === "wasm") {
|
|
53
|
+
const wasm = await import("@takumi-rs/wasm");
|
|
54
|
+
await wasm.default();
|
|
55
|
+
return new wasm.Renderer({ fonts: fontData });
|
|
41
56
|
}
|
|
42
|
-
|
|
43
|
-
if (config.devtools) startDevtoolsServer({
|
|
44
|
-
devtoolsName: streamDeck.info.plugin.uuid,
|
|
45
|
-
registry,
|
|
46
|
-
renderConfig
|
|
47
|
-
});
|
|
48
|
-
return { async connect() {
|
|
49
|
-
if (renderPool != null) renderPool.initialize().catch(() => {});
|
|
50
|
-
await streamDeck.connect();
|
|
51
|
-
} };
|
|
57
|
+
return new Renderer({ fonts: fontData });
|
|
52
58
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
function registerActionWithAdapter(adapter, definition, registry, onError) {
|
|
60
|
+
const handleError = (actionId, err) => {
|
|
61
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
62
|
+
console.error(`[@fcannizzaro/streamdeck-react] Error in action ${definition.uuid} (${actionId}):`, error);
|
|
63
|
+
onError?.(definition.uuid, actionId, error);
|
|
64
|
+
};
|
|
65
|
+
adapter.registerAction(definition.uuid, {
|
|
56
66
|
onWillAppear(ev) {
|
|
57
67
|
try {
|
|
58
68
|
const isEncoder = ev.payload.controller === "Encoder";
|
|
@@ -64,126 +74,94 @@ function createSingletonAction(definition, registry, onError) {
|
|
|
64
74
|
if (!component) return;
|
|
65
75
|
registry.create(ev, component, definition);
|
|
66
76
|
} catch (err) {
|
|
67
|
-
|
|
77
|
+
handleError(ev.action.id, err);
|
|
68
78
|
}
|
|
69
|
-
}
|
|
70
|
-
onWillDisappear(
|
|
79
|
+
},
|
|
80
|
+
onWillDisappear(actionId) {
|
|
71
81
|
try {
|
|
72
|
-
registry.destroy(
|
|
82
|
+
registry.destroy(actionId);
|
|
73
83
|
} catch (err) {
|
|
74
|
-
|
|
84
|
+
handleError(actionId, err);
|
|
75
85
|
}
|
|
76
|
-
}
|
|
77
|
-
onKeyDown(
|
|
86
|
+
},
|
|
87
|
+
onKeyDown(actionId, payload) {
|
|
78
88
|
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
|
-
});
|
|
89
|
+
registry.dispatch(actionId, "keyDown", payload);
|
|
85
90
|
} catch (err) {
|
|
86
|
-
|
|
91
|
+
handleError(actionId, err);
|
|
87
92
|
}
|
|
88
|
-
}
|
|
89
|
-
onKeyUp(
|
|
93
|
+
},
|
|
94
|
+
onKeyUp(actionId, payload) {
|
|
90
95
|
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
|
-
});
|
|
96
|
+
registry.dispatch(actionId, "keyUp", payload);
|
|
97
97
|
} catch (err) {
|
|
98
|
-
|
|
98
|
+
handleError(actionId, err);
|
|
99
99
|
}
|
|
100
|
-
}
|
|
101
|
-
onDialRotate(
|
|
100
|
+
},
|
|
101
|
+
onDialRotate(actionId, payload) {
|
|
102
102
|
try {
|
|
103
|
-
registry.dispatch(
|
|
104
|
-
ticks: ev.payload.ticks,
|
|
105
|
-
pressed: ev.payload.pressed,
|
|
106
|
-
settings: ev.payload.settings
|
|
107
|
-
});
|
|
103
|
+
registry.dispatch(actionId, "dialRotate", payload);
|
|
108
104
|
} catch (err) {
|
|
109
|
-
|
|
105
|
+
handleError(actionId, err);
|
|
110
106
|
}
|
|
111
|
-
}
|
|
112
|
-
onDialDown(
|
|
107
|
+
},
|
|
108
|
+
onDialDown(actionId, payload) {
|
|
113
109
|
try {
|
|
114
|
-
registry.dispatch(
|
|
115
|
-
settings: ev.payload.settings,
|
|
116
|
-
controller: "Encoder"
|
|
117
|
-
});
|
|
110
|
+
registry.dispatch(actionId, "dialDown", payload);
|
|
118
111
|
} catch (err) {
|
|
119
|
-
|
|
112
|
+
handleError(actionId, err);
|
|
120
113
|
}
|
|
121
|
-
}
|
|
122
|
-
onDialUp(
|
|
114
|
+
},
|
|
115
|
+
onDialUp(actionId, payload) {
|
|
123
116
|
try {
|
|
124
|
-
registry.dispatch(
|
|
125
|
-
settings: ev.payload.settings,
|
|
126
|
-
controller: "Encoder"
|
|
127
|
-
});
|
|
117
|
+
registry.dispatch(actionId, "dialUp", payload);
|
|
128
118
|
} catch (err) {
|
|
129
|
-
|
|
119
|
+
handleError(actionId, err);
|
|
130
120
|
}
|
|
131
|
-
}
|
|
132
|
-
onTouchTap(
|
|
121
|
+
},
|
|
122
|
+
onTouchTap(actionId, payload) {
|
|
133
123
|
try {
|
|
134
|
-
registry.dispatch(
|
|
135
|
-
tapPos: ev.payload.tapPos,
|
|
136
|
-
hold: ev.payload.hold,
|
|
137
|
-
settings: ev.payload.settings
|
|
138
|
-
});
|
|
124
|
+
registry.dispatch(actionId, "touchTap", payload);
|
|
139
125
|
} catch (err) {
|
|
140
|
-
|
|
126
|
+
handleError(actionId, err);
|
|
141
127
|
}
|
|
142
|
-
}
|
|
143
|
-
onDidReceiveSettings(
|
|
128
|
+
},
|
|
129
|
+
onDidReceiveSettings(actionId, settings) {
|
|
144
130
|
try {
|
|
145
|
-
registry.updateSettings(
|
|
131
|
+
registry.updateSettings(actionId, settings);
|
|
146
132
|
} catch (err) {
|
|
147
|
-
|
|
133
|
+
handleError(actionId, err);
|
|
148
134
|
}
|
|
149
|
-
}
|
|
150
|
-
onSendToPlugin(
|
|
135
|
+
},
|
|
136
|
+
onSendToPlugin(actionId, payload) {
|
|
151
137
|
try {
|
|
152
|
-
registry.dispatch(
|
|
138
|
+
registry.dispatch(actionId, "sendToPlugin", payload);
|
|
153
139
|
} catch (err) {
|
|
154
|
-
|
|
140
|
+
handleError(actionId, err);
|
|
155
141
|
}
|
|
156
|
-
}
|
|
157
|
-
onPropertyInspectorDidAppear(
|
|
142
|
+
},
|
|
143
|
+
onPropertyInspectorDidAppear(actionId) {
|
|
158
144
|
try {
|
|
159
|
-
registry.dispatch(
|
|
145
|
+
registry.dispatch(actionId, "propertyInspectorDidAppear", void 0);
|
|
160
146
|
} catch (err) {
|
|
161
|
-
|
|
147
|
+
handleError(actionId, err);
|
|
162
148
|
}
|
|
163
|
-
}
|
|
164
|
-
onPropertyInspectorDidDisappear(
|
|
149
|
+
},
|
|
150
|
+
onPropertyInspectorDidDisappear(actionId) {
|
|
165
151
|
try {
|
|
166
|
-
registry.dispatch(
|
|
152
|
+
registry.dispatch(actionId, "propertyInspectorDidDisappear", void 0);
|
|
167
153
|
} catch (err) {
|
|
168
|
-
|
|
154
|
+
handleError(actionId, err);
|
|
169
155
|
}
|
|
170
|
-
}
|
|
171
|
-
onTitleParametersDidChange(
|
|
156
|
+
},
|
|
157
|
+
onTitleParametersDidChange(actionId, payload) {
|
|
172
158
|
try {
|
|
173
|
-
registry.dispatch(
|
|
174
|
-
title: ev.payload.title,
|
|
175
|
-
settings: ev.payload.settings
|
|
176
|
-
});
|
|
159
|
+
registry.dispatch(actionId, "titleParametersDidChange", payload);
|
|
177
160
|
} catch (err) {
|
|
178
|
-
|
|
161
|
+
handleError(actionId, err);
|
|
179
162
|
}
|
|
180
163
|
}
|
|
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
|
-
}();
|
|
164
|
+
});
|
|
187
165
|
}
|
|
188
166
|
//#endregion
|
|
189
167
|
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;
|