@fcannizzaro/streamdeck-react 0.1.9 → 0.1.11
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/LICENSE +190 -21
- package/README.md +3 -1
- package/dist/action.d.ts +2 -2
- package/dist/action.js +2 -2
- package/dist/bundler-shared.d.ts +11 -0
- package/dist/bundler-shared.js +11 -0
- package/dist/context/event-bus.d.ts +1 -1
- package/dist/context/event-bus.js +1 -1
- package/dist/context/touchstrip-context.d.ts +2 -0
- package/dist/context/touchstrip-context.js +5 -0
- package/dist/devtools/bridge.d.ts +35 -7
- package/dist/devtools/bridge.js +153 -46
- package/dist/devtools/highlight.d.ts +6 -0
- package/dist/devtools/highlight.js +106 -57
- package/dist/devtools/index.js +6 -0
- package/dist/devtools/observers/lifecycle.d.ts +4 -4
- package/dist/devtools/server.d.ts +6 -1
- package/dist/devtools/server.js +6 -1
- package/dist/devtools/types.d.ts +50 -6
- package/dist/font-inline.d.ts +5 -1
- package/dist/font-inline.js +8 -3
- package/dist/hooks/animation.d.ts +154 -0
- package/dist/hooks/animation.js +381 -0
- package/dist/hooks/events.js +1 -5
- package/dist/hooks/touchstrip.d.ts +6 -0
- package/dist/hooks/touchstrip.js +37 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +3 -2
- package/dist/manifest-codegen.d.ts +38 -0
- package/dist/manifest-codegen.js +110 -0
- package/dist/node_modules/.bun/xxhash-wasm@1.1.0/node_modules/xxhash-wasm/esm/xxhash-wasm.js +3157 -0
- package/dist/plugin.js +20 -9
- package/dist/reconciler/host-config.js +19 -1
- package/dist/reconciler/vnode.d.ts +26 -0
- package/dist/reconciler/vnode.js +41 -10
- package/dist/render/buffer-pool.d.ts +19 -0
- package/dist/render/buffer-pool.js +51 -0
- package/dist/render/cache.d.ts +41 -0
- package/dist/render/cache.js +159 -5
- package/dist/render/image-cache.d.ts +53 -0
- package/dist/render/image-cache.js +128 -0
- package/dist/render/metrics.d.ts +58 -0
- package/dist/render/metrics.js +101 -0
- package/dist/render/pipeline.d.ts +46 -1
- package/dist/render/pipeline.js +370 -36
- package/dist/render/png.d.ts +10 -1
- package/dist/render/png.js +31 -13
- package/dist/render/render-pool.d.ts +26 -0
- package/dist/render/render-pool.js +141 -0
- package/dist/render/svg.d.ts +7 -0
- package/dist/render/svg.js +139 -0
- package/dist/render/worker.d.ts +1 -0
- package/dist/rollup.d.ts +23 -9
- package/dist/rollup.js +24 -9
- package/dist/roots/flush-coordinator.d.ts +18 -0
- package/dist/roots/flush-coordinator.js +38 -0
- package/dist/roots/registry.d.ts +6 -4
- package/dist/roots/registry.js +47 -33
- package/dist/roots/root.d.ts +32 -2
- package/dist/roots/root.js +104 -14
- package/dist/roots/settings-equality.d.ts +5 -0
- package/dist/roots/settings-equality.js +24 -0
- package/dist/roots/touchstrip-root.d.ts +93 -0
- package/dist/roots/touchstrip-root.js +383 -0
- package/dist/types.d.ts +62 -16
- package/dist/vite.d.ts +22 -8
- package/dist/vite.js +24 -8
- package/package.json +5 -4
- package/dist/context/touchbar-context.d.ts +0 -2
- package/dist/context/touchbar-context.js +0 -5
- package/dist/hooks/touchbar.d.ts +0 -6
- package/dist/hooks/touchbar.js +0 -37
- package/dist/roots/touchbar-root.d.ts +0 -45
- package/dist/roots/touchbar-root.js +0 -175
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
//#region src/render/render-pool.ts
|
|
3
|
+
/** Strip internal fields and function props from a VNode tree for worker transfer. */
|
|
4
|
+
function serializeVNode(node) {
|
|
5
|
+
const cleanProps = {};
|
|
6
|
+
for (const [key, value] of Object.entries(node.props)) if (typeof value !== "function" && typeof value !== "symbol") cleanProps[key] = value;
|
|
7
|
+
return {
|
|
8
|
+
type: node.type,
|
|
9
|
+
props: cleanProps,
|
|
10
|
+
children: node.children.map(serializeVNode),
|
|
11
|
+
text: node.text
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
var RenderPool = class {
|
|
15
|
+
worker = null;
|
|
16
|
+
ready = false;
|
|
17
|
+
failed = false;
|
|
18
|
+
nextId = 0;
|
|
19
|
+
pending = /* @__PURE__ */ new Map();
|
|
20
|
+
initPromise = null;
|
|
21
|
+
fonts;
|
|
22
|
+
constructor(fonts) {
|
|
23
|
+
this.fonts = fonts;
|
|
24
|
+
}
|
|
25
|
+
/** Start the worker. Call once during plugin initialization. */
|
|
26
|
+
async initialize() {
|
|
27
|
+
if (this.ready) return true;
|
|
28
|
+
if (this.failed) return false;
|
|
29
|
+
if (this.initPromise != null) {
|
|
30
|
+
await this.initPromise;
|
|
31
|
+
return this.ready;
|
|
32
|
+
}
|
|
33
|
+
this.initPromise = this.doInitialize();
|
|
34
|
+
await this.initPromise;
|
|
35
|
+
return this.ready;
|
|
36
|
+
}
|
|
37
|
+
async doInitialize() {
|
|
38
|
+
try {
|
|
39
|
+
this.worker = new Worker(new URL("data:video/mp2t;base64,Ly8g4pSA4pSAIFJlbmRlciBXb3JrZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACi8vCi8vIFJ1bnMgaW4gYSBzZXBhcmF0ZSB0aHJlYWQgdmlhIE5vZGUuanMgd29ya2VyX3RocmVhZHMuICBIYW5kbGVzIHRoZQovLyBmdWxsIHJlbmRlciBwaXBlbGluZTogc2VyaWFsaXplZCBWTm9kZSBkYXRhIOKGkiBUYWt1bWkgbm9kZXMg4oaSIHJhc3Rlci4KLy8KLy8gVXNlcyB0aGUgZGlyZWN0IFZOb2RlIOKGkiBUYWt1bWkgbm9kZSBieXBhc3MgKHNhbWUgYXMgbWFpbiB0aHJlYWQncwovLyB2bm9kZVRvVGFrdW1pTm9kZSBpbiBwaXBlbGluZS50cyksIHNraXBwaW5nIHZub2RlVG9FbGVtZW50KCkgYW5kCi8vIGZyb21Kc3goKSBlbnRpcmVseS4KLy8KLy8gVGhpcyB1bmJsb2NrcyB0aGUgbWFpbiB0aHJlYWQgZHVyaW5nIHRoZSBleHBlbnNpdmUgVGFrdW1pCi8vIHJhc3Rlcml6YXRpb24gc3RlcCAofjXigJMzMG1zIHBlciBmcmFtZSkuCi8vCi8vIFdoeSBjb2RlIGlzIGR1cGxpY2F0ZWQgKGlubGluZWQpOgovLyAgIFdvcmtlciB0aHJlYWRzIGNhbid0IGltcG9ydCBmcm9tIHRoZSBtYWluIGJ1bmRsZSDigJQgdGhleSBsb2FkCi8vICAgdGhlIGNvbXBpbGVkIHdvcmtlci5qcyBmaWxlIGluZGVwZW5kZW50bHkuICBTVkcgc2VyaWFsaXphdGlvbgovLyAgIGFuZCBWTm9kZeKGklRha3VtaSBjb252ZXJzaW9uIG11c3QgYmUgc2VsZi1jb250YWluZWQgaGVyZS4KLy8gICBCb3RoIG1pcnJvciB0aGUgbG9naWMgaW4gc3ZnLnRzIGFuZCBwaXBlbGluZS50cyByZXNwZWN0aXZlbHkuCi8vCi8vIFplcm8tY29weSByZXR1cm46Ci8vICAgVGhlIHJlbmRlcmVkIGJ1ZmZlciBpcyB0cmFuc2ZlcnJlZCAobm90IGNvcGllZCkgYmFjayB0byB0aGUgbWFpbgovLyAgIHRocmVhZCB2aWEgcG9zdE1lc3NhZ2UncyB0cmFuc2ZlciBsaXN0LiAgVGhpcyBhdm9pZHMgY29weWluZwovLyAgIHBvdGVudGlhbGx5IGxhcmdlIHJhc3RlciBidWZmZXJzIChlLmcuIDgwMMOXMTAww5c0ID0gMzIwS0IgZm9yCi8vICAgdG91Y2hzdHJpcCkgYWNyb3NzIHRoZSB0aHJlYWQgYm91bmRhcnkuCgppbXBvcnQgeyBwYXJlbnRQb3J0LCB3b3JrZXJEYXRhIH0gZnJvbSAibm9kZTp3b3JrZXJfdGhyZWFkcyI7CgovLyDilIDilIAgVHlwZXMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgppbnRlcmZhY2UgU2VyaWFsaXplZFZOb2RlIHsKICB0eXBlOiBzdHJpbmc7CiAgcHJvcHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+OwogIGNoaWxkcmVuOiBTZXJpYWxpemVkVk5vZGVbXTsKICB0ZXh0Pzogc3RyaW5nOwp9CgovKiogTWF0Y2hlcyBAdGFrdW1pLXJzL2hlbHBlcnMgTm9kZSB1bmlvbi4gUGxhaW4gb2JqZWN0IGFjY2VwdGVkIGJ5IFJlbmRlcmVyLnJlbmRlcigpLiAqLwppbnRlcmZhY2UgVGFrdW1pTm9kZSB7CiAgdHlwZTogc3RyaW5nOwogIFtrZXk6IHN0cmluZ106IHVua25vd247Cn0KCmludGVyZmFjZSBJbml0TWVzc2FnZSB7CiAgdHlwZTogImluaXQiOwogIGZvbnRzOiBBcnJheTx7CiAgICBuYW1lOiBzdHJpbmc7CiAgICBkYXRhOiBBcnJheUJ1ZmZlciB8IEJ1ZmZlcjsKICAgIHdlaWdodDogbnVtYmVyOwogICAgc3R5bGU6IHN0cmluZzsKICB9PjsKfQoKaW50ZXJmYWNlIFJlbmRlck1lc3NhZ2UgewogIHR5cGU6ICJyZW5kZXIiOwogIGlkOiBudW1iZXI7CiAgdm5vZGVzOiBTZXJpYWxpemVkVk5vZGVbXTsKICB3aWR0aDogbnVtYmVyOwogIGhlaWdodDogbnVtYmVyOwogIGZvcm1hdDogc3RyaW5nOwogIGRwcjogbnVtYmVyOwp9CgppbnRlcmZhY2UgU2h1dGRvd25NZXNzYWdlIHsKICB0eXBlOiAic2h1dGRvd24iOwp9Cgp0eXBlIFdvcmtlck1lc3NhZ2UgPSBJbml0TWVzc2FnZSB8IFJlbmRlck1lc3NhZ2UgfCBTaHV0ZG93bk1lc3NhZ2U7CgovLyDilIDilIAgU1ZHIFNlcmlhbGl6YXRpb24gKGlubGluZWQgZm9yIHdvcmtlciBjb250ZXh0KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKLy8gTWlycm9ycyB0aGUgc2VyaWFsaXplU3ZnVHJlZSgpIGZyb20gc3ZnLnRzLiBJbmxpbmVkIHRvIGF2b2lkCi8vIGNyb3NzLW1vZHVsZSBpbXBvcnQgaXNzdWVzIGluIHRoZSB3b3JrZXIgdGhyZWFkLgoKY29uc3QgU1ZHX0NBTUVMX0FUVFJTOiBSZWFkb25seVNldDxzdHJpbmc+ID0gbmV3IFNldChbCiAgImFjY2VudEhlaWdodCIsCiAgImFsaWdubWVudEJhc2VsaW5lIiwKICAiYXJhYmljRm9ybSIsCiAgImJhc2VsaW5lU2hpZnQiLAogICJjYXBIZWlnaHQiLAogICJjbGlwUGF0aCIsCiAgImNsaXBQYXRoVW5pdHMiLAogICJjbGlwUnVsZSIsCiAgImNvbG9ySW50ZXJwb2xhdGlvbiIsCiAgImNvbG9ySW50ZXJwb2xhdGlvbkZpbHRlcnMiLAogICJjb2xvclByb2ZpbGUiLAogICJjb2xvclJlbmRlcmluZyIsCiAgImVuYWJsZUJhY2tncm91bmQiLAogICJmaWxsT3BhY2l0eSIsCiAgImZpbGxSdWxlIiwKICAiZmxvb2RDb2xvciIsCiAgImZsb29kT3BhY2l0eSIsCiAgImZvbnRGYW1pbHkiLAogICJmb250U2l6ZSIsCiAgImZvbnRTaXplQWRqdXN0IiwKICAiZm9udFN0cmV0Y2giLAogICJmb250U3R5bGUiLAogICJmb250VmFyaWFudCIsCiAgImZvbnRXZWlnaHQiLAogICJnbHlwaE5hbWUiLAogICJnbHlwaE9yaWVudGF0aW9uSG9yaXpvbnRhbCIsCiAgImdseXBoT3JpZW50YXRpb25WZXJ0aWNhbCIsCiAgImhvcml6QWR2WCIsCiAgImhvcml6T3JpZ2luWCIsCiAgImltYWdlUmVuZGVyaW5nIiwKICAibGV0dGVyU3BhY2luZyIsCiAgImxpZ2h0aW5nQ29sb3IiLAogICJtYXJrZXJFbmQiLAogICJtYXJrZXJNaWQiLAogICJtYXJrZXJTdGFydCIsCiAgIm92ZXJsaW5lUG9zaXRpb24iLAogICJvdmVybGluZVRoaWNrbmVzcyIsCiAgInBhaW50T3JkZXIiLAogICJwb2ludGVyRXZlbnRzIiwKICAicHJlc2VydmVBc3BlY3RSYXRpbyIsCiAgInNoYXBlUmVuZGVyaW5nIiwKICAic3RvcENvbG9yIiwKICAic3RvcE9wYWNpdHkiLAogICJzdHJva2VEYXNoYXJyYXkiLAogICJzdHJva2VEYXNob2Zmc2V0IiwKICAic3Ryb2tlTGluZWNhcCIsCiAgInN0cm9rZUxpbmVqb2luIiwKICAic3Ryb2tlTWl0ZXJsaW1pdCIsCiAgInN0cm9rZU9wYWNpdHkiLAogICJzdHJva2VXaWR0aCIsCiAgInRleHRBbmNob3IiLAogICJ0ZXh0RGVjb3JhdGlvbiIsCiAgInRleHRSZW5kZXJpbmciLAogICJ0cmFuc2Zvcm1PcmlnaW4iLAogICJ1bmRlcmxpbmVQb3NpdGlvbiIsCiAgInVuZGVybGluZVRoaWNrbmVzcyIsCiAgInVuaWNvZGVCaWRpIiwKICAidW5pY29kZVJhbmdlIiwKICAidW5pdHNQZXJFbSIsCiAgInZBbHBoYWJldGljIiwKICAidkhhbmdpbmciLAogICJ2SWRlb2dyYXBoaWMiLAogICJ2TWF0aGVtYXRpY2FsIiwKICAidmVjdG9yRWZmZWN0IiwKICAidmVydEFkdlkiLAogICJ2ZXJ0T3JpZ2luWCIsCiAgInZlcnRPcmlnaW5ZIiwKICAid29yZFNwYWNpbmciLAogICJ3cml0aW5nTW9kZSIsCl0pOwoKY29uc3QgU1ZHX1NLSVBfUFJPUFM6IFJlYWRvbmx5U2V0PHN0cmluZz4gPSBuZXcgU2V0KFsKICAiY2hpbGRyZW4iLAogICJrZXkiLAogICJyZWYiLAogICJfX3NlbGYiLAogICJfX3NvdXJjZSIsCl0pOwoKZnVuY3Rpb24gY2FtZWxUb0tlYmFiKHN0cjogc3RyaW5nKTogc3RyaW5nIHsKICByZXR1cm4gc3RyLnJlcGxhY2UoL1tBLVpdL2csIChjaCkgPT4gYC0ke2NoLnRvTG93ZXJDYXNlKCl9YCk7Cn0KCmZ1bmN0aW9uIGVzY2FwZUF0dHIodmFsdWU6IHN0cmluZyk6IHN0cmluZyB7CiAgcmV0dXJuIHZhbHVlCiAgICAucmVwbGFjZSgvJi9nLCAiJmFtcDsiKQogICAgLnJlcGxhY2UoLyIvZywgIiZxdW90OyIpCiAgICAucmVwbGFjZSgvPC9nLCAiJmx0OyIpCiAgICAucmVwbGFjZSgvPi9nLCAiJmd0OyIpOwp9CgpmdW5jdGlvbiBzZXJpYWxpemVTdmdTdHlsZShzdHlsZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmcgewogIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdOwogIGZvciAoY29uc3Qga2V5IG9mIE9iamVjdC5rZXlzKHN0eWxlKSkgewogICAgY29uc3QgdmFsdWUgPSBzdHlsZVtrZXldOwogICAgaWYgKHZhbHVlID09IG51bGwpIGNvbnRpbnVlOwogICAgcGFydHMucHVzaChgJHtjYW1lbFRvS2ViYWIoa2V5KX06JHtTdHJpbmcodmFsdWUpLnRyaW0oKX1gKTsKICB9CiAgcmV0dXJuIHBhcnRzLmpvaW4oIjsiKTsKfQoKZnVuY3Rpb24gc2VyaWFsaXplU3ZnQXR0cihrZXk6IHN0cmluZywgdmFsdWU6IHVua25vd24pOiBzdHJpbmcgfCBudWxsIHsKICBpZiAoU1ZHX1NLSVBfUFJPUFMuaGFzKGtleSkgfHwgdmFsdWUgPT0gbnVsbCkgcmV0dXJuIG51bGw7CiAgbGV0IGF0dHJOYW1lOiBzdHJpbmc7CiAgaWYgKGtleSA9PT0gImNsYXNzTmFtZSIpIGF0dHJOYW1lID0gImNsYXNzIjsKICBlbHNlIGlmIChTVkdfQ0FNRUxfQVRUUlMuaGFzKGtleSkpIGF0dHJOYW1lID0gY2FtZWxUb0tlYmFiKGtleSk7CiAgZWxzZSBhdHRyTmFtZSA9IGtleTsKICBpZiAoa2V5ID09PSAic3R5bGUiICYmIHR5cGVvZiB2YWx1ZSA9PT0gIm9iamVjdCIpIHsKICAgIGNvbnN0IGNzcyA9IHNlcmlhbGl6ZVN2Z1N0eWxlKHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KTsKICAgIGlmICghY3NzKSByZXR1cm4gbnVsbDsKICAgIHJldHVybiBgJHthdHRyTmFtZX09IiR7ZXNjYXBlQXR0cihjc3MpfSJgOwogIH0KICBpZiAodHlwZW9mIHZhbHVlID09PSAiYm9vbGVhbiIpIHJldHVybiBgJHthdHRyTmFtZX09IiR7U3RyaW5nKHZhbHVlKX0iYDsKICByZXR1cm4gYCR7YXR0ck5hbWV9PSIke2VzY2FwZUF0dHIoU3RyaW5nKHZhbHVlKSl9ImA7Cn0KCmZ1bmN0aW9uIHNlcmlhbGl6ZVN2Z1ZOb2RlKG5vZGU6IFNlcmlhbGl6ZWRWTm9kZSk6IHN0cmluZyB7CiAgaWYgKG5vZGUudHlwZSA9PT0gIiN0ZXh0IikgcmV0dXJuIG5vZGUudGV4dCA/PyAiIjsKICBjb25zdCBhdHRyczogc3RyaW5nW10gPSBbXTsKICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhub2RlLnByb3BzKSkgewogICAgY29uc3QgYXR0ciA9IHNlcmlhbGl6ZVN2Z0F0dHIoa2V5LCB2YWx1ZSk7CiAgICBpZiAoYXR0ciAhPSBudWxsKSBhdHRycy5wdXNoKGF0dHIpOwogIH0KICBjb25zdCBjaGlsZE1hcmt1cCA9IG5vZGUuY2hpbGRyZW4ubWFwKHNlcmlhbGl6ZVN2Z1ZOb2RlKS5qb2luKCIiKTsKICBjb25zdCBhdHRyU3RyID0gYXR0cnMubGVuZ3RoID4gMCA/IGAgJHthdHRycy5qb2luKCIgIil9YCA6ICIiOwogIHJldHVybiBgPCR7bm9kZS50eXBlfSR7YXR0clN0cn0+JHtjaGlsZE1hcmt1cH08LyR7bm9kZS50eXBlfT5gOwp9CgpmdW5jdGlvbiBzZXJpYWxpemVTdmdUcmVlKHN2Z05vZGU6IFNlcmlhbGl6ZWRWTm9kZSk6IHN0cmluZyB7CiAgaWYgKCEoInhtbG5zIiBpbiBzdmdOb2RlLnByb3BzKSkgewogICAgY29uc3QgYXVnbWVudGVkID0gewogICAgICAuLi5zdmdOb2RlLAogICAgICBwcm9wczogeyAuLi5zdmdOb2RlLnByb3BzLCB4bWxuczogImh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB9LAogICAgfTsKICAgIHJldHVybiBzZXJpYWxpemVTdmdWTm9kZShhdWdtZW50ZWQpOwogIH0KICByZXR1cm4gc2VyaWFsaXplU3ZnVk5vZGUoc3ZnTm9kZSk7Cn0KCi8vIOKUgOKUgCBEaXJlY3QgVk5vZGUg4oaSIFRha3VtaSBOb2RlIENvbnZlcnNpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACi8vIE1pcnJvcnMgdGhlIG1haW4tdGhyZWFkIHZub2RlVG9UYWt1bWlOb2RlKCkgZnJvbSBwaXBlbGluZS50cy4KLy8gSW5saW5lZCB0byBhdm9pZCBjcm9zcy1tb2R1bGUgaW1wb3J0IGlzc3VlcyBpbiB3b3JrZXIgY29udGV4dC4KCmZ1bmN0aW9uIHZub2RlVG9UYWt1bWlOb2RlKG5vZGU6IFNlcmlhbGl6ZWRWTm9kZSk6IFRha3VtaU5vZGUgewogIC8vIFRleHQgbm9kZXMg4oaSIFRha3VtaSBUZXh0Tm9kZQogIGlmIChub2RlLnR5cGUgPT09ICIjdGV4dCIpIHsKICAgIHJldHVybiB7IHR5cGU6ICJ0ZXh0IiwgdGV4dDogbm9kZS50ZXh0ID8/ICIiIH07CiAgfQoKICBjb25zdCB7IGNoaWxkcmVuOiBfY2hpbGRyZW4sIGNsYXNzTmFtZSwgc3JjLCAuLi5yZXN0UHJvcHMgfSA9IG5vZGUucHJvcHM7CgogIC8vIE1hcCBjbGFzc05hbWUg4oaSIHR3IChzYW1lIGxvZ2ljIGFzIG1haW4gdGhyZWFkKQogIGxldCB0dzogc3RyaW5nIHwgdW5kZWZpbmVkID0gdHlwZW9mIHJlc3RQcm9wcy50dyA9PT0gInN0cmluZyIgPyByZXN0UHJvcHMudHcgOiB1bmRlZmluZWQ7CiAgaWYgKHR5cGVvZiBjbGFzc05hbWUgPT09ICJzdHJpbmciICYmIGNsYXNzTmFtZS5sZW5ndGggPiAwKSB7CiAgICB0dyA9IHR3ID8gdHcgKyAiICIgKyBjbGFzc05hbWUgOiBjbGFzc05hbWU7CiAgfQoKICAvLyBJbWFnZSBub2RlcyDihpIgVGFrdW1pIEltYWdlTm9kZQogIGlmIChub2RlLnR5cGUgPT09ICJpbWciICYmIHR5cGVvZiBzcmMgPT09ICJzdHJpbmciKSB7CiAgICByZXR1cm4gewogICAgICB0eXBlOiAiaW1hZ2UiLAogICAgICBzcmM6IHNyYyBhcyBzdHJpbmcsCiAgICAgIC4uLih0dyA/IHsgdHcgfSA6IHt9KSwKICAgICAgLi4ucmVzdFByb3BzLAogICAgfTsKICB9CgogIC8vIFNWRyBub2RlcyDihpIgVGFrdW1pIEltYWdlTm9kZSAoc2VyaWFsaXplIHN1YnRyZWUgdG8gU1ZHIG1hcmt1cCkKICBpZiAobm9kZS50eXBlID09PSAic3ZnIikgewogICAgY29uc3Qgc3ZnTWFya3VwID0gc2VyaWFsaXplU3ZnVHJlZShub2RlKTsKICAgIGNvbnN0IHdpZHRoID0gdHlwZW9mIG5vZGUucHJvcHMud2lkdGggPT09ICJudW1iZXIiID8gbm9kZS5wcm9wcy53aWR0aCA6IHVuZGVmaW5lZDsKICAgIGNvbnN0IGhlaWdodCA9IHR5cGVvZiBub2RlLnByb3BzLmhlaWdodCA9PT0gIm51bWJlciIgPyBub2RlLnByb3BzLmhlaWdodCA6IHVuZGVmaW5lZDsKICAgIHJldHVybiB7CiAgICAgIHR5cGU6ICJpbWFnZSIsCiAgICAgIHNyYzogc3ZnTWFya3VwLAogICAgICAuLi4od2lkdGggIT0gbnVsbCA/IHsgd2lkdGggfSA6IHt9KSwKICAgICAgLi4uKGhlaWdodCAhPSBudWxsID8geyBoZWlnaHQgfSA6IHt9KSwKICAgICAgLi4uKHR3ID8geyB0dyB9IDoge30pLAogICAgICAuLi4obm9kZS5wcm9wcy5zdHlsZSA/IHsgc3R5bGU6IG5vZGUucHJvcHMuc3R5bGUgfSA6IHt9KSwKICAgICAgdGFnTmFtZTogInN2ZyIsCiAgICB9OwogIH0KCiAgLy8gQWxsIG90aGVyIG5vZGVzIOKGkiBUYWt1bWkgQ29udGFpbmVyTm9kZQogIGNvbnN0IHRha3VtaUNoaWxkcmVuID0KICAgIG5vZGUuY2hpbGRyZW4ubGVuZ3RoID4gMCA/IG5vZGUuY2hpbGRyZW4ubWFwKHZub2RlVG9UYWt1bWlOb2RlKSA6IHVuZGVmaW5lZDsKCiAgcmV0dXJuIHsKICAgIHR5cGU6ICJjb250YWluZXIiLAogICAgLi4uKHR3ID8geyB0dyB9IDoge30pLAogICAgLi4ucmVzdFByb3BzLAogICAgLi4uKHRha3VtaUNoaWxkcmVuID8geyBjaGlsZHJlbjogdGFrdW1pQ2hpbGRyZW4gfSA6IHt9KSwKICB9Owp9CgovLyDilIDilIAgUm9vdCBzdHlsZSBjb25zdGFudCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCmNvbnN0IFJPT1RfU1RZTEUgPSB7IGRpc3BsYXk6ICJmbGV4Iiwgd2lkdGg6ICIxMDAlIiwgaGVpZ2h0OiAiMTAwJSIgfSBhcyBjb25zdDsKCi8vIOKUgOKUgCBXb3JrZXIgU3RhdGUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgpsZXQgcmVuZGVyZXI6IGltcG9ydCgiQHRha3VtaS1ycy9jb3JlIikuUmVuZGVyZXIgfCBudWxsID0gbnVsbDsKCi8vIOKUgOKUgCBNZXNzYWdlIEhhbmRsZXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgphc3luYyBmdW5jdGlvbiBoYW5kbGVNZXNzYWdlKG1zZzogV29ya2VyTWVzc2FnZSk6IFByb21pc2U8dm9pZD4gewogIHN3aXRjaCAobXNnLnR5cGUpIHsKICAgIGNhc2UgImluaXQiOiB7CiAgICAgIHRyeSB7CiAgICAgICAgLy8gRHluYW1pYyBpbXBvcnQg4oCUIG1heSBmYWlsIGlmIHRoZSBuYXRpdmUgYWRkb24gY2FuJ3QgbG9hZCBpbiBhIHdvcmtlcgogICAgICAgIGNvbnN0IGNvcmUgPSBhd2FpdCBpbXBvcnQoIkB0YWt1bWktcnMvY29yZSIpOwoKICAgICAgICByZW5kZXJlciA9IG5ldyBjb3JlLlJlbmRlcmVyKHsKICAgICAgICAgIGZvbnRzOiBtc2cuZm9udHMubWFwKChmKSA9PiAoewogICAgICAgICAgICBuYW1lOiBmLm5hbWUsCiAgICAgICAgICAgIGRhdGE6IGYuZGF0YSwKICAgICAgICAgICAgd2VpZ2h0OiBmLndlaWdodCBhcyAxMDAgfCAyMDAgfCAzMDAgfCA0MDAgfCA1MDAgfCA2MDAgfCA3MDAgfCA4MDAgfCA5MDAsCiAgICAgICAgICAgIHN0eWxlOiBmLnN0eWxlIGFzICJub3JtYWwiIHwgIml0YWxpYyIsCiAgICAgICAgICB9KSksCiAgICAgICAgfSk7CgogICAgICAgIHBhcmVudFBvcnQhLnBvc3RNZXNzYWdlKHsgdHlwZTogInJlYWR5IiB9KTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgcGFyZW50UG9ydCEucG9zdE1lc3NhZ2UoewogICAgICAgICAgdHlwZTogImVycm9yIiwKICAgICAgICAgIGlkOiAtMSwKICAgICAgICAgIGVycm9yOiBgV29ya2VyIGluaXQgZmFpbGVkOiAke2VyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKX1gLAogICAgICAgIH0pOwogICAgICB9CiAgICAgIGJyZWFrOwogICAgfQoKICAgIGNhc2UgInJlbmRlciI6IHsKICAgICAgaWYgKHJlbmRlcmVyID09IG51bGwpIHsKICAgICAgICBwYXJlbnRQb3J0IS5wb3N0TWVzc2FnZSh7CiAgICAgICAgICB0eXBlOiAiZXJyb3IiLAogICAgICAgICAgaWQ6IG1zZy5pZCwKICAgICAgICAgIGVycm9yOiAiV29ya2VyIG5vdCBpbml0aWFsaXplZCIsCiAgICAgICAgfSk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CgogICAgICB0cnkgewogICAgICAgIC8vIDEuIENvbnZlcnQgc2VyaWFsaXplZCBWTm9kZSBkYXRhIOKGkiBUYWt1bWkgbm9kZXMgZGlyZWN0bHkgKGJ5cGFzcyBmcm9tSnN4KQogICAgICAgIGNvbnN0IGNoaWxkcmVuID0gbXNnLnZub2Rlcy5tYXAodm5vZGVUb1Rha3VtaU5vZGUpOwogICAgICAgIGNvbnN0IHJvb3ROb2RlOiBUYWt1bWlOb2RlID0gewogICAgICAgICAgdHlwZTogImNvbnRhaW5lciIsCiAgICAgICAgICBzdHlsZTogUk9PVF9TVFlMRSwKICAgICAgICAgIGNoaWxkcmVuLAogICAgICAgIH07CgogICAgICAgIC8vIDIuIFJlbmRlciB0byByYXN0ZXIgaW1hZ2UKICAgICAgICBjb25zdCBidWZmZXIgPSBhd2FpdCByZW5kZXJlci5yZW5kZXIocm9vdE5vZGUsIHsKICAgICAgICAgIHdpZHRoOiBtc2cud2lkdGgsCiAgICAgICAgICBoZWlnaHQ6IG1zZy5oZWlnaHQsCiAgICAgICAgICBmb3JtYXQ6IG1zZy5mb3JtYXQgYXMgaW1wb3J0KCJAdGFrdW1pLXJzL2NvcmUiKS5PdXRwdXRGb3JtYXQsCiAgICAgICAgICBkZXZpY2VQaXhlbFJhdGlvOiBtc2cuZHByLAogICAgICAgIH0pOwoKICAgICAgICAvLyBUcmFuc2ZlciB0aGUgYnVmZmVyICh6ZXJvLWNvcHkpIGJhY2sgdG8gdGhlIG1haW4gdGhyZWFkCiAgICAgICAgY29uc3QgYWIgPQogICAgICAgICAgYnVmZmVyIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIKICAgICAgICAgICAgPyBidWZmZXIKICAgICAgICAgICAgOiBidWZmZXIuYnVmZmVyLnNsaWNlKGJ1ZmZlci5ieXRlT2Zmc2V0LCBidWZmZXIuYnl0ZU9mZnNldCArIGJ1ZmZlci5ieXRlTGVuZ3RoKTsKCiAgICAgICAgcGFyZW50UG9ydCEucG9zdE1lc3NhZ2UoCiAgICAgICAgICB7IHR5cGU6ICJyZXN1bHQiLCBpZDogbXNnLmlkLCBidWZmZXI6IGFiIH0sCiAgICAgICAgICB7IHRyYW5zZmVyOiBbYWIgYXMgQXJyYXlCdWZmZXJdIH0sCiAgICAgICAgKTsKICAgICAgfSBjYXRjaCAoZXJyKSB7CiAgICAgICAgcGFyZW50UG9ydCEucG9zdE1lc3NhZ2UoewogICAgICAgICAgdHlwZTogImVycm9yIiwKICAgICAgICAgIGlkOiBtc2cuaWQsCiAgICAgICAgICBlcnJvcjogZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLAogICAgICAgIH0pOwogICAgICB9CiAgICAgIGJyZWFrOwogICAgfQoKICAgIGNhc2UgInNodXRkb3duIjogewogICAgICBwcm9jZXNzLmV4aXQoMCk7CiAgICB9CiAgfQp9CgovLyDilIDilIAgQXV0by1pbml0IGlmIGZvbnRzIHByb3ZpZGVkIHZpYSB3b3JrZXJEYXRhIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKaWYgKHdvcmtlckRhdGE/LmZvbnRzKSB7CiAgaGFuZGxlTWVzc2FnZSh7IHR5cGU6ICJpbml0IiwgZm9udHM6IHdvcmtlckRhdGEuZm9udHMgfSk7Cn0KCnBhcmVudFBvcnQhLm9uKCJtZXNzYWdlIiwgKG1zZzogV29ya2VyTWVzc2FnZSkgPT4gewogIGhhbmRsZU1lc3NhZ2UobXNnKTsKfSk7Cg==", "" + import.meta.url), { workerData: { fonts: this.fonts.map((f) => ({
|
|
40
|
+
name: f.name,
|
|
41
|
+
data: f.data,
|
|
42
|
+
weight: f.weight,
|
|
43
|
+
style: f.style
|
|
44
|
+
})) } });
|
|
45
|
+
await new Promise((resolve, reject) => {
|
|
46
|
+
const timeout = setTimeout(() => {
|
|
47
|
+
reject(/* @__PURE__ */ new Error("Worker initialization timed out (5s)"));
|
|
48
|
+
}, 5e3);
|
|
49
|
+
const onMessage = (msg) => {
|
|
50
|
+
if (msg.type === "ready") {
|
|
51
|
+
clearTimeout(timeout);
|
|
52
|
+
this.worker.off("message", onMessage);
|
|
53
|
+
this.worker.off("error", onError);
|
|
54
|
+
resolve();
|
|
55
|
+
} else if (msg.type === "error" && msg.id === -1) {
|
|
56
|
+
clearTimeout(timeout);
|
|
57
|
+
this.worker.off("message", onMessage);
|
|
58
|
+
this.worker.off("error", onError);
|
|
59
|
+
reject(new Error(msg.error ?? "Unknown worker init error"));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const onError = (err) => {
|
|
63
|
+
clearTimeout(timeout);
|
|
64
|
+
reject(err);
|
|
65
|
+
};
|
|
66
|
+
this.worker.on("message", onMessage);
|
|
67
|
+
this.worker.on("error", onError);
|
|
68
|
+
});
|
|
69
|
+
this.worker.on("message", this.handleResponse.bind(this));
|
|
70
|
+
this.worker.on("exit", (code) => {
|
|
71
|
+
if (code !== 0) console.warn(`[@fcannizzaro/streamdeck-react] Render worker exited with code ${code}`);
|
|
72
|
+
this.handleWorkerDeath();
|
|
73
|
+
});
|
|
74
|
+
this.worker.on("error", (err) => {
|
|
75
|
+
console.error("[@fcannizzaro/streamdeck-react] Render worker error:", err);
|
|
76
|
+
this.handleWorkerDeath();
|
|
77
|
+
});
|
|
78
|
+
this.ready = true;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.warn("[@fcannizzaro/streamdeck-react] Worker initialization failed, falling back to main-thread rendering:", err instanceof Error ? err.message : err);
|
|
81
|
+
this.failed = true;
|
|
82
|
+
this.worker?.terminate().catch(() => {});
|
|
83
|
+
this.worker = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Whether the worker is available for offloaded rendering. */
|
|
87
|
+
get isAvailable() {
|
|
88
|
+
return this.ready && this.worker != null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Render VNode children in the worker thread.
|
|
92
|
+
* Returns the raw raster buffer.
|
|
93
|
+
*/
|
|
94
|
+
async render(vnodes, width, height, format, dpr) {
|
|
95
|
+
if (!this.isAvailable) throw new Error("Worker not available");
|
|
96
|
+
const id = this.nextId++;
|
|
97
|
+
const serialized = vnodes.map(serializeVNode);
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
this.pending.set(id, {
|
|
100
|
+
resolve,
|
|
101
|
+
reject
|
|
102
|
+
});
|
|
103
|
+
this.worker.postMessage({
|
|
104
|
+
type: "render",
|
|
105
|
+
id,
|
|
106
|
+
vnodes: serialized,
|
|
107
|
+
width,
|
|
108
|
+
height,
|
|
109
|
+
format,
|
|
110
|
+
dpr
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/** Gracefully shut down the worker. */
|
|
115
|
+
async shutdown() {
|
|
116
|
+
if (this.worker != null) {
|
|
117
|
+
for (const [_, req] of this.pending) req.reject(/* @__PURE__ */ new Error("Worker shutting down"));
|
|
118
|
+
this.pending.clear();
|
|
119
|
+
this.worker.postMessage({ type: "shutdown" });
|
|
120
|
+
await this.worker.terminate();
|
|
121
|
+
this.worker = null;
|
|
122
|
+
}
|
|
123
|
+
this.ready = false;
|
|
124
|
+
}
|
|
125
|
+
handleResponse(msg) {
|
|
126
|
+
if (msg.type === "ready") return;
|
|
127
|
+
const req = this.pending.get(msg.id);
|
|
128
|
+
if (req == null) return;
|
|
129
|
+
this.pending.delete(msg.id);
|
|
130
|
+
if (msg.type === "result" && msg.buffer != null) req.resolve(Buffer.from(msg.buffer));
|
|
131
|
+
else if (msg.type === "error") req.reject(new Error(msg.error ?? "Unknown worker render error"));
|
|
132
|
+
}
|
|
133
|
+
handleWorkerDeath() {
|
|
134
|
+
this.ready = false;
|
|
135
|
+
this.worker = null;
|
|
136
|
+
for (const [_, req] of this.pending) req.reject(/* @__PURE__ */ new Error("Worker died unexpectedly"));
|
|
137
|
+
this.pending.clear();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
//#endregion
|
|
141
|
+
export { RenderPool };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { VNode } from '../reconciler/vnode';
|
|
2
|
+
/**
|
|
3
|
+
* Serialize an `<svg>` VNode (and its entire subtree) to an SVG markup string.
|
|
4
|
+
* Auto-injects `xmlns="http://www.w3.org/2000/svg"` if not present.
|
|
5
|
+
* The returned string can be used as the `src` of a Takumi ImageNode.
|
|
6
|
+
*/
|
|
7
|
+
export declare function serializeSvgTree(svgNode: VNode): string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//#region src/render/svg.ts
|
|
2
|
+
var SVG_CAMEL_ATTRS = new Set([
|
|
3
|
+
"accentHeight",
|
|
4
|
+
"alignmentBaseline",
|
|
5
|
+
"arabicForm",
|
|
6
|
+
"baselineShift",
|
|
7
|
+
"capHeight",
|
|
8
|
+
"clipPath",
|
|
9
|
+
"clipPathUnits",
|
|
10
|
+
"clipRule",
|
|
11
|
+
"colorInterpolation",
|
|
12
|
+
"colorInterpolationFilters",
|
|
13
|
+
"colorProfile",
|
|
14
|
+
"colorRendering",
|
|
15
|
+
"enableBackground",
|
|
16
|
+
"fillOpacity",
|
|
17
|
+
"fillRule",
|
|
18
|
+
"floodColor",
|
|
19
|
+
"floodOpacity",
|
|
20
|
+
"fontFamily",
|
|
21
|
+
"fontSize",
|
|
22
|
+
"fontSizeAdjust",
|
|
23
|
+
"fontStretch",
|
|
24
|
+
"fontStyle",
|
|
25
|
+
"fontVariant",
|
|
26
|
+
"fontWeight",
|
|
27
|
+
"glyphName",
|
|
28
|
+
"glyphOrientationHorizontal",
|
|
29
|
+
"glyphOrientationVertical",
|
|
30
|
+
"horizAdvX",
|
|
31
|
+
"horizOriginX",
|
|
32
|
+
"imageRendering",
|
|
33
|
+
"letterSpacing",
|
|
34
|
+
"lightingColor",
|
|
35
|
+
"markerEnd",
|
|
36
|
+
"markerMid",
|
|
37
|
+
"markerStart",
|
|
38
|
+
"overlinePosition",
|
|
39
|
+
"overlineThickness",
|
|
40
|
+
"paintOrder",
|
|
41
|
+
"pointerEvents",
|
|
42
|
+
"preserveAspectRatio",
|
|
43
|
+
"shapeRendering",
|
|
44
|
+
"stopColor",
|
|
45
|
+
"stopOpacity",
|
|
46
|
+
"strokeDasharray",
|
|
47
|
+
"strokeDashoffset",
|
|
48
|
+
"strokeLinecap",
|
|
49
|
+
"strokeLinejoin",
|
|
50
|
+
"strokeMiterlimit",
|
|
51
|
+
"strokeOpacity",
|
|
52
|
+
"strokeWidth",
|
|
53
|
+
"textAnchor",
|
|
54
|
+
"textDecoration",
|
|
55
|
+
"textRendering",
|
|
56
|
+
"transformOrigin",
|
|
57
|
+
"underlinePosition",
|
|
58
|
+
"underlineThickness",
|
|
59
|
+
"unicodeBidi",
|
|
60
|
+
"unicodeRange",
|
|
61
|
+
"unitsPerEm",
|
|
62
|
+
"vAlphabetic",
|
|
63
|
+
"vHanging",
|
|
64
|
+
"vIdeographic",
|
|
65
|
+
"vMathematical",
|
|
66
|
+
"vectorEffect",
|
|
67
|
+
"vertAdvY",
|
|
68
|
+
"vertOriginX",
|
|
69
|
+
"vertOriginY",
|
|
70
|
+
"wordSpacing",
|
|
71
|
+
"writingMode"
|
|
72
|
+
]);
|
|
73
|
+
var SKIP_PROPS = new Set([
|
|
74
|
+
"children",
|
|
75
|
+
"key",
|
|
76
|
+
"ref",
|
|
77
|
+
"__self",
|
|
78
|
+
"__source"
|
|
79
|
+
]);
|
|
80
|
+
function camelToKebab(str) {
|
|
81
|
+
return str.replace(/[A-Z]/g, (ch) => `-${ch.toLowerCase()}`);
|
|
82
|
+
}
|
|
83
|
+
function escapeAttr(value) {
|
|
84
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
85
|
+
}
|
|
86
|
+
function serializeStyle(style) {
|
|
87
|
+
const parts = [];
|
|
88
|
+
for (const key of Object.keys(style)) {
|
|
89
|
+
const value = style[key];
|
|
90
|
+
if (value == null) continue;
|
|
91
|
+
parts.push(`${camelToKebab(key)}:${String(value).trim()}`);
|
|
92
|
+
}
|
|
93
|
+
return parts.join(";");
|
|
94
|
+
}
|
|
95
|
+
function serializeAttr(key, value) {
|
|
96
|
+
if (SKIP_PROPS.has(key) || value == null) return null;
|
|
97
|
+
let attrName;
|
|
98
|
+
if (key === "className") attrName = "class";
|
|
99
|
+
else if (SVG_CAMEL_ATTRS.has(key)) attrName = camelToKebab(key);
|
|
100
|
+
else attrName = key;
|
|
101
|
+
if (key === "style" && typeof value === "object") {
|
|
102
|
+
const css = serializeStyle(value);
|
|
103
|
+
if (!css) return null;
|
|
104
|
+
return `${attrName}="${escapeAttr(css)}"`;
|
|
105
|
+
}
|
|
106
|
+
if (typeof value === "boolean") return `${attrName}="${String(value)}"`;
|
|
107
|
+
return `${attrName}="${escapeAttr(String(value))}"`;
|
|
108
|
+
}
|
|
109
|
+
function serializeVNode(node) {
|
|
110
|
+
if (node.type === "#text") return node.text ?? "";
|
|
111
|
+
const attrs = [];
|
|
112
|
+
for (const [key, value] of Object.entries(node.props)) {
|
|
113
|
+
const attr = serializeAttr(key, value);
|
|
114
|
+
if (attr != null) attrs.push(attr);
|
|
115
|
+
}
|
|
116
|
+
const childMarkup = node.children.map(serializeVNode).join("");
|
|
117
|
+
const attrStr = attrs.length > 0 ? ` ${attrs.join(" ")}` : "";
|
|
118
|
+
return `<${node.type}${attrStr}>${childMarkup}</${node.type}>`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Serialize an `<svg>` VNode (and its entire subtree) to an SVG markup string.
|
|
122
|
+
* Auto-injects `xmlns="http://www.w3.org/2000/svg"` if not present.
|
|
123
|
+
* The returned string can be used as the `src` of a Takumi ImageNode.
|
|
124
|
+
*/
|
|
125
|
+
function serializeSvgTree(svgNode) {
|
|
126
|
+
if (!("xmlns" in svgNode.props)) {
|
|
127
|
+
const original = svgNode.props;
|
|
128
|
+
svgNode.props = {
|
|
129
|
+
...original,
|
|
130
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
131
|
+
};
|
|
132
|
+
const result = serializeVNode(svgNode);
|
|
133
|
+
svgNode.props = original;
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
return serializeVNode(svgNode);
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { serializeSvgTree };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/rollup.d.ts
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
import { Plugin } from 'rollup';
|
|
2
2
|
import { StreamDeckTargetOptions } from './bundler-shared';
|
|
3
3
|
export type { StreamDeckPlatform, StreamDeckArch, StreamDeckTarget, StreamDeckTargetOptions, } from './bundler-shared';
|
|
4
|
+
export interface StreamDeckRollupOptions extends StreamDeckTargetOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Path to the plugin `manifest.json`. When omitted, the plugin
|
|
7
|
+
* auto-detects by scanning the project root for a `*.sdPlugin/manifest.json`.
|
|
8
|
+
*
|
|
9
|
+
* Set to `false` to disable manifest type generation entirely.
|
|
10
|
+
*/
|
|
11
|
+
manifest?: string | false;
|
|
12
|
+
}
|
|
4
13
|
/**
|
|
5
|
-
* Rollup plugin
|
|
6
|
-
* binding (`.node` file) into the bundle output directory so the Stream Deck
|
|
7
|
-
* Node.js runtime can find it at startup.
|
|
14
|
+
* Rollup plugin for Stream Deck React projects.
|
|
8
15
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* filesystem access is needed.
|
|
16
|
+
* Same responsibilities as the Vite plugin (see vite.ts) with
|
|
17
|
+
* Rollup-specific differences:
|
|
12
18
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
19
|
+
* - No `configResolved` — dev mode detected via `this.meta.watchMode`
|
|
20
|
+
* - `onLog` suppresses `MODULE_LEVEL_DIRECTIVE` warnings (caused by
|
|
21
|
+
* "use client" directives in React dependencies)
|
|
22
|
+
* - `buildStart` resolves root via `resolve(".")` (no Vite config)
|
|
23
|
+
* - `writeBundle` reads output dir from `outputOptions.dir` or
|
|
24
|
+
* `dirname(outputOptions.file)` (Rollup supports both)
|
|
25
|
+
* - No `closeBundle` restart hook (handled externally)
|
|
26
|
+
*
|
|
27
|
+
* Font inlining and manifest codegen are shared via bundler-shared.ts,
|
|
28
|
+
* font-inline.ts, and manifest-codegen.ts.
|
|
15
29
|
*/
|
|
16
|
-
export declare function streamDeckReact(options?:
|
|
30
|
+
export declare function streamDeckReact(options?: StreamDeckRollupOptions): Plugin;
|
package/dist/rollup.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { NOOP_DEVTOOLS_CODE, NOOP_DEVTOOLS_ID, copyNativeBindings, isDevelopmentMode, isLibraryDevtoolsImport, shouldStripDevtools } from "./bundler-shared.js";
|
|
2
2
|
import { loadFont, resolveFontId } from "./font-inline.js";
|
|
3
|
-
import {
|
|
3
|
+
import { generateManifestTypes } from "./manifest-codegen.js";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
4
5
|
//#region src/rollup.ts
|
|
5
6
|
/**
|
|
6
|
-
* Rollup plugin
|
|
7
|
-
* binding (`.node` file) into the bundle output directory so the Stream Deck
|
|
8
|
-
* Node.js runtime can find it at startup.
|
|
7
|
+
* Rollup plugin for Stream Deck React projects.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* filesystem access is needed.
|
|
9
|
+
* Same responsibilities as the Vite plugin (see vite.ts) with
|
|
10
|
+
* Rollup-specific differences:
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* - No `configResolved` — dev mode detected via `this.meta.watchMode`
|
|
13
|
+
* - `onLog` suppresses `MODULE_LEVEL_DIRECTIVE` warnings (caused by
|
|
14
|
+
* "use client" directives in React dependencies)
|
|
15
|
+
* - `buildStart` resolves root via `resolve(".")` (no Vite config)
|
|
16
|
+
* - `writeBundle` reads output dir from `outputOptions.dir` or
|
|
17
|
+
* `dirname(outputOptions.file)` (Rollup supports both)
|
|
18
|
+
* - No `closeBundle` restart hook (handled externally)
|
|
19
|
+
*
|
|
20
|
+
* Font inlining and manifest codegen are shared via bundler-shared.ts,
|
|
21
|
+
* font-inline.ts, and manifest-codegen.ts.
|
|
16
22
|
*/
|
|
17
23
|
function streamDeckReact(options = {}) {
|
|
18
24
|
return {
|
|
@@ -20,6 +26,15 @@ function streamDeckReact(options = {}) {
|
|
|
20
26
|
onLog(_level, log) {
|
|
21
27
|
if (log.code === "MODULE_LEVEL_DIRECTIVE") return false;
|
|
22
28
|
},
|
|
29
|
+
buildStart() {
|
|
30
|
+
const root = resolve(".");
|
|
31
|
+
const warn = (msg) => this.warn(msg);
|
|
32
|
+
const result = generateManifestTypes(root, options.manifest, warn);
|
|
33
|
+
if (result) {
|
|
34
|
+
this.addWatchFile(result.manifestPath);
|
|
35
|
+
if (result.written) console.log("[@fcannizzaro/streamdeck-react] Generated src/streamdeck-env.d.ts");
|
|
36
|
+
}
|
|
37
|
+
},
|
|
23
38
|
resolveId: {
|
|
24
39
|
order: "pre",
|
|
25
40
|
handler(source, importer) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Any root that can participate in prioritized flushing. */
|
|
2
|
+
export interface FlushableRoot {
|
|
3
|
+
/** Current render priority. 0 = animating (highest), 3 = idle (lowest). */
|
|
4
|
+
readonly priority: number;
|
|
5
|
+
/** Execute the flush (render + push to hardware). */
|
|
6
|
+
executeFlush(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export declare class FlushCoordinator {
|
|
9
|
+
private pending;
|
|
10
|
+
private scheduled;
|
|
11
|
+
private draining;
|
|
12
|
+
/**
|
|
13
|
+
* Enqueue a root for flushing. Flushes are batched via microtask
|
|
14
|
+
* and processed in priority order.
|
|
15
|
+
*/
|
|
16
|
+
requestFlush(root: FlushableRoot): void;
|
|
17
|
+
private drain;
|
|
18
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/roots/flush-coordinator.ts
|
|
2
|
+
var FlushCoordinator = class {
|
|
3
|
+
pending = /* @__PURE__ */ new Set();
|
|
4
|
+
scheduled = false;
|
|
5
|
+
draining = false;
|
|
6
|
+
/**
|
|
7
|
+
* Enqueue a root for flushing. Flushes are batched via microtask
|
|
8
|
+
* and processed in priority order.
|
|
9
|
+
*/
|
|
10
|
+
requestFlush(root) {
|
|
11
|
+
this.pending.add(root);
|
|
12
|
+
if (!this.scheduled && !this.draining) {
|
|
13
|
+
this.scheduled = true;
|
|
14
|
+
queueMicrotask(() => this.drain());
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async drain() {
|
|
18
|
+
this.scheduled = false;
|
|
19
|
+
if (this.draining || this.pending.size === 0) return;
|
|
20
|
+
this.draining = true;
|
|
21
|
+
try {
|
|
22
|
+
while (this.pending.size > 0) {
|
|
23
|
+
const roots = [...this.pending];
|
|
24
|
+
this.pending.clear();
|
|
25
|
+
roots.sort((a, b) => a.priority - b.priority);
|
|
26
|
+
for (const root of roots) await root.executeFlush();
|
|
27
|
+
}
|
|
28
|
+
} finally {
|
|
29
|
+
this.draining = false;
|
|
30
|
+
if (this.pending.size > 0 && !this.scheduled) {
|
|
31
|
+
this.scheduled = true;
|
|
32
|
+
queueMicrotask(() => this.drain());
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { FlushCoordinator };
|
package/dist/roots/registry.d.ts
CHANGED
|
@@ -6,24 +6,26 @@ import { RenderConfig } from '../render/pipeline';
|
|
|
6
6
|
import { RegistryObserver } from '../devtools/observers/lifecycle';
|
|
7
7
|
export declare class RootRegistry {
|
|
8
8
|
private roots;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
9
|
+
private touchStripRoots;
|
|
10
|
+
private touchStripActions;
|
|
11
11
|
private renderConfig;
|
|
12
12
|
private renderDebounceMs;
|
|
13
13
|
private sdkInstance;
|
|
14
14
|
private globalSettings;
|
|
15
15
|
private onGlobalSettingsChange;
|
|
16
16
|
private wrapper?;
|
|
17
|
+
private flushCoordinator;
|
|
17
18
|
/** DevTools observer. Set externally by startDevtoolsServer(). null when devtools is off. */
|
|
18
19
|
observer: RegistryObserver | null;
|
|
19
20
|
constructor(renderConfig: RenderConfig, renderDebounceMs: number, sdkInstance: StreamDeckAccess["sdk"], onGlobalSettingsChange: (settings: JsonObject) => Promise<void>, wrapper?: WrapperComponent);
|
|
20
21
|
setGlobalSettings(settings: JsonObject): void;
|
|
21
22
|
create(ev: WillAppearEvent<JsonObject>, component: ComponentType, definition: ActionDefinition): void;
|
|
22
|
-
private
|
|
23
|
+
private registerTouchStripColumn;
|
|
23
24
|
private getEncoderColumn;
|
|
24
25
|
destroy(contextId: string): void;
|
|
26
|
+
private static readonly INTERACTION_EVENTS;
|
|
25
27
|
dispatch<K extends keyof EventMap>(contextId: string, event: K, payload: EventMap[K]): void;
|
|
26
|
-
private
|
|
28
|
+
private dispatchToTouchStrip;
|
|
27
29
|
updateSettings(contextId: string, settings: JsonObject): void;
|
|
28
30
|
destroyAll(): void;
|
|
29
31
|
}
|