@pixerate/editor 0.123.0 → 0.124.1
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/agent/index.cjs +2 -2
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.js +3 -3
- package/dist/animation/index.d.cts +2 -1
- package/dist/animation/index.d.ts +2 -1
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +11 -733
- package/dist/animation-editor/index.d.ts +11 -733
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/{bezier-C-W9LFyD.d.cts → bezier-CBzKKDjl.d.cts} +2 -66
- package/dist/{bezier-DQZE5dxA.d.ts → bezier-CWv6LQal.d.ts} +2 -66
- package/dist/{brushstrokeRender-M4S65DHT.cjs → brushstrokeRender-EELTV7U4.cjs} +8 -8
- package/dist/{brushstrokeRender-M4S65DHT.cjs.map → brushstrokeRender-EELTV7U4.cjs.map} +1 -1
- package/dist/{brushstrokeRender-CDDRSV2W.js → brushstrokeRender-LRV7GXGD.js} +3 -3
- package/dist/canvas/index.cjs +6 -6
- package/dist/canvas/index.d.cts +10 -6
- package/dist/canvas/index.d.ts +10 -6
- package/dist/canvas/index.js +1 -1
- package/dist/{chunk-SMJGE7HM.cjs → chunk-4QQZV5Y5.cjs} +512 -39
- package/dist/chunk-4QQZV5Y5.cjs.map +1 -0
- package/dist/{chunk-TVXFHE4T.js → chunk-64PYNEHJ.js} +45 -12
- package/dist/chunk-64PYNEHJ.js.map +1 -0
- package/dist/{chunk-2IW3BWRT.cjs → chunk-7LQO6OHH.cjs} +2 -2
- package/dist/{chunk-2IW3BWRT.cjs.map → chunk-7LQO6OHH.cjs.map} +1 -1
- package/dist/chunk-CN4PJHUG.cjs +234 -0
- package/dist/chunk-CN4PJHUG.cjs.map +1 -0
- package/dist/chunk-FHYDQOPC.cjs +215 -0
- package/dist/chunk-FHYDQOPC.cjs.map +1 -0
- package/dist/{chunk-KMBC4HNV.js → chunk-J67OLQUQ.js} +2 -2
- package/dist/chunk-JRXJTPES.js +215 -0
- package/dist/chunk-JRXJTPES.js.map +1 -0
- package/dist/{chunk-IPFRWILU.js → chunk-ORHMVUR6.js} +488 -15
- package/dist/chunk-ORHMVUR6.js.map +1 -0
- package/dist/{chunk-6CJRDJCU.cjs → chunk-PEHYWUVV.cjs} +176 -111
- package/dist/chunk-PEHYWUVV.cjs.map +1 -0
- package/dist/chunk-QEG3GRN2.js +234 -0
- package/dist/chunk-QEG3GRN2.js.map +1 -0
- package/dist/{chunk-6ZBR4J62.js → chunk-WATAQ2Z2.js} +79 -14
- package/dist/chunk-WATAQ2Z2.js.map +1 -0
- package/dist/{chunk-RLRUAPBJ.cjs → chunk-Z6OUCS2K.cjs} +47 -14
- package/dist/chunk-Z6OUCS2K.cjs.map +1 -0
- package/dist/effects/index.cjs +605 -0
- package/dist/effects/index.cjs.map +1 -0
- package/dist/effects/index.d.cts +163 -0
- package/dist/effects/index.d.ts +163 -0
- package/dist/effects/index.js +605 -0
- package/dist/effects/index.js.map +1 -0
- package/dist/evaluate-2S3vSPLy.d.cts +55 -0
- package/dist/evaluate-CfmT6xA3.d.ts +55 -0
- package/dist/{export-DfjzHAJI.d.ts → export-BGtXgMJ3.d.ts} +536 -128
- package/dist/{export-Dv0-ZVrV.d.cts → export-BdfY_lH0.d.ts} +392 -5
- package/dist/{export-Dv0-ZVrV.d.ts → export-CbpjwxaI.d.cts} +392 -5
- package/dist/{export-lU5qSpHE.d.cts → export-WmYoWmLM.d.cts} +536 -128
- package/dist/flair-CxB3fOcU.d.ts +739 -0
- package/dist/flair-D7BxyBwL.d.cts +739 -0
- package/dist/gradient-KDfCvhuj.d.cts +66 -0
- package/dist/gradient-KDfCvhuj.d.ts +66 -0
- package/dist/helpers-777mC6dc.d.cts +253 -0
- package/dist/helpers-DX2_7L5t.d.ts +253 -0
- package/dist/image-editor/index.cjs +1296 -805
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +39 -182
- package/dist/image-editor/index.d.ts +39 -182
- package/dist/image-editor/index.js +497 -6
- package/dist/image-editor/index.js.map +1 -1
- package/dist/{opentype-p8Xxf-Wh.d.ts → opentype-14PDI5D2.d.ts} +1 -1
- package/dist/{opentype-BV7wWatY.d.cts → opentype-D80AVmMu.d.cts} +1 -1
- package/dist/paint/index.cjs +2 -2
- package/dist/paint/index.d.cts +32 -10
- package/dist/paint/index.d.ts +32 -10
- package/dist/paint/index.js +1 -1
- package/dist/paint-editor/index.cjs +10 -8
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +11 -5
- package/dist/paint-editor/index.d.ts +11 -5
- package/dist/paint-editor/index.js +4 -2
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/parameters/index.d.cts +4 -184
- package/dist/parameters/index.d.ts +4 -184
- package/dist/{pathfinder-IxcyhO00.d.cts → pathfinder-C0UoFypx.d.cts} +1 -1
- package/dist/{pathfinder-StVChJOT.d.ts → pathfinder-DjISxkUv.d.ts} +1 -1
- package/dist/{player-DrcP2xqj.d.cts → player-CBtEoU2x.d.cts} +2 -54
- package/dist/{player-DV4Jujdq.d.ts → player-cGLrngL_.d.ts} +2 -54
- package/dist/{svg-BtND4TJu.d.cts → svg-BDrjRnL1.d.cts} +3 -2
- package/dist/{svg-BfVXG7hL.d.ts → svg-C-vZ3q50.d.ts} +3 -2
- package/dist/tools/index.cjs +2 -2
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.js +3 -3
- package/dist/tools-C7JtLYzo.d.ts +126 -0
- package/dist/tools-DHKGHiSK.d.cts +126 -0
- package/dist/{types-fuXEme9j.d.ts → types-BcX7sCsn.d.ts} +2 -2
- package/dist/{types-DJF3Ghn5.d.cts → types-CznabG7m.d.cts} +2 -2
- package/dist/types-D3-J3TFf.d.ts +185 -0
- package/dist/types-DMcf_z4J.d.cts +185 -0
- package/dist/vector/index.d.cts +10 -10
- package/dist/vector/index.d.ts +10 -10
- package/dist/video-editor/index.cjs +749 -277
- package/dist/video-editor/index.cjs.map +1 -1
- package/dist/video-editor/index.d.cts +265 -138
- package/dist/video-editor/index.d.ts +265 -138
- package/dist/video-editor/index.js +490 -18
- package/dist/video-editor/index.js.map +1 -1
- package/dist/{warp-BjmAgQwq.d.cts → warp-CH9MM435.d.cts} +1 -1
- package/dist/{warp-Ca5fedzS.d.ts → warp-k_PdtXOG.d.ts} +1 -1
- package/package.json +6 -1
- package/skills/pixerate-image-editor/SKILL.md +7 -0
- package/skills/pixerate-image-editor/references/provider-effects.md +35 -0
- package/skills/pixerate-video-editor/SKILL.md +39 -1
- package/dist/chunk-6CJRDJCU.cjs.map +0 -1
- package/dist/chunk-6ZBR4J62.js.map +0 -1
- package/dist/chunk-IPFRWILU.js.map +0 -1
- package/dist/chunk-RLRUAPBJ.cjs.map +0 -1
- package/dist/chunk-SMJGE7HM.cjs.map +0 -1
- package/dist/chunk-TVXFHE4T.js.map +0 -1
- /package/dist/{brushstrokeRender-CDDRSV2W.js.map → brushstrokeRender-LRV7GXGD.js.map} +0 -0
- /package/dist/{chunk-KMBC4HNV.js.map → chunk-J67OLQUQ.js.map} +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
validateJsonSchema
|
|
3
|
+
} from "./chunk-4FX5ACVG.js";
|
|
4
|
+
|
|
5
|
+
// src/effects/errors.ts
|
|
6
|
+
var EFFECT_ERROR_CODES = ["unknown_effect", "invalid_values", "unsupported", "disposed", "no_provider"];
|
|
7
|
+
var EffectError = class extends Error {
|
|
8
|
+
code;
|
|
9
|
+
effectId;
|
|
10
|
+
constructor(code, message, effectId) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "EffectError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.effectId = effectId;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var UnknownEffectError = class extends EffectError {
|
|
18
|
+
constructor(effectId, providerName) {
|
|
19
|
+
super(
|
|
20
|
+
"unknown_effect",
|
|
21
|
+
`Unknown effect "${effectId}"${providerName ? ` in ${providerName}` : ""}. Call search_effects or list() for valid ids.`,
|
|
22
|
+
effectId
|
|
23
|
+
);
|
|
24
|
+
this.name = "UnknownEffectError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var EffectControlError = class extends EffectError {
|
|
28
|
+
details;
|
|
29
|
+
constructor(effectId, details) {
|
|
30
|
+
const lines = details.map((d) => `- ${d.path || "/"}: ${d.message}`).join("\n");
|
|
31
|
+
super("invalid_values", `Invalid values for effect "${effectId}":
|
|
32
|
+
${lines}`, effectId);
|
|
33
|
+
this.name = "EffectControlError";
|
|
34
|
+
this.details = details;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var EffectUnsupportedError = class extends EffectError {
|
|
38
|
+
constructor(message, effectId) {
|
|
39
|
+
super("unsupported", message, effectId);
|
|
40
|
+
this.name = "EffectUnsupportedError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var EffectDisposedError = class extends EffectError {
|
|
44
|
+
constructor(effectId) {
|
|
45
|
+
super("disposed", `The renderer for "${effectId}" was disposed. Create a new renderer.`, effectId);
|
|
46
|
+
this.name = "EffectDisposedError";
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var NoEffectsProviderError = class extends EffectError {
|
|
50
|
+
constructor() {
|
|
51
|
+
super("no_provider", "No effects provider is registered with this editor. The host must call setEffectsProvider(provider) first.");
|
|
52
|
+
this.name = "NoEffectsProviderError";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
function isEffectError(error) {
|
|
56
|
+
if (error instanceof EffectError) return true;
|
|
57
|
+
const code = error instanceof Error ? error.code : void 0;
|
|
58
|
+
return typeof code === "string" && EFFECT_ERROR_CODES.includes(code);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/effects/helpers.ts
|
|
62
|
+
function effectValueErrors(description, values) {
|
|
63
|
+
if (values === void 0) return [];
|
|
64
|
+
return validateJsonSchema(values, { ...description.controls, required: [] });
|
|
65
|
+
}
|
|
66
|
+
function resolveEffectValues(description, values) {
|
|
67
|
+
const errors = effectValueErrors(description, values);
|
|
68
|
+
if (errors.length) throw new EffectControlError(description.id, errors);
|
|
69
|
+
return { ...description.defaults, ...values ?? {} };
|
|
70
|
+
}
|
|
71
|
+
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use));
|
|
72
|
+
function searchEffectSummaries(catalog, query = "", options = {}) {
|
|
73
|
+
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
74
|
+
const scored = [];
|
|
75
|
+
catalog.forEach((e, i) => {
|
|
76
|
+
if (!matchesFilters(e, options)) return;
|
|
77
|
+
const name = e.name.toLowerCase();
|
|
78
|
+
const hay = [e.id, e.name, e.category, ...e.tags ?? [], e.description ?? ""].join(" ").toLowerCase();
|
|
79
|
+
let score = 0;
|
|
80
|
+
for (const w of words) {
|
|
81
|
+
if (!hay.includes(w)) return;
|
|
82
|
+
score += name.startsWith(w) ? 3 : name.includes(w) ? 2 : 1;
|
|
83
|
+
}
|
|
84
|
+
scored.push({ e, score, i });
|
|
85
|
+
});
|
|
86
|
+
scored.sort((a, b) => b.score - a.score || a.i - b.i);
|
|
87
|
+
const out = scored.map((s) => s.e);
|
|
88
|
+
return options.limit !== void 0 ? out.slice(0, Math.max(0, options.limit)) : out;
|
|
89
|
+
}
|
|
90
|
+
function createEffectPixels(width, height, float = false) {
|
|
91
|
+
const n = Math.max(0, width * height * 4);
|
|
92
|
+
return { width, height, data: new Uint8ClampedArray(n), ...float ? { float: new Float32Array(n) } : {} };
|
|
93
|
+
}
|
|
94
|
+
function isEffectPixels(source) {
|
|
95
|
+
return !!source && typeof source === "object" && source.data instanceof Uint8ClampedArray && typeof source.width === "number" && typeof source.height === "number";
|
|
96
|
+
}
|
|
97
|
+
var srgbToLinear = (v) => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
98
|
+
var linearToSrgb = (v) => v > 0 ? v <= 31308e-7 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055 : 0;
|
|
99
|
+
function encodeEffectFloat(pixels) {
|
|
100
|
+
const f = pixels.float;
|
|
101
|
+
if (!f) return pixels;
|
|
102
|
+
const d = pixels.data;
|
|
103
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
104
|
+
d[i] = Math.round(linearToSrgb(f[i]) * 255);
|
|
105
|
+
d[i + 1] = Math.round(linearToSrgb(f[i + 1]) * 255);
|
|
106
|
+
d[i + 2] = Math.round(linearToSrgb(f[i + 2]) * 255);
|
|
107
|
+
d[i + 3] = Math.round(Math.min(1, Math.max(0, f[i + 3])) * 255);
|
|
108
|
+
}
|
|
109
|
+
return pixels;
|
|
110
|
+
}
|
|
111
|
+
function applyEffectMask(result, mask, source) {
|
|
112
|
+
const { data } = result;
|
|
113
|
+
const m = mask.data;
|
|
114
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
115
|
+
const k = m[i + 3] / 255;
|
|
116
|
+
if (k >= 1) continue;
|
|
117
|
+
if (source) {
|
|
118
|
+
for (let c = 0; c < 4; c++) data[i + c] = source.data[i + c] + (data[i + c] - source.data[i + c]) * k;
|
|
119
|
+
if (result.float && source.float) {
|
|
120
|
+
for (let c = 0; c < 4; c++) result.float[i + c] = source.float[i + c] + (result.float[i + c] - source.float[i + c]) * k;
|
|
121
|
+
}
|
|
122
|
+
} else {
|
|
123
|
+
data[i + 3] = data[i + 3] * k;
|
|
124
|
+
if (result.float) result.float[i + 3] *= k;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
var EffectsSlot = class {
|
|
130
|
+
current = null;
|
|
131
|
+
listeners = /* @__PURE__ */ new Set();
|
|
132
|
+
get provider() {
|
|
133
|
+
return this.current;
|
|
134
|
+
}
|
|
135
|
+
set(provider) {
|
|
136
|
+
if (provider === this.current) return;
|
|
137
|
+
this.current = provider;
|
|
138
|
+
for (const l of [...this.listeners]) l(provider);
|
|
139
|
+
}
|
|
140
|
+
/** Sets `provider` and returns a function that clears it again (if it is still the one set). */
|
|
141
|
+
use(provider) {
|
|
142
|
+
this.set(provider);
|
|
143
|
+
return () => {
|
|
144
|
+
if (this.current === provider) this.set(null);
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** The provider, or a `NoEffectsProviderError`. */
|
|
148
|
+
require() {
|
|
149
|
+
if (!this.current) throw new NoEffectsProviderError();
|
|
150
|
+
return this.current;
|
|
151
|
+
}
|
|
152
|
+
subscribe(listener) {
|
|
153
|
+
this.listeners.add(listener);
|
|
154
|
+
return () => this.listeners.delete(listener);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/effects/pool.ts
|
|
159
|
+
var EffectRendererPool = class {
|
|
160
|
+
entries = /* @__PURE__ */ new Map();
|
|
161
|
+
maxRenderers;
|
|
162
|
+
constructor(options = {}) {
|
|
163
|
+
this.maxRenderers = Math.max(1, options.maxRenderers ?? 4);
|
|
164
|
+
}
|
|
165
|
+
/** Live renderers now. */
|
|
166
|
+
get size() {
|
|
167
|
+
return this.entries.size;
|
|
168
|
+
}
|
|
169
|
+
/** Renders `request` with the provider's renderer for this effect, size and depth (made on first use). */
|
|
170
|
+
render(provider, id, size, request) {
|
|
171
|
+
const depth = size.depth ?? 8;
|
|
172
|
+
const key = `${provider.id}:${id}@${size.width}x${size.height}x${depth}`;
|
|
173
|
+
let entry = this.entries.get(key);
|
|
174
|
+
if (entry) {
|
|
175
|
+
this.entries.delete(key);
|
|
176
|
+
this.entries.set(key, entry);
|
|
177
|
+
} else {
|
|
178
|
+
const renderer = Promise.resolve().then(() => provider.createRenderer(id, { width: size.width, height: size.height, depth }));
|
|
179
|
+
renderer.catch(() => {
|
|
180
|
+
if (this.entries.get(key)?.renderer === renderer) this.entries.delete(key);
|
|
181
|
+
});
|
|
182
|
+
entry = { renderer, queue: Promise.resolve() };
|
|
183
|
+
this.entries.set(key, entry);
|
|
184
|
+
this.evict();
|
|
185
|
+
}
|
|
186
|
+
const e = entry;
|
|
187
|
+
const next = e.queue.then(() => e.renderer).then((r) => r.render(request));
|
|
188
|
+
e.queue = next.catch(() => void 0);
|
|
189
|
+
return next;
|
|
190
|
+
}
|
|
191
|
+
/** Disposes the least recently used renderers over the limit, after their queued renders. */
|
|
192
|
+
evict() {
|
|
193
|
+
while (this.entries.size > this.maxRenderers) {
|
|
194
|
+
const [key, entry] = this.entries.entries().next().value;
|
|
195
|
+
this.entries.delete(key);
|
|
196
|
+
dispose(entry);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Disposes every renderer (after its queued renders). */
|
|
200
|
+
clear() {
|
|
201
|
+
for (const entry of this.entries.values()) dispose(entry);
|
|
202
|
+
this.entries.clear();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
function dispose(entry) {
|
|
206
|
+
entry.queue.then(() => entry.renderer).then(
|
|
207
|
+
(r) => r.dispose(),
|
|
208
|
+
() => {
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export {
|
|
214
|
+
EFFECT_ERROR_CODES,
|
|
215
|
+
EffectError,
|
|
216
|
+
UnknownEffectError,
|
|
217
|
+
EffectControlError,
|
|
218
|
+
EffectUnsupportedError,
|
|
219
|
+
EffectDisposedError,
|
|
220
|
+
NoEffectsProviderError,
|
|
221
|
+
isEffectError,
|
|
222
|
+
effectValueErrors,
|
|
223
|
+
resolveEffectValues,
|
|
224
|
+
searchEffectSummaries,
|
|
225
|
+
createEffectPixels,
|
|
226
|
+
isEffectPixels,
|
|
227
|
+
srgbToLinear,
|
|
228
|
+
linearToSrgb,
|
|
229
|
+
encodeEffectFloat,
|
|
230
|
+
applyEffectMask,
|
|
231
|
+
EffectsSlot,
|
|
232
|
+
EffectRendererPool
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=chunk-QEG3GRN2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/effects/errors.ts","../src/effects/helpers.ts","../src/effects/pool.ts"],"sourcesContent":["import type { SchemaValidationError } from '../tools/schema.js';\n\nexport type EffectErrorCode = 'unknown_effect' | 'invalid_values' | 'unsupported' | 'disposed' | 'no_provider';\n\nexport const EFFECT_ERROR_CODES: readonly EffectErrorCode[] = ['unknown_effect', 'invalid_values', 'unsupported', 'disposed', 'no_provider'];\n\n/**\n * Base of every effects error. Check `code` (or use `isEffectError`) rather than `instanceof`\n * across packages, since a provider may be bundled with its own copy of this class.\n */\nexport class EffectError extends Error {\n\treadonly code: EffectErrorCode;\n\treadonly effectId?: string;\n\n\tconstructor(code: EffectErrorCode, message: string, effectId?: string) {\n\t\tsuper(message);\n\t\tthis.name = 'EffectError';\n\t\tthis.code = code;\n\t\tthis.effectId = effectId;\n\t}\n}\n\n/** No effect has this id. */\nexport class UnknownEffectError extends EffectError {\n\tconstructor(effectId: string, providerName?: string) {\n\t\tsuper(\n\t\t\t'unknown_effect',\n\t\t\t`Unknown effect \"${effectId}\"${providerName ? ` in ${providerName}` : ''}. Call search_effects or list() for valid ids.`,\n\t\t\teffectId\n\t\t);\n\t\tthis.name = 'UnknownEffectError';\n\t}\n}\n\n/** Control values do not match the effect's control schema (unknown control, wrong type, out of range). */\nexport class EffectControlError extends EffectError {\n\treadonly details: readonly SchemaValidationError[];\n\n\tconstructor(effectId: string, details: readonly SchemaValidationError[]) {\n\t\tconst lines = details.map((d) => `- ${d.path || '/'}: ${d.message}`).join('\\n');\n\t\tsuper('invalid_values', `Invalid values for effect \"${effectId}\":\\n${lines}`, effectId);\n\t\tthis.name = 'EffectControlError';\n\t\tthis.details = details;\n\t}\n}\n\n/** The provider cannot do this (a vector effect asked for pixels, a size above `maxSize`, a missing input...). */\nexport class EffectUnsupportedError extends EffectError {\n\tconstructor(message: string, effectId?: string) {\n\t\tsuper('unsupported', message, effectId);\n\t\tthis.name = 'EffectUnsupportedError';\n\t}\n}\n\n/** The renderer was disposed. Create a new one. */\nexport class EffectDisposedError extends EffectError {\n\tconstructor(effectId: string) {\n\t\tsuper('disposed', `The renderer for \"${effectId}\" was disposed. Create a new renderer.`, effectId);\n\t\tthis.name = 'EffectDisposedError';\n\t}\n}\n\n/** No effects provider is registered with this editor. */\nexport class NoEffectsProviderError extends EffectError {\n\tconstructor() {\n\t\tsuper('no_provider', 'No effects provider is registered with this editor. The host must call setEffectsProvider(provider) first.');\n\t\tthis.name = 'NoEffectsProviderError';\n\t}\n}\n\n/** True for any effects error, including one thrown by another copy of this module. */\nexport function isEffectError(error: unknown): error is EffectError {\n\tif (error instanceof EffectError) return true;\n\tconst code = error instanceof Error ? (error as unknown as { code?: unknown }).code : undefined;\n\treturn typeof code === 'string' && EFFECT_ERROR_CODES.includes(code as EffectErrorCode);\n}\n","/**\n * Helpers for providers and editors: value validation, catalog search, pixel buffers, masking,\n * and a provider slot editors hold.\n */\n\nimport { validateJsonSchema, type SchemaValidationError } from '../tools/schema.js';\nimport { EffectControlError, NoEffectsProviderError } from './errors.js';\nimport type {\n\tEffectDescription,\n\tEffectImageSource,\n\tEffectPixels,\n\tEffectProvider,\n\tEffectSearchOptions,\n\tEffectSummary,\n\tEffectValues\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Values\n// ---------------------------------------------------------------------------\n\n/** Problems with `values` against an effect's control schema (unknown controls, types, ranges). Partial values are fine. */\nexport function effectValueErrors(description: Pick<EffectDescription, 'controls'>, values: unknown): SchemaValidationError[] {\n\tif (values === undefined) return [];\n\treturn validateJsonSchema(values, { ...description.controls, required: [] });\n}\n\n/**\n * `values` over the effect's defaults, validated against its control schema. Throws\n * `EffectControlError` naming every bad control. Providers call this in `render`; editors call\n * it before saving values.\n */\nexport function resolveEffectValues(description: Pick<EffectDescription, 'id' | 'controls' | 'defaults'>, values?: EffectValues): Record<string, EffectValues[string]> {\n\tconst errors = effectValueErrors(description, values);\n\tif (errors.length) throw new EffectControlError(description.id, errors);\n\treturn { ...description.defaults, ...(values ?? {}) };\n}\n\n// ---------------------------------------------------------------------------\n// Search\n// ---------------------------------------------------------------------------\n\nconst matchesFilters = (e: EffectSummary, o: EffectSearchOptions) =>\n\t(!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use));\n\n/**\n * Filters a catalog and ranks it against `query` (every word must appear in the id, name,\n * category, tags or description; name matches rank first). An empty query keeps catalog order.\n * Providers can implement `list` and `search` with it.\n */\nexport function searchEffectSummaries<T extends EffectSummary>(catalog: readonly T[], query = '', options: EffectSearchOptions = {}): T[] {\n\tconst words = query.toLowerCase().split(/\\s+/).filter(Boolean);\n\tconst scored: { e: T; score: number; i: number }[] = [];\n\tcatalog.forEach((e, i) => {\n\t\tif (!matchesFilters(e, options)) return;\n\t\tconst name = e.name.toLowerCase();\n\t\tconst hay = [e.id, e.name, e.category, ...(e.tags ?? []), e.description ?? ''].join(' ').toLowerCase();\n\t\tlet score = 0;\n\t\tfor (const w of words) {\n\t\t\tif (!hay.includes(w)) return;\n\t\t\tscore += name.startsWith(w) ? 3 : name.includes(w) ? 2 : 1;\n\t\t}\n\t\tscored.push({ e, score, i });\n\t});\n\tscored.sort((a, b) => b.score - a.score || a.i - b.i);\n\tconst out = scored.map((s) => s.e);\n\treturn options.limit !== undefined ? out.slice(0, Math.max(0, options.limit)) : out;\n}\n\n// ---------------------------------------------------------------------------\n// Pixels\n// ---------------------------------------------------------------------------\n\n/** A transparent picture, with float samples when `float` is set. */\nexport function createEffectPixels(width: number, height: number, float = false): EffectPixels {\n\tconst n = Math.max(0, width * height * 4);\n\treturn { width, height, data: new Uint8ClampedArray(n), ...(float ? { float: new Float32Array(n) } : {}) };\n}\n\n/** True for an `EffectPixels` (rather than a host image object). */\nexport function isEffectPixels(source: EffectImageSource | unknown): source is EffectPixels {\n\treturn (\n\t\t!!source &&\n\t\ttypeof source === 'object' &&\n\t\t(source as EffectPixels).data instanceof Uint8ClampedArray &&\n\t\ttypeof (source as EffectPixels).width === 'number' &&\n\t\ttypeof (source as EffectPixels).height === 'number'\n\t);\n}\n\n/** sRGB-encoded 0–1 to linear light. */\nexport const srgbToLinear = (v: number) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4));\n/** Linear light to sRGB-encoded (clamped below at 0; may exceed 1). */\nexport const linearToSrgb = (v: number) => (v > 0 ? (v <= 0.0031308 ? v * 12.92 : 1.055 * Math.pow(v, 1 / 2.4) - 0.055) : 0);\n\n/** Fills `pixels.data` from `pixels.float` (linear float to clamped 8-bit sRGB). */\nexport function encodeEffectFloat(pixels: EffectPixels): EffectPixels {\n\tconst f = pixels.float;\n\tif (!f) return pixels;\n\tconst d = pixels.data;\n\tfor (let i = 0; i < d.length; i += 4) {\n\t\td[i] = Math.round(linearToSrgb(f[i]) * 255);\n\t\td[i + 1] = Math.round(linearToSrgb(f[i + 1]) * 255);\n\t\td[i + 2] = Math.round(linearToSrgb(f[i + 2]) * 255);\n\t\td[i + 3] = Math.round(Math.min(1, Math.max(0, f[i + 3])) * 255);\n\t}\n\treturn pixels;\n}\n\n/**\n * Blends an effect `result` back over its `source` by `mask` coverage (alpha channel), in place\n * on `result`: where the mask is 0 the source shows, where it is 255 the result. Without a\n * source (generators), the result's alpha is multiplied by coverage. Editors use it when the\n * provider does not `supportsMask`. All three must have the same size.\n */\nexport function applyEffectMask(result: EffectPixels, mask: EffectPixels, source?: EffectPixels): EffectPixels {\n\tconst { data } = result;\n\tconst m = mask.data;\n\tfor (let i = 0; i < data.length; i += 4) {\n\t\tconst k = m[i + 3] / 255;\n\t\tif (k >= 1) continue;\n\t\tif (source) {\n\t\t\tfor (let c = 0; c < 4; c++) data[i + c] = source.data[i + c] + (data[i + c] - source.data[i + c]) * k;\n\t\t\tif (result.float && source.float) {\n\t\t\t\tfor (let c = 0; c < 4; c++) result.float[i + c] = source.float[i + c] + (result.float[i + c] - source.float[i + c]) * k;\n\t\t\t}\n\t\t} else {\n\t\t\tdata[i + 3] = data[i + 3] * k;\n\t\t\tif (result.float) result.float[i + 3] *= k;\n\t\t}\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// The slot an editor keeps\n// ---------------------------------------------------------------------------\n\n/**\n * Holds an editor's effects provider. Controllers expose it as `effectsProvider`,\n * `setEffectsProvider(provider | null)` and `useEffects(provider)`; listeners hear every change.\n */\nexport class EffectsSlot {\n\tprivate current: EffectProvider | null = null;\n\tprivate readonly listeners = new Set<(provider: EffectProvider | null) => void>();\n\n\tget provider(): EffectProvider | null {\n\t\treturn this.current;\n\t}\n\n\tset(provider: EffectProvider | null): void {\n\t\tif (provider === this.current) return;\n\t\tthis.current = provider;\n\t\tfor (const l of [...this.listeners]) l(provider);\n\t}\n\n\t/** Sets `provider` and returns a function that clears it again (if it is still the one set). */\n\tuse(provider: EffectProvider): () => void {\n\t\tthis.set(provider);\n\t\treturn () => {\n\t\t\tif (this.current === provider) this.set(null);\n\t\t};\n\t}\n\n\t/** The provider, or a `NoEffectsProviderError`. */\n\trequire(): EffectProvider {\n\t\tif (!this.current) throw new NoEffectsProviderError();\n\t\treturn this.current;\n\t}\n\n\tsubscribe(listener: (provider: EffectProvider | null) => void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n}\n","/**\n * `EffectRendererPool`: keeps a few live renderers, reused per effect, size and depth.\n *\n * GPU providers hold one graphics context per renderer, and browsers allow only about sixteen\n * WebGL contexts at a time (the oldest is lost after that). So the pool keeps at most\n * `maxRenderers` renderers. It disposes the least recently used one once its renders finish, and it\n * runs one render at a time per renderer, as the contract requires.\n */\n\nimport type { EffectPixels, EffectProvider, EffectRenderer, EffectRenderRequest } from './types.js';\n\ninterface PoolEntry {\n\trenderer: Promise<EffectRenderer>;\n\t/** The last render queued on it. */\n\tqueue: Promise<unknown>;\n}\n\nexport interface EffectRendererPoolOptions {\n\t/** Most live renderers (default 4). */\n\tmaxRenderers?: number;\n}\n\nexport class EffectRendererPool {\n\tprivate readonly entries = new Map<string, PoolEntry>();\n\tprivate readonly maxRenderers: number;\n\n\tconstructor(options: EffectRendererPoolOptions = {}) {\n\t\tthis.maxRenderers = Math.max(1, options.maxRenderers ?? 4);\n\t}\n\n\t/** Live renderers now. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/** Renders `request` with the provider's renderer for this effect, size and depth (made on first use). */\n\trender(provider: EffectProvider, id: string, size: { width: number; height: number; depth?: 8 | 16 | 32 }, request: EffectRenderRequest): Promise<EffectPixels> {\n\t\tconst depth = size.depth ?? 8;\n\t\tconst key = `${provider.id}:${id}@${size.width}x${size.height}x${depth}`;\n\t\tlet entry = this.entries.get(key);\n\t\tif (entry) {\n\t\t\tthis.entries.delete(key);\n\t\t\tthis.entries.set(key, entry);\n\t\t} else {\n\t\t\tconst renderer = Promise.resolve().then(() => provider.createRenderer(id, { width: size.width, height: size.height, depth }));\n\t\t\trenderer.catch(() => {\n\t\t\t\tif (this.entries.get(key)?.renderer === renderer) this.entries.delete(key);\n\t\t\t});\n\t\t\tentry = { renderer, queue: Promise.resolve() };\n\t\t\tthis.entries.set(key, entry);\n\t\t\tthis.evict();\n\t\t}\n\t\tconst e = entry;\n\t\tconst next = e.queue.then(() => e.renderer).then((r) => r.render(request));\n\t\te.queue = next.catch(() => undefined);\n\t\treturn next;\n\t}\n\n\t/** Disposes the least recently used renderers over the limit, after their queued renders. */\n\tprivate evict(): void {\n\t\twhile (this.entries.size > this.maxRenderers) {\n\t\t\tconst [key, entry] = this.entries.entries().next().value!;\n\t\t\tthis.entries.delete(key);\n\t\t\tdispose(entry);\n\t\t}\n\t}\n\n\t/** Disposes every renderer (after its queued renders). */\n\tclear(): void {\n\t\tfor (const entry of this.entries.values()) dispose(entry);\n\t\tthis.entries.clear();\n\t}\n}\n\nfunction dispose(entry: PoolEntry) {\n\tentry.queue\n\t\t.then(() => entry.renderer)\n\t\t.then(\n\t\t\t(r) => r.dispose(),\n\t\t\t() => {}\n\t\t);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4661,6 +4661,15 @@ var fail4 = (message) => {
|
|
|
4661
4661
|
throw new LayerOperationError(message);
|
|
4662
4662
|
};
|
|
4663
4663
|
var SMART_RENDER_MARGIN = MAX_EFFECT_REACH;
|
|
4664
|
+
var incompleteSurfaces = /* @__PURE__ */ new WeakSet();
|
|
4665
|
+
var isSmartSurfaceIncomplete = (surface) => incompleteSurfaces.has(surface);
|
|
4666
|
+
var surfaceIds = /* @__PURE__ */ new WeakMap();
|
|
4667
|
+
var nextSurfaceId = 0;
|
|
4668
|
+
var surfaceId = (s) => {
|
|
4669
|
+
let id = surfaceIds.get(s);
|
|
4670
|
+
if (id === void 0) surfaceIds.set(s, id = ++nextSurfaceId);
|
|
4671
|
+
return id;
|
|
4672
|
+
};
|
|
4664
4673
|
function newContentsId() {
|
|
4665
4674
|
return newLayerId("so");
|
|
4666
4675
|
}
|
|
@@ -4802,7 +4811,15 @@ function placeContents(layer, canvas, options = {}) {
|
|
|
4802
4811
|
}
|
|
4803
4812
|
function activeSmartFilters(stack) {
|
|
4804
4813
|
if (!stack?.enabled) return [];
|
|
4805
|
-
return stack.filters.filter((f) => f.kind !== "psd" && isActiveFilter(f) && f.opacity > 0);
|
|
4814
|
+
return stack.filters.filter((f) => f.kind !== "psd" && f.kind !== "effect" && isActiveFilter(f) && f.opacity > 0);
|
|
4815
|
+
}
|
|
4816
|
+
function activeEffectSmartFilters(stack) {
|
|
4817
|
+
if (!stack?.enabled) return [];
|
|
4818
|
+
return stack.filters.filter((f) => f.kind === "effect" && f.enabled && f.opacity > 0);
|
|
4819
|
+
}
|
|
4820
|
+
function runnableSmartFilters(stack) {
|
|
4821
|
+
if (!stack?.enabled) return [];
|
|
4822
|
+
return stack.filters.filter((f) => f.opacity > 0 && (f.kind === "effect" ? f.enabled : f.kind !== "psd" && isActiveFilter(f)));
|
|
4806
4823
|
}
|
|
4807
4824
|
function filterMaskCoverage(mask, canvas) {
|
|
4808
4825
|
if (!mask || mask.enabled === false || mask.source) return null;
|
|
@@ -4823,27 +4840,41 @@ function filterMaskCoverage(mask, canvas) {
|
|
|
4823
4840
|
return v;
|
|
4824
4841
|
}
|
|
4825
4842
|
function applySmartFilters(placed, stack, canvas) {
|
|
4826
|
-
const active =
|
|
4843
|
+
const active = runnableSmartFilters(stack);
|
|
4827
4844
|
if (!active.length || !(canvas.width > 0 && canvas.height > 0)) return placed;
|
|
4828
4845
|
const W = canvas.width;
|
|
4829
4846
|
const H = canvas.height;
|
|
4830
|
-
const
|
|
4847
|
+
const token = active.some((f) => f.kind === "effect") ? canvas.effects?.token ?? -1 : 0;
|
|
4848
|
+
const key = JSON.stringify([W, H, token, active.map((f) => ({ ...f, id: void 0 }))]);
|
|
4831
4849
|
let entry = filteredCache.get(placed);
|
|
4850
|
+
let incomplete = false;
|
|
4832
4851
|
if (!entry || entry.key !== key) {
|
|
4833
4852
|
const base = readRegion(placed, { x: 0, y: 0, width: W, height: H });
|
|
4834
4853
|
let acc = new Uint8ClampedArray(base);
|
|
4835
|
-
for (const f of active) {
|
|
4836
|
-
|
|
4837
|
-
|
|
4854
|
+
for (const [i, f] of active.entries()) {
|
|
4855
|
+
let out2;
|
|
4856
|
+
if (f.kind === "effect") {
|
|
4857
|
+
if (incomplete) continue;
|
|
4858
|
+
const inputKey = JSON.stringify([surfaceId(placed), W, H, active.slice(0, i + 1).map((x) => ({ ...x, id: void 0, blend: x === f ? void 0 : x.blend, opacity: x === f ? void 0 : x.opacity }))]);
|
|
4859
|
+
const r = canvas.effects?.resolve(f, acc, W, H, inputKey);
|
|
4860
|
+
if (!r?.result) {
|
|
4861
|
+
if (r?.pending) incomplete = true;
|
|
4862
|
+
continue;
|
|
4863
|
+
}
|
|
4864
|
+
out2 = new Uint8ClampedArray(r.result);
|
|
4865
|
+
} else {
|
|
4866
|
+
out2 = new Uint8ClampedArray(acc);
|
|
4867
|
+
runLayerFilters(out2, W, H, [f]);
|
|
4868
|
+
}
|
|
4838
4869
|
if (f.blend === "normal") {
|
|
4839
|
-
if (f.opacity >= 1) acc =
|
|
4840
|
-
else lerpInto(acc,
|
|
4870
|
+
if (f.opacity >= 1) acc = out2;
|
|
4871
|
+
else lerpInto(acc, out2, f.opacity, null);
|
|
4841
4872
|
} else {
|
|
4842
|
-
blendTile(acc,
|
|
4873
|
+
blendTile(acc, out2, f.blend, f.opacity, null);
|
|
4843
4874
|
}
|
|
4844
4875
|
}
|
|
4845
4876
|
entry = { key, base, filtered: acc };
|
|
4846
|
-
filteredCache.set(placed, entry);
|
|
4877
|
+
if (!incomplete) filteredCache.set(placed, entry);
|
|
4847
4878
|
}
|
|
4848
4879
|
const cov = filterMaskCoverage(stack.mask, canvas);
|
|
4849
4880
|
let result = entry.filtered;
|
|
@@ -4851,7 +4882,9 @@ function applySmartFilters(placed, stack, canvas) {
|
|
|
4851
4882
|
result = new Uint8ClampedArray(entry.base);
|
|
4852
4883
|
lerpInto(result, entry.filtered, 1, cov);
|
|
4853
4884
|
}
|
|
4854
|
-
|
|
4885
|
+
const out = surfaceFromPixels({ width: W, height: H, data: result }, 4, 0, 0);
|
|
4886
|
+
if (incomplete) incompleteSurfaces.add(out);
|
|
4887
|
+
return out;
|
|
4855
4888
|
}
|
|
4856
4889
|
function lerpInto(dst, src, k, coverage) {
|
|
4857
4890
|
for (let i = 0, p = 0; i < dst.length; i += 4, p++) {
|
|
@@ -4940,7 +4973,7 @@ function offsetSmartObject(layers, id, dx, dy, canvas) {
|
|
|
4940
4973
|
const moveMask = (m2) => m2 && m2.linked !== false && !m2.source ? { ...m2, surface: translateSurface(m2.surface, mx, my) } : m2;
|
|
4941
4974
|
if (layer.mask) next.mask = moveMask(layer.mask);
|
|
4942
4975
|
if (layer.smartFilters?.mask) next.smartFilters = { ...layer.smartFilters, mask: moveMask(layer.smartFilters.mask) };
|
|
4943
|
-
const spatial = activeSmartFilters(layer.smartFilters).some((f) => f.kind === "blur");
|
|
4976
|
+
const spatial = activeSmartFilters(layer.smartFilters).some((f) => f.kind === "blur") || activeEffectSmartFilters(layer.smartFilters).length > 0;
|
|
4944
4977
|
const b = contentBounds(layer.surface);
|
|
4945
4978
|
const m = SMART_RENDER_MARGIN;
|
|
4946
4979
|
const cut = b && (b.x <= -m || b.y <= -m || b.x + b.width >= canvas.width + m || b.y + b.height >= canvas.height + m);
|
|
@@ -5185,13 +5218,27 @@ function refitPlacement(layer, contents) {
|
|
|
5185
5218
|
}
|
|
5186
5219
|
function normalizeSmartFilter(raw, id = newLayerId("flt")) {
|
|
5187
5220
|
if (raw.kind === "psd") fail4("Photoshop filters can only come from a PSD; add develop or blur.");
|
|
5188
|
-
const base = normalizeFilter(raw, id);
|
|
5221
|
+
const base = raw.kind === "effect" ? normalizeEffectSmartFilter(raw, id) : normalizeFilter(raw, id);
|
|
5189
5222
|
const blend = raw.blend ?? "normal";
|
|
5190
5223
|
if (!isBlendMode(blend) || blend === "pigment") fail4(`Unknown blend mode "${String(blend)}" for a smart filter.`);
|
|
5191
5224
|
const opacity = raw.opacity ?? 1;
|
|
5192
5225
|
if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail4("opacity must be a number from 0 to 1.");
|
|
5193
5226
|
return { ...base, blend, opacity };
|
|
5194
5227
|
}
|
|
5228
|
+
function normalizeEffectSmartFilter(raw, id) {
|
|
5229
|
+
if (typeof raw.effect !== "string" || !raw.effect) fail4('An effect smart filter needs an "effect" id from search_effects.');
|
|
5230
|
+
const values = raw.values ?? {};
|
|
5231
|
+
if (typeof values !== "object" || values === null || Array.isArray(values)) fail4('"values" must be an object of control values.');
|
|
5232
|
+
return {
|
|
5233
|
+
id,
|
|
5234
|
+
kind: "effect",
|
|
5235
|
+
enabled: raw.enabled !== false,
|
|
5236
|
+
effect: raw.effect,
|
|
5237
|
+
...typeof raw.provider === "string" ? { provider: raw.provider } : {},
|
|
5238
|
+
...typeof raw.name === "string" ? { name: raw.name } : {},
|
|
5239
|
+
values: { ...values }
|
|
5240
|
+
};
|
|
5241
|
+
}
|
|
5195
5242
|
function withStack(layer, stack, canvas) {
|
|
5196
5243
|
const next = { ...layer };
|
|
5197
5244
|
if (stack && (stack.filters.length || stack.mask || !stack.enabled)) next.smartFilters = stack;
|
|
@@ -5422,6 +5469,7 @@ function serializeLayer(layer, ctx) {
|
|
|
5422
5469
|
}
|
|
5423
5470
|
if (layer.fillOpacity !== void 0 && layer.fillOpacity !== 1) out.fillOpacity = layer.fillOpacity;
|
|
5424
5471
|
if (layer.filters?.length) out.filters = JSON.parse(JSON.stringify(layer.filters));
|
|
5472
|
+
if (layer.generator) out.generator = JSON.parse(JSON.stringify(layer.generator));
|
|
5425
5473
|
if (layer.source !== "document") {
|
|
5426
5474
|
const deep = encodeDeepSurface(layer.surface);
|
|
5427
5475
|
if (deep) out.deep = deep;
|
|
@@ -5812,6 +5860,8 @@ function deserializeLayer(raw, ctx) {
|
|
|
5812
5860
|
if (typeof r.fillOpacity === "number" && r.fillOpacity !== 1) layer.fillOpacity = num2(r.fillOpacity, 0, 1, 1);
|
|
5813
5861
|
const filters = readFilters(r.filters);
|
|
5814
5862
|
if (filters) layer.filters = filters;
|
|
5863
|
+
const generator = readEffectRef(r.generator);
|
|
5864
|
+
if (generator) layer.generator = { ...generator, ...typeof r.generator.time === "number" && r.generator.time > 0 ? { time: r.generator.time } : {} };
|
|
5815
5865
|
return layer;
|
|
5816
5866
|
}
|
|
5817
5867
|
case "adjustment": {
|
|
@@ -5898,6 +5948,15 @@ function readContents(id, ctx) {
|
|
|
5898
5948
|
memo.set(id, c);
|
|
5899
5949
|
return c;
|
|
5900
5950
|
}
|
|
5951
|
+
var isEffectValue = (x) => typeof x === "string" || typeof x === "boolean" || typeof x === "number" && Number.isFinite(x) || Array.isArray(x) && x.every((n) => typeof n === "number" && Number.isFinite(n));
|
|
5952
|
+
function readEffectRef(raw) {
|
|
5953
|
+
if (!raw || typeof raw !== "object" || typeof raw.effect !== "string" || !raw.effect) return void 0;
|
|
5954
|
+
const values = {};
|
|
5955
|
+
if (raw.values && typeof raw.values === "object") {
|
|
5956
|
+
for (const [k, v] of Object.entries(raw.values)) if (isEffectValue(v)) values[k] = Array.isArray(v) ? [...v] : v;
|
|
5957
|
+
}
|
|
5958
|
+
return { effect: raw.effect, ...typeof raw.provider === "string" ? { provider: raw.provider } : {}, ...typeof raw.name === "string" ? { name: raw.name } : {}, values };
|
|
5959
|
+
}
|
|
5901
5960
|
function readSmartFilters(raw, ctx, what) {
|
|
5902
5961
|
if (!raw || typeof raw !== "object") return void 0;
|
|
5903
5962
|
const r = raw;
|
|
@@ -5918,6 +5977,10 @@ function readSmartFilters(raw, ctx, what) {
|
|
|
5918
5977
|
} else if (f.kind === "develop" || f.kind === "blur") {
|
|
5919
5978
|
const base = plain[k++];
|
|
5920
5979
|
if (base) filters.push({ ...base, blend, opacity });
|
|
5980
|
+
} else if (f.kind === "effect") {
|
|
5981
|
+
const ref = readEffectRef(f);
|
|
5982
|
+
if (ref) filters.push({ id: typeof f.id === "string" ? f.id : newLayerId("flt"), kind: "effect", enabled: f.enabled !== false, ...ref, blend, opacity });
|
|
5983
|
+
else ctx.problems.push(`${what}: an effect smart filter without an effect id was left out`);
|
|
5921
5984
|
}
|
|
5922
5985
|
}
|
|
5923
5986
|
const stack = { enabled: r.enabled !== false, filters };
|
|
@@ -11594,6 +11657,7 @@ export {
|
|
|
11594
11657
|
isValidWarp,
|
|
11595
11658
|
geometryHomography,
|
|
11596
11659
|
SMART_RENDER_MARGIN,
|
|
11660
|
+
isSmartSurfaceIncomplete,
|
|
11597
11661
|
newContentsId,
|
|
11598
11662
|
contentsBox,
|
|
11599
11663
|
placementGeometry,
|
|
@@ -11605,6 +11669,7 @@ export {
|
|
|
11605
11669
|
documentContents,
|
|
11606
11670
|
placeContents,
|
|
11607
11671
|
activeSmartFilters,
|
|
11672
|
+
activeEffectSmartFilters,
|
|
11608
11673
|
filterMaskCoverage,
|
|
11609
11674
|
applySmartFilters,
|
|
11610
11675
|
renderSmartObject,
|
|
@@ -11671,4 +11736,4 @@ export {
|
|
|
11671
11736
|
smartContentsFile,
|
|
11672
11737
|
exportPsd
|
|
11673
11738
|
};
|
|
11674
|
-
//# sourceMappingURL=chunk-
|
|
11739
|
+
//# sourceMappingURL=chunk-WATAQ2Z2.js.map
|