shaders 2.5.114 → 2.5.115
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/js/createPreview.d.ts +7 -3
- package/dist/js/createPreview.d.ts.map +1 -1
- package/dist/js/createPreview.js +110 -4
- package/dist/js/createShader.js +1 -1
- package/dist/js/types.d.ts +32 -0
- package/dist/js/types.d.ts.map +1 -1
- package/dist/react/Preview.js +113 -9
- package/dist/react/Shader.js +1 -1
- package/dist/react/bundle.js +183 -183
- package/dist/react/engine/Preview.d.ts +29 -0
- package/dist/react/engine/Preview.d.ts.map +1 -1
- package/dist/solid/engine/Preview.d.ts +18 -0
- package/dist/solid/engine/Preview.d.ts.map +1 -1
- package/dist/solid/engine/Preview.js +69 -2
- package/dist/solid/engine/Shader.js +1 -1
- package/dist/svelte/engine/Preview.svelte.d.ts +18 -0
- package/dist/svelte/index.js +124 -13
- package/dist/svelte/source/engine/Preview.svelte +134 -10
- package/dist/vue/Preview.vue_vue_type_script_setup_true_lang.js +111 -8
- package/dist/vue/Shader.vue_vue_type_script_setup_true_lang.js +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,16 @@ export interface ShaderDefinition {
|
|
|
15
15
|
colorSpace?: 'p3-linear' | 'srgb';
|
|
16
16
|
devicePreview?: string;
|
|
17
17
|
}
|
|
18
|
+
export interface KeyPropTarget {
|
|
19
|
+
component_id: string;
|
|
20
|
+
prop: string;
|
|
21
|
+
}
|
|
22
|
+
export interface KeyProp {
|
|
23
|
+
label: string;
|
|
24
|
+
type: 'color' | 'range' | 'logo' | 'image';
|
|
25
|
+
category?: string;
|
|
26
|
+
targets: KeyPropTarget[];
|
|
27
|
+
}
|
|
18
28
|
export interface PreviewProps {
|
|
19
29
|
shader?: string;
|
|
20
30
|
presetId?: string;
|
|
@@ -24,6 +34,25 @@ export interface PreviewProps {
|
|
|
24
34
|
watermarkLink?: string;
|
|
25
35
|
style?: CSSProperties | string;
|
|
26
36
|
className?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Map of camelCase key_prop identifier → user value, layered on top of the
|
|
39
|
+
* preset's authored defaults. Identifiers are derived from `key_prop.label`
|
|
40
|
+
* via the same slugify rules the Pro Framer exporter uses, so identifiers
|
|
41
|
+
* generated by codegen and identifiers expected here always match.
|
|
42
|
+
*
|
|
43
|
+
* Only applies on the `presetId` path — `shader` token previews don't carry
|
|
44
|
+
* key_props and silently ignore this prop.
|
|
45
|
+
*/
|
|
46
|
+
configuration?: Record<string, unknown>;
|
|
47
|
+
/**
|
|
48
|
+
* Lock the preview to a specific snapshot of the preset, addressed by
|
|
49
|
+
* content hash. Customized snippets pin a version so subsequent upstream
|
|
50
|
+
* edits (label renames, key_prop regenerations) don't silently break the
|
|
51
|
+
* configuration. Server falls back to latest with the
|
|
52
|
+
* `X-Shaders-Version-Fallback` header when the requested snapshot is
|
|
53
|
+
* missing. Only meaningful on the `presetId` path.
|
|
54
|
+
*/
|
|
55
|
+
version?: string;
|
|
27
56
|
[key: string]: any;
|
|
28
57
|
}
|
|
29
58
|
export declare const Preview: FC<PreviewProps>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/engine/Preview.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4E,KAAK,aAAa,EAAE,KAAK,EAAE,EAA4B,MAAM,OAAO,CAAA;AAKvJ,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IACjC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;
|
|
1
|
+
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/engine/Preview.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4E,KAAK,aAAa,EAAE,KAAK,EAAE,EAA4B,MAAM,OAAO,CAAA;AAKvJ,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC3B,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IACjC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAA;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAgPD,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAAA;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CACnB;AA6BD,eAAO,MAAM,OAAO,EAAE,EAAE,CAAC,YAAY,CAwIpC,CAAA;AAED,eAAe,OAAO,CAAA"}
|
|
@@ -8,6 +8,24 @@ export interface PreviewProps {
|
|
|
8
8
|
class?: string;
|
|
9
9
|
watermarkText?: string;
|
|
10
10
|
watermarkLink?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Map of camelCase key_prop identifier → user value, layered on top of the
|
|
13
|
+
* preset's authored defaults. Identifiers are derived from `key_prop.label`
|
|
14
|
+
* via the same slugify rules the Pro Framer exporter uses, so identifiers
|
|
15
|
+
* generated by codegen and identifiers expected here always match.
|
|
16
|
+
*
|
|
17
|
+
* Only applies on the `presetId` path — `shader` token previews don't carry
|
|
18
|
+
* key_props and silently ignore this prop.
|
|
19
|
+
*/
|
|
20
|
+
configuration?: Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Lock the preview to a specific snapshot of the preset, addressed by
|
|
23
|
+
* content hash. Customized snippets pin a version so subsequent upstream
|
|
24
|
+
* edits don't silently break the configuration. Server falls back to
|
|
25
|
+
* latest with the `X-Shaders-Version-Fallback` header when the requested
|
|
26
|
+
* snapshot is missing. Only meaningful on the `presetId` path.
|
|
27
|
+
*/
|
|
28
|
+
version?: string;
|
|
11
29
|
}
|
|
12
30
|
export default function Preview(props: PreviewProps): JSX.Element;
|
|
13
31
|
//# sourceMappingURL=Preview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/engine/Preview.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,GAAG,EAAoC,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"Preview.d.ts","sourceRoot":"","sources":["../../src/engine/Preview.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,GAAG,EAAoC,MAAM,UAAU,CAAA;AA6ZlH,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,GAAG,CAAC,aAAa,GAAG,MAAM,CAAA;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAID,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,KAAK,EAAE,YAAY,eA6HlD"}
|
|
@@ -243,6 +243,64 @@ function decodePreviewDefinition(encoded, key) {
|
|
|
243
243
|
throw new Error("Failed to decode shader definition: invalid data or incorrect key");
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
+
const RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set(["break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof", "new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while", "with", "yield"]);
|
|
247
|
+
function slugifyIdentifier(label, fallback) {
|
|
248
|
+
const cleaned = label.replace(/[^A-Za-z0-9]+/g, " ").trim();
|
|
249
|
+
if (!cleaned) return fallback;
|
|
250
|
+
const parts = cleaned.split(/\s+/);
|
|
251
|
+
const camel = parts.map((part, i) => i === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
252
|
+
const safe = /^[A-Za-z_$]/.test(camel) ? camel : `_${camel}`;
|
|
253
|
+
return RESERVED_IDENTIFIERS.has(safe) ? `_${safe}` : safe;
|
|
254
|
+
}
|
|
255
|
+
function buildKeyPropIdentifierMap(keyProps) {
|
|
256
|
+
const used = /* @__PURE__ */ new Set(["style"]);
|
|
257
|
+
const map = /* @__PURE__ */ new Map();
|
|
258
|
+
if (!(keyProps == null ? void 0 : keyProps.length)) return map;
|
|
259
|
+
keyProps.forEach((kp, i) => {
|
|
260
|
+
var _a;
|
|
261
|
+
if (!((_a = kp.targets) == null ? void 0 : _a.length)) return;
|
|
262
|
+
const base = slugifyIdentifier(kp.label, `keyProp${i}`);
|
|
263
|
+
let ident = base;
|
|
264
|
+
let suffix = 1;
|
|
265
|
+
while (used.has(ident)) {
|
|
266
|
+
suffix++;
|
|
267
|
+
ident = `${base}${suffix}`;
|
|
268
|
+
}
|
|
269
|
+
used.add(ident);
|
|
270
|
+
map.set(ident, kp);
|
|
271
|
+
});
|
|
272
|
+
return map;
|
|
273
|
+
}
|
|
274
|
+
function findInTree(id, list) {
|
|
275
|
+
var _a;
|
|
276
|
+
for (const c of list) {
|
|
277
|
+
if (c.id === id) return c;
|
|
278
|
+
if ((_a = c.children) == null ? void 0 : _a.length) {
|
|
279
|
+
const hit = findInTree(id, c.children);
|
|
280
|
+
if (hit) return hit;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
function applyConfiguration(definition, configuration, keyProps) {
|
|
286
|
+
if (!configuration || !(keyProps == null ? void 0 : keyProps.length)) return definition;
|
|
287
|
+
const entries = Object.entries(configuration);
|
|
288
|
+
if (entries.length === 0) return definition;
|
|
289
|
+
const identMap = buildKeyPropIdentifierMap(keyProps);
|
|
290
|
+
const cloned = JSON.parse(JSON.stringify(definition.components));
|
|
291
|
+
for (const [ident, value] of entries) {
|
|
292
|
+
const kp = identMap.get(ident);
|
|
293
|
+
if (!kp) continue;
|
|
294
|
+
for (const t of kp.targets ?? []) {
|
|
295
|
+
const comp = findInTree(t.component_id, cloned);
|
|
296
|
+
if ((comp == null ? void 0 : comp.props) && t.prop in comp.props) comp.props[t.prop] = value;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
...definition,
|
|
301
|
+
components: cloned
|
|
302
|
+
};
|
|
303
|
+
}
|
|
246
304
|
function RenderComponent(props) {
|
|
247
305
|
const Component = componentMap[props.config.type];
|
|
248
306
|
if (!Component) return null;
|
|
@@ -297,9 +355,11 @@ function Preview(props) {
|
|
|
297
355
|
console.warn("[Shaders] The Preview component requires a Shaders license for production use. Visit https://shaders.com for more information.");
|
|
298
356
|
}
|
|
299
357
|
const [fetchedDefinition, setFetchedDefinition] = createSignal(null);
|
|
358
|
+
const [fetchedKeyProps, setFetchedKeyProps] = createSignal(null);
|
|
300
359
|
function fetchAndDecode(url) {
|
|
301
360
|
const key = props.obfuscationKey || DEFAULT_KEY;
|
|
302
361
|
setFetchedDefinition(null);
|
|
362
|
+
setFetchedKeyProps(null);
|
|
303
363
|
fetch(url).then((res) => {
|
|
304
364
|
if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`);
|
|
305
365
|
return res.json();
|
|
@@ -307,6 +367,7 @@ function Preview(props) {
|
|
|
307
367
|
const item = data.preset || data.shader;
|
|
308
368
|
if ((item == null ? void 0 : item.definition) && typeof item.definition === "string") {
|
|
309
369
|
setFetchedDefinition(decodePreviewDefinition(item.definition, key));
|
|
370
|
+
setFetchedKeyProps(Array.isArray(item.key_props) ? item.key_props : null);
|
|
310
371
|
}
|
|
311
372
|
}).catch((err) => {
|
|
312
373
|
console.error("[Shaders Preview] Failed to fetch preview:", err);
|
|
@@ -319,19 +380,25 @@ function Preview(props) {
|
|
|
319
380
|
fetchAndDecode(`${baseUrl}/api/preview/shader/${token}`);
|
|
320
381
|
} else if (!props.presetId) {
|
|
321
382
|
setFetchedDefinition(null);
|
|
383
|
+
setFetchedKeyProps(null);
|
|
322
384
|
}
|
|
323
385
|
});
|
|
324
386
|
createEffect(() => {
|
|
325
387
|
const id = props.presetId;
|
|
388
|
+
const version = props.version;
|
|
326
389
|
const baseUrl = props.apiBaseUrl || "https://shaders.com";
|
|
327
390
|
if (id) {
|
|
328
|
-
|
|
391
|
+
const versionParam = version ? `?version=${encodeURIComponent(version)}` : "";
|
|
392
|
+
fetchAndDecode(`${baseUrl}/api/preview/preset/${id}${versionParam}`);
|
|
329
393
|
} else if (!props.shader) {
|
|
330
394
|
setFetchedDefinition(null);
|
|
395
|
+
setFetchedKeyProps(null);
|
|
331
396
|
}
|
|
332
397
|
});
|
|
333
398
|
const definition = createMemo(() => {
|
|
334
|
-
|
|
399
|
+
const def = fetchedDefinition();
|
|
400
|
+
if (!def) return null;
|
|
401
|
+
return applyConfiguration(def, props.configuration, fetchedKeyProps());
|
|
335
402
|
});
|
|
336
403
|
const watermarkText = () => props.watermarkText || "Unlock your Shaders Pro license";
|
|
337
404
|
const watermarkLink = () => props.watermarkLink || "https://shaders.com/dashboard?pricing=true";
|
|
@@ -67,7 +67,7 @@ function Shader(allProps) {
|
|
|
67
67
|
const checkRendering = () => {
|
|
68
68
|
const stats = rendererInstance.getPerformanceStats();
|
|
69
69
|
if (stats.fps > 0) {
|
|
70
|
-
const version = "2.5.
|
|
70
|
+
const version = "2.5.115";
|
|
71
71
|
telemetryCollector = startTelemetry(rendererInstance, version, props.disableTelemetry, props.isPreview);
|
|
72
72
|
if (telemetryCollector) {
|
|
73
73
|
telemetryCollector.start();
|
|
@@ -6,6 +6,24 @@ interface Props {
|
|
|
6
6
|
watermarkText?: string;
|
|
7
7
|
watermarkLink?: string;
|
|
8
8
|
style?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Map of camelCase key_prop identifier → user value, layered on top of the
|
|
11
|
+
* preset's authored defaults. Identifiers are derived from `key_prop.label`
|
|
12
|
+
* via the same slugify rules the Pro Framer exporter uses, so identifiers
|
|
13
|
+
* generated by codegen and identifiers expected here always match.
|
|
14
|
+
*
|
|
15
|
+
* Only applies on the `presetId` path — `shader` token previews don't carry
|
|
16
|
+
* key_props and silently ignore this prop.
|
|
17
|
+
*/
|
|
18
|
+
configuration?: Record<string, unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Lock the preview to a specific snapshot of the preset, addressed by
|
|
21
|
+
* content hash. Customized snippets pin a version so subsequent upstream
|
|
22
|
+
* edits don't silently break the configuration. Server falls back to
|
|
23
|
+
* latest with the `X-Shaders-Version-Fallback` header when the requested
|
|
24
|
+
* snapshot is missing. Only meaningful on the `presetId` path.
|
|
25
|
+
*/
|
|
26
|
+
version?: string;
|
|
9
27
|
[key: string]: any;
|
|
10
28
|
}
|
|
11
29
|
declare const Preview: import("svelte").Component<Props, {}, "">;
|
package/dist/svelte/index.js
CHANGED
|
@@ -21540,7 +21540,7 @@ function Shader($$anchor, $$props) {
|
|
|
21540
21540
|
const checkRendering = () => {
|
|
21541
21541
|
if (!rendererInstance) return;
|
|
21542
21542
|
if (rendererInstance.getPerformanceStats().fps > 0) {
|
|
21543
|
-
telemetryCollector = startTelemetry(rendererInstance, "2.5.
|
|
21543
|
+
telemetryCollector = startTelemetry(rendererInstance, "2.5.115", disableTelemetry(), isPreview());
|
|
21544
21544
|
if (telemetryCollector) telemetryCollector.start();
|
|
21545
21545
|
telemetryStartTimeout = null;
|
|
21546
21546
|
} else telemetryStartTimeout = window.setTimeout(checkRendering, 500);
|
|
@@ -21855,14 +21855,111 @@ function Preview($$anchor, $$props) {
|
|
|
21855
21855
|
return res.json();
|
|
21856
21856
|
}).then((data) => {
|
|
21857
21857
|
const item = data.preset || data.shader;
|
|
21858
|
-
if (item?.definition && typeof item.definition === "string") return
|
|
21859
|
-
|
|
21858
|
+
if (item?.definition && typeof item.definition === "string") return {
|
|
21859
|
+
definition: decodePreviewDefinition(item.definition, key),
|
|
21860
|
+
keyProps: Array.isArray(item.key_props) ? item.key_props : null
|
|
21861
|
+
};
|
|
21862
|
+
return {
|
|
21863
|
+
definition: null,
|
|
21864
|
+
keyProps: null
|
|
21865
|
+
};
|
|
21860
21866
|
}).catch((err) => {
|
|
21861
21867
|
console.error("[Shaders Preview] Failed to fetch preview:", err);
|
|
21862
|
-
return
|
|
21868
|
+
return {
|
|
21869
|
+
definition: null,
|
|
21870
|
+
keyProps: null
|
|
21871
|
+
};
|
|
21872
|
+
});
|
|
21873
|
+
}
|
|
21874
|
+
const RESERVED_IDENTIFIERS = new Set([
|
|
21875
|
+
"break",
|
|
21876
|
+
"case",
|
|
21877
|
+
"catch",
|
|
21878
|
+
"class",
|
|
21879
|
+
"const",
|
|
21880
|
+
"continue",
|
|
21881
|
+
"default",
|
|
21882
|
+
"delete",
|
|
21883
|
+
"do",
|
|
21884
|
+
"else",
|
|
21885
|
+
"export",
|
|
21886
|
+
"extends",
|
|
21887
|
+
"finally",
|
|
21888
|
+
"for",
|
|
21889
|
+
"function",
|
|
21890
|
+
"if",
|
|
21891
|
+
"import",
|
|
21892
|
+
"in",
|
|
21893
|
+
"instanceof",
|
|
21894
|
+
"new",
|
|
21895
|
+
"return",
|
|
21896
|
+
"super",
|
|
21897
|
+
"switch",
|
|
21898
|
+
"this",
|
|
21899
|
+
"throw",
|
|
21900
|
+
"try",
|
|
21901
|
+
"typeof",
|
|
21902
|
+
"var",
|
|
21903
|
+
"void",
|
|
21904
|
+
"while",
|
|
21905
|
+
"with",
|
|
21906
|
+
"yield"
|
|
21907
|
+
]);
|
|
21908
|
+
function slugifyIdentifier(label, fallback) {
|
|
21909
|
+
const cleaned = label.replace(/[^A-Za-z0-9]+/g, " ").trim();
|
|
21910
|
+
if (!cleaned) return fallback;
|
|
21911
|
+
const camel = cleaned.split(/\s+/).map((part, i) => i === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
21912
|
+
const safe = /^[A-Za-z_$]/.test(camel) ? camel : `_${camel}`;
|
|
21913
|
+
return RESERVED_IDENTIFIERS.has(safe) ? `_${safe}` : safe;
|
|
21914
|
+
}
|
|
21915
|
+
function buildKeyPropIdentifierMap(keyProps) {
|
|
21916
|
+
const used = new Set(["style"]);
|
|
21917
|
+
const map = /* @__PURE__ */ new Map();
|
|
21918
|
+
if (!keyProps?.length) return map;
|
|
21919
|
+
keyProps.forEach((kp, i) => {
|
|
21920
|
+
if (!kp.targets?.length) return;
|
|
21921
|
+
const base = slugifyIdentifier(kp.label, `keyProp${i}`);
|
|
21922
|
+
let ident = base;
|
|
21923
|
+
let suffix = 1;
|
|
21924
|
+
while (used.has(ident)) {
|
|
21925
|
+
suffix++;
|
|
21926
|
+
ident = `${base}${suffix}`;
|
|
21927
|
+
}
|
|
21928
|
+
used.add(ident);
|
|
21929
|
+
map.set(ident, kp);
|
|
21863
21930
|
});
|
|
21931
|
+
return map;
|
|
21932
|
+
}
|
|
21933
|
+
function findInTree(id, list) {
|
|
21934
|
+
for (const c of list) {
|
|
21935
|
+
if (c.id === id) return c;
|
|
21936
|
+
if (c.children?.length) {
|
|
21937
|
+
const hit = findInTree(id, c.children);
|
|
21938
|
+
if (hit) return hit;
|
|
21939
|
+
}
|
|
21940
|
+
}
|
|
21941
|
+
return null;
|
|
21942
|
+
}
|
|
21943
|
+
function applyConfiguration(definition, configuration$1, keyProps) {
|
|
21944
|
+
if (!configuration$1 || !keyProps?.length) return definition;
|
|
21945
|
+
const entries = Object.entries(configuration$1);
|
|
21946
|
+
if (entries.length === 0) return definition;
|
|
21947
|
+
const identMap = buildKeyPropIdentifierMap(keyProps);
|
|
21948
|
+
const cloned = JSON.parse(JSON.stringify(definition.components));
|
|
21949
|
+
for (const [ident, value] of entries) {
|
|
21950
|
+
const kp = identMap.get(ident);
|
|
21951
|
+
if (!kp) continue;
|
|
21952
|
+
for (const t of kp.targets ?? []) {
|
|
21953
|
+
const comp = findInTree(t.component_id, cloned);
|
|
21954
|
+
if (comp?.props && t.prop in comp.props) comp.props[t.prop] = value;
|
|
21955
|
+
}
|
|
21956
|
+
}
|
|
21957
|
+
return {
|
|
21958
|
+
...definition,
|
|
21959
|
+
components: cloned
|
|
21960
|
+
};
|
|
21864
21961
|
}
|
|
21865
|
-
const shader = $.prop($$props, "shader", 3, void 0), presetId = $.prop($$props, "presetId", 3, void 0), apiBaseUrl = $.prop($$props, "apiBaseUrl", 3, "https://shaders.com"), obfuscationKey = $.prop($$props, "obfuscationKey", 3, "shaders-preview-key"), watermarkText = $.prop($$props, "watermarkText", 3, "Unlock your Shaders Pro license"), watermarkLink = $.prop($$props, "watermarkLink", 3, "https://shaders.com/dashboard?pricing=true"), style = $.prop($$props, "style", 3, void 0), restProps = $.rest_props($$props, [
|
|
21962
|
+
const shader = $.prop($$props, "shader", 3, void 0), presetId = $.prop($$props, "presetId", 3, void 0), apiBaseUrl = $.prop($$props, "apiBaseUrl", 3, "https://shaders.com"), obfuscationKey = $.prop($$props, "obfuscationKey", 3, "shaders-preview-key"), watermarkText = $.prop($$props, "watermarkText", 3, "Unlock your Shaders Pro license"), watermarkLink = $.prop($$props, "watermarkLink", 3, "https://shaders.com/dashboard?pricing=true"), style = $.prop($$props, "style", 3, void 0), configuration = $.prop($$props, "configuration", 3, void 0), version = $.prop($$props, "version", 3, void 0), restProps = $.rest_props($$props, [
|
|
21866
21963
|
"$$slots",
|
|
21867
21964
|
"$$events",
|
|
21868
21965
|
"$$legacy",
|
|
@@ -21872,26 +21969,40 @@ function Preview($$anchor, $$props) {
|
|
|
21872
21969
|
"obfuscationKey",
|
|
21873
21970
|
"watermarkText",
|
|
21874
21971
|
"watermarkLink",
|
|
21875
|
-
"style"
|
|
21972
|
+
"style",
|
|
21973
|
+
"configuration",
|
|
21974
|
+
"version"
|
|
21876
21975
|
]);
|
|
21877
21976
|
let fetchedDefinition = $.state(null);
|
|
21977
|
+
let fetchedKeyProps = $.state(null);
|
|
21878
21978
|
let watermarkHovered = $.state(false);
|
|
21879
|
-
let resolvedDefinition = $.derived(() => $.get(fetchedDefinition)
|
|
21979
|
+
let resolvedDefinition = $.derived(() => $.get(fetchedDefinition) ? applyConfiguration($.get(fetchedDefinition), configuration(), $.get(fetchedKeyProps)) : null);
|
|
21880
21980
|
$.user_effect(() => {
|
|
21881
21981
|
if (shader()) {
|
|
21882
21982
|
$.set(fetchedDefinition, null);
|
|
21883
|
-
|
|
21884
|
-
|
|
21983
|
+
$.set(fetchedKeyProps, null);
|
|
21984
|
+
fetchAndDecode(`${apiBaseUrl()}/api/preview/shader/${shader()}`, obfuscationKey()).then((result) => {
|
|
21985
|
+
$.set(fetchedDefinition, result.definition, true);
|
|
21986
|
+
$.set(fetchedKeyProps, result.keyProps, true);
|
|
21885
21987
|
});
|
|
21886
|
-
} else if (!presetId())
|
|
21988
|
+
} else if (!presetId()) {
|
|
21989
|
+
$.set(fetchedDefinition, null);
|
|
21990
|
+
$.set(fetchedKeyProps, null);
|
|
21991
|
+
}
|
|
21887
21992
|
});
|
|
21888
21993
|
$.user_effect(() => {
|
|
21889
21994
|
if (presetId()) {
|
|
21890
21995
|
$.set(fetchedDefinition, null);
|
|
21891
|
-
|
|
21892
|
-
|
|
21996
|
+
$.set(fetchedKeyProps, null);
|
|
21997
|
+
const versionParam = version() ? `?version=${encodeURIComponent(version())}` : "";
|
|
21998
|
+
fetchAndDecode(`${apiBaseUrl()}/api/preview/preset/${presetId()}${versionParam}`, obfuscationKey()).then((result) => {
|
|
21999
|
+
$.set(fetchedDefinition, result.definition, true);
|
|
22000
|
+
$.set(fetchedKeyProps, result.keyProps, true);
|
|
21893
22001
|
});
|
|
21894
|
-
} else if (!shader())
|
|
22002
|
+
} else if (!shader()) {
|
|
22003
|
+
$.set(fetchedDefinition, null);
|
|
22004
|
+
$.set(fetchedKeyProps, null);
|
|
22005
|
+
}
|
|
21895
22006
|
});
|
|
21896
22007
|
onMount(() => {
|
|
21897
22008
|
console.warn("[Shaders] Preview component is intended for use with a Shaders license. Visit https://shaders.com for more information.");
|
|
@@ -247,6 +247,18 @@ interface ShaderDefinition {
|
|
|
247
247
|
devicePreview?: string
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
interface KeyPropTarget {
|
|
251
|
+
component_id: string
|
|
252
|
+
prop: string
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface KeyProp {
|
|
256
|
+
label: string
|
|
257
|
+
type: 'color' | 'range' | 'logo' | 'image'
|
|
258
|
+
category?: string
|
|
259
|
+
targets: KeyPropTarget[]
|
|
260
|
+
}
|
|
261
|
+
|
|
250
262
|
// --- Preview Decode Logic ---
|
|
251
263
|
// No name mapping — preview definitions contain readable JSON.
|
|
252
264
|
|
|
@@ -274,8 +286,13 @@ function decodePreviewDefinition(encoded: string, key: string): ShaderDefinition
|
|
|
274
286
|
}
|
|
275
287
|
}
|
|
276
288
|
|
|
277
|
-
// Fetch from API and decode response
|
|
278
|
-
|
|
289
|
+
// Fetch from API and decode response, returning both the definition and key_props
|
|
290
|
+
interface FetchResult {
|
|
291
|
+
definition: ShaderDefinition | null
|
|
292
|
+
keyProps: KeyProp[] | null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function fetchAndDecode(url: string, key: string): Promise<FetchResult> {
|
|
279
296
|
return fetch(url)
|
|
280
297
|
.then(res => {
|
|
281
298
|
if (!res.ok) throw new Error(`Failed to fetch: ${res.status}`)
|
|
@@ -284,16 +301,94 @@ function fetchAndDecode(url: string, key: string): Promise<ShaderDefinition | nu
|
|
|
284
301
|
.then(data => {
|
|
285
302
|
const item = data.preset || data.shader
|
|
286
303
|
if (item?.definition && typeof item.definition === 'string') {
|
|
287
|
-
return
|
|
304
|
+
return {
|
|
305
|
+
definition: decodePreviewDefinition(item.definition, key),
|
|
306
|
+
keyProps: Array.isArray(item.key_props) ? item.key_props : null,
|
|
307
|
+
}
|
|
288
308
|
}
|
|
289
|
-
return null
|
|
309
|
+
return { definition: null, keyProps: null }
|
|
290
310
|
})
|
|
291
311
|
.catch(err => {
|
|
292
312
|
console.error('[Shaders Preview] Failed to fetch preview:', err)
|
|
293
|
-
return null
|
|
313
|
+
return { definition: null, keyProps: null }
|
|
294
314
|
})
|
|
295
315
|
}
|
|
296
316
|
|
|
317
|
+
// --- Configuration apply ---
|
|
318
|
+
// Source of truth: dotcom/utils/keyPropsIdentifier.ts. Inlined per framework
|
|
319
|
+
// because the build pipeline copies framework dist into the published npm
|
|
320
|
+
// package and won't carry a cross-package import.
|
|
321
|
+
|
|
322
|
+
const RESERVED_IDENTIFIERS = new Set([
|
|
323
|
+
'break', 'case', 'catch', 'class', 'const', 'continue', 'default',
|
|
324
|
+
'delete', 'do', 'else', 'export', 'extends', 'finally', 'for',
|
|
325
|
+
'function', 'if', 'import', 'in', 'instanceof', 'new', 'return',
|
|
326
|
+
'super', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void',
|
|
327
|
+
'while', 'with', 'yield',
|
|
328
|
+
])
|
|
329
|
+
|
|
330
|
+
function slugifyIdentifier(label: string, fallback: string): string {
|
|
331
|
+
const cleaned = label.replace(/[^A-Za-z0-9]+/g, ' ').trim()
|
|
332
|
+
if (!cleaned) return fallback
|
|
333
|
+
const parts = cleaned.split(/\s+/)
|
|
334
|
+
const camel = parts
|
|
335
|
+
.map((part, i) => i === 0 ? part.charAt(0).toLowerCase() + part.slice(1) : part.charAt(0).toUpperCase() + part.slice(1))
|
|
336
|
+
.join('')
|
|
337
|
+
const safe = /^[A-Za-z_$]/.test(camel) ? camel : `_${camel}`
|
|
338
|
+
return RESERVED_IDENTIFIERS.has(safe) ? `_${safe}` : safe
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function buildKeyPropIdentifierMap(keyProps: KeyProp[] | null | undefined): Map<string, KeyProp> {
|
|
342
|
+
const used = new Set<string>(['style'])
|
|
343
|
+
const map = new Map<string, KeyProp>()
|
|
344
|
+
if (!keyProps?.length) return map
|
|
345
|
+
keyProps.forEach((kp, i) => {
|
|
346
|
+
if (!kp.targets?.length) return
|
|
347
|
+
const base = slugifyIdentifier(kp.label, `keyProp${i}`)
|
|
348
|
+
let ident = base
|
|
349
|
+
let suffix = 1
|
|
350
|
+
while (used.has(ident)) {
|
|
351
|
+
suffix++
|
|
352
|
+
ident = `${base}${suffix}`
|
|
353
|
+
}
|
|
354
|
+
used.add(ident)
|
|
355
|
+
map.set(ident, kp)
|
|
356
|
+
})
|
|
357
|
+
return map
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function findInTree(id: string, list: ShaderComponent[]): ShaderComponent | null {
|
|
361
|
+
for (const c of list) {
|
|
362
|
+
if (c.id === id) return c
|
|
363
|
+
if (c.children?.length) {
|
|
364
|
+
const hit = findInTree(id, c.children)
|
|
365
|
+
if (hit) return hit
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return null
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function applyConfiguration(
|
|
372
|
+
definition: ShaderDefinition,
|
|
373
|
+
configuration: Record<string, unknown> | null | undefined,
|
|
374
|
+
keyProps: KeyProp[] | null | undefined
|
|
375
|
+
): ShaderDefinition {
|
|
376
|
+
if (!configuration || !keyProps?.length) return definition
|
|
377
|
+
const entries = Object.entries(configuration)
|
|
378
|
+
if (entries.length === 0) return definition
|
|
379
|
+
const identMap = buildKeyPropIdentifierMap(keyProps)
|
|
380
|
+
const cloned = JSON.parse(JSON.stringify(definition.components)) as ShaderComponent[]
|
|
381
|
+
for (const [ident, value] of entries) {
|
|
382
|
+
const kp = identMap.get(ident)
|
|
383
|
+
if (!kp) continue
|
|
384
|
+
for (const t of kp.targets ?? []) {
|
|
385
|
+
const comp = findInTree(t.component_id, cloned)
|
|
386
|
+
if (comp?.props && t.prop in comp.props) comp.props[t.prop] = value
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return { ...definition, components: cloned }
|
|
390
|
+
}
|
|
391
|
+
|
|
297
392
|
// --- Props ---
|
|
298
393
|
|
|
299
394
|
interface Props {
|
|
@@ -304,6 +399,24 @@ interface Props {
|
|
|
304
399
|
watermarkText?: string
|
|
305
400
|
watermarkLink?: string
|
|
306
401
|
style?: string
|
|
402
|
+
/**
|
|
403
|
+
* Map of camelCase key_prop identifier → user value, layered on top of the
|
|
404
|
+
* preset's authored defaults. Identifiers are derived from `key_prop.label`
|
|
405
|
+
* via the same slugify rules the Pro Framer exporter uses, so identifiers
|
|
406
|
+
* generated by codegen and identifiers expected here always match.
|
|
407
|
+
*
|
|
408
|
+
* Only applies on the `presetId` path — `shader` token previews don't carry
|
|
409
|
+
* key_props and silently ignore this prop.
|
|
410
|
+
*/
|
|
411
|
+
configuration?: Record<string, unknown>
|
|
412
|
+
/**
|
|
413
|
+
* Lock the preview to a specific snapshot of the preset, addressed by
|
|
414
|
+
* content hash. Customized snippets pin a version so subsequent upstream
|
|
415
|
+
* edits don't silently break the configuration. Server falls back to
|
|
416
|
+
* latest with the `X-Shaders-Version-Fallback` header when the requested
|
|
417
|
+
* snapshot is missing. Only meaningful on the `presetId` path.
|
|
418
|
+
*/
|
|
419
|
+
version?: string
|
|
307
420
|
[key: string]: any
|
|
308
421
|
}
|
|
309
422
|
|
|
@@ -315,35 +428,46 @@ const {
|
|
|
315
428
|
watermarkText = 'Unlock your Shaders Pro license',
|
|
316
429
|
watermarkLink = 'https://shaders.com/dashboard?pricing=true',
|
|
317
430
|
style = undefined,
|
|
431
|
+
configuration = undefined,
|
|
432
|
+
version = undefined,
|
|
318
433
|
...restProps
|
|
319
434
|
}: Props = $props()
|
|
320
435
|
|
|
321
436
|
// --- Decode & Resolve ---
|
|
322
437
|
|
|
323
438
|
let fetchedDefinition: ShaderDefinition | null = $state(null)
|
|
439
|
+
let fetchedKeyProps: KeyProp[] | null = $state(null)
|
|
324
440
|
let watermarkHovered = $state(false)
|
|
325
441
|
|
|
326
|
-
let resolvedDefinition: ShaderDefinition | null = $derived(
|
|
442
|
+
let resolvedDefinition: ShaderDefinition | null = $derived(
|
|
443
|
+
fetchedDefinition ? applyConfiguration(fetchedDefinition, configuration, fetchedKeyProps) : null
|
|
444
|
+
)
|
|
327
445
|
|
|
328
446
|
// Fetch shader by token
|
|
329
447
|
$effect(() => {
|
|
330
448
|
if (shader) {
|
|
331
449
|
fetchedDefinition = null
|
|
450
|
+
fetchedKeyProps = null
|
|
332
451
|
fetchAndDecode(`${apiBaseUrl}/api/preview/shader/${shader}`, obfuscationKey)
|
|
333
|
-
.then(
|
|
452
|
+
.then(result => { fetchedDefinition = result.definition; fetchedKeyProps = result.keyProps })
|
|
334
453
|
} else if (!presetId) {
|
|
335
454
|
fetchedDefinition = null
|
|
455
|
+
fetchedKeyProps = null
|
|
336
456
|
}
|
|
337
457
|
})
|
|
338
458
|
|
|
339
|
-
// Fetch preset by ID
|
|
459
|
+
// Fetch preset by ID. Version pins the preview to a snapshot; when omitted,
|
|
460
|
+
// the server returns latest.
|
|
340
461
|
$effect(() => {
|
|
341
462
|
if (presetId) {
|
|
342
463
|
fetchedDefinition = null
|
|
343
|
-
|
|
344
|
-
|
|
464
|
+
fetchedKeyProps = null
|
|
465
|
+
const versionParam = version ? `?version=${encodeURIComponent(version)}` : ''
|
|
466
|
+
fetchAndDecode(`${apiBaseUrl}/api/preview/preset/${presetId}${versionParam}`, obfuscationKey)
|
|
467
|
+
.then(result => { fetchedDefinition = result.definition; fetchedKeyProps = result.keyProps })
|
|
345
468
|
} else if (!shader) {
|
|
346
469
|
fetchedDefinition = null
|
|
470
|
+
fetchedKeyProps = null
|
|
347
471
|
}
|
|
348
472
|
})
|
|
349
473
|
|