@snowcone-app/canvas 0.18.1 → 0.19.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.
Files changed (32) hide show
  1. package/dist/{EditorCanvasImpl-Bv4yagew.js → EditorCanvasImpl-_3xCnLkx.js} +4 -4
  2. package/dist/{EditorCanvasImpl-Bv4yagew.js.map → EditorCanvasImpl-_3xCnLkx.js.map} +1 -1
  3. package/dist/EditorContext-5QpqAhjh.js.map +1 -1
  4. package/dist/{SnowconeCanvas-C_O-aeaQ.js → SnowconeCanvas-3PA17na3.js} +2 -2
  5. package/dist/{SnowconeCanvas-C_O-aeaQ.js.map → SnowconeCanvas-3PA17na3.js.map} +1 -1
  6. package/dist/api/embed.d.ts +2 -2
  7. package/dist/api/testing.d.ts +3 -0
  8. package/dist/components/editor/EditorBuy.d.ts +16 -5
  9. package/dist/components/editor/RootInternals.d.ts +5 -0
  10. package/dist/components/editor/commerceStore.d.ts +68 -0
  11. package/dist/components/editor/index.d.ts +4 -0
  12. package/dist/components/editor/useProductSelection.d.ts +39 -0
  13. package/dist/design-builder-v4935TeS.js +343 -0
  14. package/dist/design-builder-v4935TeS.js.map +1 -0
  15. package/dist/embed-D2QOpJXG.js +1090 -0
  16. package/dist/embed-D2QOpJXG.js.map +1 -0
  17. package/dist/embed.mjs +27 -25
  18. package/dist/embed.mjs.map +1 -1
  19. package/dist/errors/editorError.d.ts +15 -1
  20. package/dist/index.mjs +40 -39
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/template-fields-Dr3fMItw.js +329 -0
  23. package/dist/template-fields-Dr3fMItw.js.map +1 -0
  24. package/dist/testing/assertRenders.d.ts +53 -0
  25. package/dist/testing/failureFixtures.d.ts +25 -0
  26. package/dist/testing.mjs +191 -80
  27. package/dist/testing.mjs.map +1 -1
  28. package/package.json +3 -3
  29. package/dist/embed-Cdbjay9i.js +0 -961
  30. package/dist/embed-Cdbjay9i.js.map +0 -1
  31. package/dist/template-fields-DgoBtajp.js +0 -668
  32. package/dist/template-fields-DgoBtajp.js.map +0 -1
@@ -1,668 +0,0 @@
1
- import { jsxs as rt, jsx as _ } from "react/jsx-runtime";
2
- import { p as z, o as P, U as it } from "./MenuButton-CeoEAtzP.js";
3
- import { d as at, u as Q, c as T, b as $, g as L } from "./EditorContext-5QpqAhjh.js";
4
- import { useCallback as w, useEffect as st, useMemo as H, useRef as R, useState as X } from "react";
5
- import { c as ct, y as K, T as B, I as Z, z as lt } from "./FontAnalyzer-DIlqmyuf.js";
6
- import { a as dt, o as E, b as f, n as ft, l as b, u as V, c as ht } from "./CanvasStateV1-Bq48qhv5.js";
7
- const _t = () => {
8
- const { undo: t, redo: e, canUndo: n, canRedo: o } = at();
9
- return /* @__PURE__ */ rt("div", { className: "sc-undo-redo", style: { display: "inline-flex", gap: 4 }, children: [
10
- /* @__PURE__ */ _(
11
- z,
12
- {
13
- icon: P.undo,
14
- onClick: t,
15
- disabled: !n,
16
- tooltip: "Undo",
17
- "aria-label": "Undo"
18
- }
19
- ),
20
- /* @__PURE__ */ _(
21
- z,
22
- {
23
- icon: P.redo,
24
- onClick: e,
25
- disabled: !o,
26
- tooltip: "Redo",
27
- "aria-label": "Redo"
28
- }
29
- )
30
- ] });
31
- }, j = ct("useCommands");
32
- function zt() {
33
- const t = Q(), {
34
- undo: e,
35
- redo: n,
36
- canUndo: o,
37
- canRedo: i,
38
- executeElementUpdate: r,
39
- executeAddElement: a,
40
- executeRemoveElement: s,
41
- executeReorderElement: l,
42
- executeCommandBatch: c,
43
- executeCreateArtboard: m,
44
- executeDeleteArtboard: u,
45
- executeUpdateArtboard: h
46
- } = t, g = w(() => {
47
- j.warn("[useCommands] clearHistory is not yet implemented in EditorContext");
48
- }, []), N = w((k) => {
49
- j.warn("[useCommands] clearArtboardHistory is not yet implemented in EditorContext");
50
- }, []);
51
- return {
52
- // Undo/Redo
53
- undo: e,
54
- redo: n,
55
- canUndo: o,
56
- canRedo: i,
57
- // History management
58
- clearHistory: g,
59
- clearArtboardHistory: N,
60
- // Command execution
61
- executeElementUpdate: r,
62
- executeAddElement: a,
63
- executeRemoveElement: s,
64
- executeReorderElement: l,
65
- executeCreateArtboard: m,
66
- executeDeleteArtboard: u,
67
- executeUpdateArtboard: h,
68
- // Batch execution
69
- executeCommandBatch: c
70
- };
71
- }
72
- function Pt() {
73
- const { selectedElement: t } = Q();
74
- return t ?? null;
75
- }
76
- const mt = 3e3, G = {
77
- text: "useTextBinding",
78
- image: "useImageBinding",
79
- font: "useFontBinding"
80
- }, ut = {
81
- text: "setText",
82
- image: "setImageUrl",
83
- font: "setFontFamily"
84
- }, gt = {
85
- text: "text",
86
- image: "image",
87
- font: "text"
88
- }, S = /* @__PURE__ */ new Set();
89
- function J(t) {
90
- return t instanceof B ? "text" : t instanceof Z ? "image" : "other";
91
- }
92
- function pt(t, e, n) {
93
- const o = `[${G[t]}]`, i = `the binding is dead (isConnected: false, ${ut[t]} is a no-op)`, r = gt[t], a = n.getAllByName(e);
94
- if (a.length > 0) {
95
- const c = [...new Set(a.map(J))], m = r === "text" ? "image" : "text", u = c.length === 1 && c[0] === m ? ` Bind it with ${G[m]}("${e}") instead.` : "";
96
- return `${o} the layer named "${e}" is ${c.join("/")}, not ${r} — ${i}.${u}`;
97
- }
98
- const s = n.getAll().filter((c) => c.name).map((c) => `"${c.name}" (${J(c)})`), l = s.length ? `Named layers on this canvas: ${s.join(", ")}. Check the name for a typo.` : `No element on this canvas has a name — name one via design().${r}(…, { name: '${e}' }) or the element's \`name\` property.`;
99
- return `${o} no ${r} layer named "${e}" exists — ${i}. ${l}`;
100
- }
101
- function U(t, e, n, o) {
102
- st(() => {
103
- var a;
104
- if (o || typeof K > "u" || ((a = K.env) == null ? void 0 : a.NODE_ENV) === "production") return;
105
- const i = `${t}:${e}`;
106
- if (S.has(i)) return;
107
- const r = setTimeout(() => {
108
- S.has(i) || (S.add(i), console.warn(pt(t, e, n)));
109
- }, mt);
110
- return () => clearTimeout(r);
111
- }, [t, e, n, o]);
112
- }
113
- const yt = () => {
114
- }, bt = [];
115
- function Lt(t) {
116
- const { elementStore: e } = T(), { executeElementUpdate: n } = $(), o = H(() => e.getAllByName(t).filter((a) => a instanceof B), [e, t]), i = w(
117
- (r) => {
118
- for (const a of o) {
119
- const s = a.clone();
120
- s.setText(r), n(a, s);
121
- }
122
- },
123
- [o, n]
124
- );
125
- return U("text", t, e, o.length > 0), o.length === 0 ? { text: "", setText: yt, element: null, elements: bt, isConnected: !1 } : {
126
- text: o[0].getText(),
127
- setText: i,
128
- element: o[0],
129
- elements: o,
130
- isConnected: !0
131
- };
132
- }
133
- const xt = () => {
134
- }, Ct = [];
135
- function wt(t, e, n) {
136
- const o = t / e;
137
- if (n >= o) {
138
- const i = e, r = i * n, a = t / r;
139
- return {
140
- width: r,
141
- height: i,
142
- cropX: (1 - a) / 2,
143
- cropY: 0,
144
- cropWidth: a,
145
- cropHeight: 1
146
- };
147
- } else {
148
- const i = t, r = i / n, a = e / r;
149
- return {
150
- width: i,
151
- height: r,
152
- cropX: 0,
153
- cropY: (1 - a) / 2,
154
- cropWidth: 1,
155
- cropHeight: a
156
- };
157
- }
158
- }
159
- function Xt(t, e) {
160
- const n = (e == null ? void 0 : e.fit) ?? "cover", { elementStore: o, setElements: i } = T(), { executeElementUpdate: r } = $(), a = H(() => o.getAllByName(t).filter((g) => g instanceof Z), [o, t]), s = R(/* @__PURE__ */ new Map());
161
- for (const h of a)
162
- if (!s.current.has(h.id)) {
163
- const g = h.transformData;
164
- s.current.set(h.id, {
165
- width: g.width * g.cropWidth,
166
- height: g.height * g.cropHeight
167
- });
168
- }
169
- const l = R(a);
170
- l.current = a;
171
- const c = R(r);
172
- c.current = r;
173
- const m = R(i);
174
- m.current = i;
175
- const u = w(
176
- (h) => {
177
- const g = l.current, N = c.current;
178
- for (const k of g) {
179
- const W = s.current.get(k.id);
180
- if (!W) continue;
181
- const { width: I, height: Y } = W, p = k.clone();
182
- p.imageLoaded = !1, p.imageElement = null, p.isCropping = !1, p.imageUrl = h, n === "cover" ? (p.preserveDimensions = !0, p.onLoadCallback = (d) => {
183
- const D = d.imageAspectRatio || 1, y = wt(I, Y, D);
184
- d.transformData.width = y.width, d.transformData.height = y.height, d.transformData.cropX = y.cropX, d.transformData.cropY = y.cropY, d.transformData.cropWidth = y.cropWidth, d.transformData.cropHeight = y.cropHeight, N(k, d);
185
- }) : (p.preserveDimensions = !1, p.onLoadCallback = (d) => {
186
- const D = d.transformData.width, y = d.transformData.height, v = Math.min(I / D, Y / y, 1);
187
- v < 1 && (d.transformData.width = D * v, d.transformData.height = y * v), d.transformData.cropX = 0, d.transformData.cropY = 0, d.transformData.cropWidth = 1, d.transformData.cropHeight = 1, N(k, d);
188
- }), p.loadImage(h);
189
- }
190
- },
191
- [n]
192
- // Stable deps only — imageElements/executeElementUpdate/setElements accessed via refs
193
- );
194
- return U("image", t, o, a.length > 0), a.length === 0 ? { imageUrl: "", setImageUrl: xt, element: null, elements: Ct, isConnected: !1 } : {
195
- imageUrl: a[0].imageUrl,
196
- setImageUrl: u,
197
- element: a[0],
198
- elements: a,
199
- isConnected: !0
200
- };
201
- }
202
- const kt = [];
203
- function A(t) {
204
- let e = t.trim().toUpperCase();
205
- return e.length === 4 && e.startsWith("#") && (e = "#" + e[1] + e[1] + e[2] + e[2] + e[3] + e[3]), e;
206
- }
207
- function x(t, e) {
208
- return typeof t == "string" && A(t) === e;
209
- }
210
- function tt(t, e) {
211
- var o, i, r;
212
- const n = t;
213
- return x(n.color, e) || x((o = n.transformData) == null ? void 0 : o.fillColor, e) || x((i = n.transformData) == null ? void 0 : i.strokeColor, e) || ((r = n.stroke) == null ? void 0 : r.enabled) === !0 && x(n.stroke.color, e);
214
- }
215
- function Et(t, e, n) {
216
- var a, s, l;
217
- if (!tt(t, e)) return null;
218
- const o = t.clone(), i = t, r = o;
219
- return x(i.color, e) && (typeof r.setColor == "function" ? r.setColor(n) : r.color = n), x((a = i.transformData) == null ? void 0 : a.fillColor, e) && (r.transformData = { ...r.transformData, fillColor: n }), x((s = i.transformData) == null ? void 0 : s.strokeColor, e) && (r.transformData = { ...r.transformData, strokeColor: n }), ((l = i.stroke) == null ? void 0 : l.enabled) === !0 && x(i.stroke.color, e) && (r.stroke = { ...r.stroke, color: n }), o;
220
- }
221
- function Kt(t) {
222
- const { elementStore: e } = T(), { executeElementUpdate: n } = $(), [o, i] = X(() => A(t)), [r, a] = X(t);
223
- t !== r && (a(t), i(A(t)));
224
- const s = H(
225
- () => e.getAll().filter((c) => tt(c, o)),
226
- [e, o]
227
- ), l = w(
228
- (c) => {
229
- const m = A(c);
230
- if (m !== o) {
231
- for (const u of e.getAll()) {
232
- const h = Et(u, o, c);
233
- h && n(u, h);
234
- }
235
- i(m);
236
- }
237
- },
238
- [e, o, n]
239
- );
240
- return s.length === 0 ? { color: o, setColor: l, elements: kt, isConnected: !1 } : { color: o, setColor: l, elements: s, isConnected: !0 };
241
- }
242
- const Tt = () => {
243
- }, Nt = [];
244
- function Vt(t) {
245
- const { elementStore: e } = T(), { executeElementUpdate: n } = $(), o = H(() => e.getAllByName(t).filter((a) => a instanceof B), [e, t]), i = w(
246
- (r) => {
247
- it.loadGoogleFontByName(r);
248
- for (const a of o) {
249
- if (a.fontFamily === r) continue;
250
- lt.clearCache(a.fontFamily);
251
- const s = a.clone();
252
- s.fontFamily = r, n(a, s);
253
- }
254
- },
255
- [o, n]
256
- );
257
- return U("font", t, e, o.length > 0), o.length === 0 ? { fontFamily: "", setFontFamily: Tt, element: null, elements: Nt, isConnected: !1 } : {
258
- fontFamily: o[0].fontFamily,
259
- setFontFamily: i,
260
- element: o[0],
261
- elements: o,
262
- isConnected: !0
263
- };
264
- }
265
- function jt(t) {
266
- const { elementStore: e } = T();
267
- return t ? e.getByName(t) ?? null : null;
268
- }
269
- const Dt = 1;
270
- function C(t) {
271
- return typeof t == "number" && Number.isFinite(t) ? Math.round(t * 100) / 100 : t;
272
- }
273
- function et(t) {
274
- return {
275
- ...t,
276
- ...t.x !== void 0 && { x: C(t.x) },
277
- ...t.y !== void 0 && { y: C(t.y) },
278
- ...t.width !== void 0 && { width: C(t.width) },
279
- ...t.height !== void 0 && { height: C(t.height) },
280
- ...t.rotation !== void 0 && { rotation: C(t.rotation) }
281
- };
282
- }
283
- function Rt(t) {
284
- var o;
285
- const e = (o = t.artboards) != null && o.length ? t.artboards : [{ name: "Front", width: 800, height: 800 }], n = e.map(() => []);
286
- for (const i of t.elements || []) {
287
- const r = i == null ? void 0 : i.artboard, a = typeof r == "string" ? e.findIndex((s) => (s.name || "Front") === r) : -1;
288
- n[a >= 0 ? a : 0].push(At(i));
289
- }
290
- return {
291
- schemaVersion: Dt,
292
- artboards: e.map((i, r) => ({
293
- id: `artboard-${r + 1}`,
294
- name: i.name || "Front",
295
- x: 0,
296
- y: 0,
297
- width: C(i.width || 800),
298
- height: C(i.height || 800),
299
- backgroundColor: i.backgroundColor || "transparent",
300
- // Pass the artboard's flag through — the server worker only paints the
301
- // background when this is true. Hardcoding `false` here silently dropped
302
- // explicitly-set artboard backgrounds from every realtime mockup while
303
- // the editor kept showing them (WYSIWYG drift).
304
- exportBackground: i.exportBackground ?? !1,
305
- elements: n[r],
306
- distressTexture: i.distressTexture,
307
- imageMask: i.imageMask
308
- }))
309
- };
310
- }
311
- function At(t) {
312
- const e = t.type || t.transformType;
313
- if (e === "image")
314
- return $t(t);
315
- const { artboard: n, ...o } = t;
316
- return et({
317
- ...o,
318
- type: e
319
- });
320
- }
321
- function $t(t) {
322
- var c;
323
- const e = t.transformData || {}, n = t.masks && t.masks.length > 0, o = e.width || 0, i = e.height || 0, r = n ? o : o * (e.cropWidth ?? 1), a = n ? i : i * (e.cropHeight ?? 1), s = {
324
- id: t.id,
325
- type: "image",
326
- x: t.x || 0,
327
- y: t.y || 0,
328
- width: r,
329
- height: a,
330
- rotation: t.rotation || 0,
331
- imageUrl: t.imageUrl,
332
- imageAspectRatio: t.imageAspectRatio
333
- };
334
- return !n && (e.cropX !== 0 || e.cropY !== 0 || e.cropWidth !== 1 || e.cropHeight !== 1) && (s.cropX = e.cropX, s.cropY = e.cropY, s.cropWidth = e.cropWidth, s.cropHeight = e.cropHeight, s.needsCropPixelConversion = !0), e.flipHorizontal && (s.flipHorizontal = e.flipHorizontal), e.flipVertical && (s.flipVertical = e.flipVertical), e.borderRadius && (s.borderRadius = e.borderRadius), t.opacity !== void 0 && (s.opacity = t.opacity), t.distressEffect && (s.distressEffect = t.distressEffect), ((c = t.masks) == null ? void 0 : c.length) > 0 && (s.masks = t.masks), t.blendMode && (s.blendMode = t.blendMode), t.knockoutParts && (s.knockoutParts = t.knockoutParts), t.stroke && (s.stroke = t.stroke), et(s);
335
- }
336
- const q = "center", nt = Symbol.for("snowcone.canvas.layerToken");
337
- function Gt(t) {
338
- return typeof t == "object" && t !== null && t[nt] === !0 && typeof t.name == "string" && (t.kind === "text" || t.kind === "image");
339
- }
340
- function Ht(t, e) {
341
- return Object.freeze({ kind: t, name: e, [nt]: !0 });
342
- }
343
- function O(t) {
344
- const e = t.transformType;
345
- return e === "image" ? "image" : e === "shape" || e === "group" || e === "path" ? null : "text";
346
- }
347
- function vt(t) {
348
- return t === "top-left" || t === "left" || t === "bottom-left" ? "left" : t === "top-right" || t === "right" || t === "bottom-right" ? "right" : "center";
349
- }
350
- function St(t) {
351
- return t === "top-left" || t === "top" || t === "top-right" ? "top" : t === "bottom-left" || t === "bottom" || t === "bottom-right" ? "bottom" : "middle";
352
- }
353
- function F(t, e, n, o, i, r) {
354
- const a = vt(t), s = St(t), l = a === "left" ? e / 2 + r : a === "right" ? o - e / 2 - r : o / 2, c = s === "top" ? n / 2 + r : s === "bottom" ? i - n / 2 - r : i / 2;
355
- return { x: l, y: c };
356
- }
357
- function Ft(t, e, n) {
358
- const o = e / n;
359
- if (t >= o) {
360
- const a = n * t, s = e / a;
361
- return {
362
- width: a,
363
- height: n,
364
- cropX: (1 - s) / 2,
365
- cropY: 0,
366
- cropWidth: s,
367
- cropHeight: 1
368
- };
369
- }
370
- const i = e / t, r = n / i;
371
- return {
372
- width: e,
373
- height: i,
374
- cropX: 0,
375
- cropY: (1 - r) / 2,
376
- cropWidth: 1,
377
- cropHeight: r
378
- };
379
- }
380
- class M {
381
- constructor(e) {
382
- if (this.els = [], this.idCounter = 0, !(e.width > 0) || !(e.height > 0))
383
- throw new Error("design(): artboard width and height must be positive numbers");
384
- this.artboard = {
385
- name: e.name || "Front",
386
- width: e.width,
387
- height: e.height,
388
- backgroundColor: e.backgroundColor
389
- };
390
- }
391
- /**
392
- * Seed a builder from a previously saved canvas state — the editor shape
393
- * `<SnowconeCanvas onChange>` emits (and `toJSON()` produces).
394
- *
395
- * This is how a saved editable template re-enters the turnkey embed path:
396
- * load the template's state JSON, lift it back into a `DesignBuilder`, and
397
- * hand it to `<RealtimeEmbed design={…}>`. The active artboard becomes the
398
- * builder's artboard; elements tagged for other artboards are skipped
399
- * (untagged elements are assumed to belong to the active one).
400
- */
401
- static fromState(e) {
402
- const n = e.artboards.find((i) => i.name === e.activeArtboard) ?? e.artboards[0];
403
- if (!n)
404
- throw new Error("DesignBuilder.fromState(): state has no artboards");
405
- const o = new M({
406
- name: n.name,
407
- width: n.width,
408
- height: n.height,
409
- backgroundColor: n.backgroundColor
410
- });
411
- for (const i of e.elements)
412
- i.artboard && i.artboard !== n.name || o.els.push({ ...i });
413
- return o;
414
- }
415
- nextId(e) {
416
- return `${e}-${++this.idCounter}`;
417
- }
418
- background(e, n = {}) {
419
- const { width: o, height: i } = this.artboard;
420
- let r;
421
- if (n.aspectRatio && n.aspectRatio > 0) {
422
- const s = Ft(n.aspectRatio, o, i);
423
- r = {
424
- type: "image",
425
- width: s.width,
426
- height: s.height,
427
- cropX: s.cropX,
428
- cropY: s.cropY,
429
- cropWidth: s.cropWidth,
430
- cropHeight: s.cropHeight,
431
- flipHorizontal: !1,
432
- flipVertical: !1,
433
- borderRadius: 0
434
- };
435
- } else
436
- r = {
437
- type: "image",
438
- width: o,
439
- height: i,
440
- cropX: 0,
441
- cropY: 0,
442
- cropWidth: 1,
443
- cropHeight: 1,
444
- flipHorizontal: !1,
445
- flipVertical: !1,
446
- borderRadius: 0
447
- };
448
- const a = {
449
- id: this.nextId("bg"),
450
- transformType: "image",
451
- x: o / 2,
452
- y: i / 2,
453
- rotation: 0,
454
- opacity: n.opacity ?? 1,
455
- imageUrl: e,
456
- imageAspectRatio: n.aspectRatio,
457
- transformData: r,
458
- name: n.name ?? "Background"
459
- };
460
- return this.els.unshift(a), this;
461
- }
462
- image(e, n = {}) {
463
- const { width: o, height: i } = this.artboard, r = n.width ?? Math.round(o / 3), a = n.height ?? (n.aspectRatio ? Math.round(r / n.aspectRatio) : r), { x: s, y: l } = F(n.anchor ?? q, r, a, o, i, n.padding ?? 0), c = {
464
- id: this.nextId("img"),
465
- transformType: "image",
466
- x: s,
467
- y: l,
468
- rotation: n.rotation ?? 0,
469
- opacity: n.opacity ?? 1,
470
- imageUrl: e,
471
- imageAspectRatio: n.aspectRatio,
472
- transformData: {
473
- type: "image",
474
- width: r,
475
- height: a,
476
- cropX: 0,
477
- cropY: 0,
478
- cropWidth: 1,
479
- cropHeight: 1,
480
- flipHorizontal: !1,
481
- flipVertical: !1,
482
- borderRadius: 0
483
- },
484
- ...n.name ? { name: n.name } : {}
485
- };
486
- return this.els.push(c), this;
487
- }
488
- text(e, n = {}) {
489
- const { width: o, height: i } = this.artboard, r = n.fontSize ?? Math.round(this.artboard.height / 12), { x: a, y: s } = F(n.anchor ?? "top", 0, r, o, i, n.padding ?? Math.round(i / 20)), l = {
490
- id: this.nextId("text"),
491
- transformType: "custom",
492
- x: a,
493
- y: s,
494
- rotation: n.rotation ?? 0,
495
- text: e,
496
- fontSize: r,
497
- fontFamily: n.fontFamily ?? "Anton",
498
- color: n.color ?? "#ffffff",
499
- textAlign: n.textAlign ?? "center",
500
- bold: n.bold ?? !1,
501
- italic: n.italic ?? !1,
502
- transformData: { type: "custom" },
503
- ...n.name ? { name: n.name } : {}
504
- };
505
- return this.els.push(l), this;
506
- }
507
- /**
508
- * Mint a {@link LayerToken} for a named layer — the only currency
509
- * `Editor.TextField` accepts (ADR-0085 §1). Compile time: `name` is
510
- * constrained to this design's declared layer names, kind inferred from
511
- * which per-kind union it falls in. Runtime: validated EAGERLY against the
512
- * built elements (nearest the mistake), so CMS/JSON-loaded designs get the
513
- * same protection with `string` typing.
514
- *
515
- * @throws EditorError `E_LAYER_NOT_FOUND` — no layer with this name; lists
516
- * the valid names per kind.
517
- * @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that
518
- * is not bindable (shape/group/path).
519
- * @experimental ADR-0085; subject to change until the P4 gate.
520
- */
521
- layer(e) {
522
- const n = this.els.find((i) => i.name === e);
523
- if (!n) {
524
- const i = this.layerNamesOfKind("text"), r = this.layerNamesOfKind("image");
525
- throw new L(
526
- "E_LAYER_NOT_FOUND",
527
- `no layer "${e}" in this design. Text layers: ${JSON.stringify(i)}; image layers: ${JSON.stringify(r)}. Fix the name, or add the layer first — e.g. design(…).text('…', { name: '${e}' }).`,
528
- { layerName: e, validNames: [...i, ...r], textNames: i, imageNames: r, docSlug: "embed-mode" }
529
- );
530
- }
531
- const o = O(n);
532
- if (!o)
533
- throw new L(
534
- "E_LAYER_WRONG_KIND",
535
- `layer "${e}" is a ${n.transformType} element — only text and image layers are bindable. Bindable layers: ${JSON.stringify([...this.layerNamesOfKind("text"), ...this.layerNamesOfKind("image")])}.`,
536
- { layerName: e, actualType: n.transformType, docSlug: "embed-mode" }
537
- );
538
- return Ht(o, e);
539
- }
540
- /** Names of bindable layers of one kind, in stack order. @internal */
541
- layerNamesOfKind(e) {
542
- return this.els.filter((n) => O(n) === e).map((n) => n.name).filter((n) => !!n);
543
- }
544
- /** An anchored vector shape. */
545
- shape(e, n = {}) {
546
- const { width: o, height: i } = this.artboard, r = n.size ?? Math.round(o / 6), a = typeof r == "number" ? r : r.width, s = typeof r == "number" ? r : r.height, { x: l, y: c } = F(n.anchor ?? q, a, s, o, i, n.padding ?? 0), m = {
547
- type: "shape",
548
- shapeType: e,
549
- width: a,
550
- height: s,
551
- fillColor: n.fill ?? "#000000",
552
- fillOpacity: n.opacity ?? 1,
553
- ...n.points !== void 0 ? { points: n.points } : {},
554
- ...n.sides !== void 0 ? { sides: n.sides } : {}
555
- }, u = {
556
- id: this.nextId("shape"),
557
- transformType: "shape",
558
- x: l,
559
- y: c,
560
- rotation: n.rotation ?? 0,
561
- transformData: m,
562
- ...n.name ? { name: n.name } : {}
563
- };
564
- return this.els.push(u), this;
565
- }
566
- /** The composed elements, in render order (background first). Editor shape. */
567
- get elements() {
568
- return this.els.map((e) => ({ ...e }));
569
- }
570
- /** Editor-shape canvas state for `<SnowconeCanvas initialElements=… />` and
571
- * `serializeStateForServer`. `activeArtboard` is the artboard name. */
572
- toJSON() {
573
- return {
574
- elements: this.elements,
575
- artboards: [{
576
- name: this.artboard.name,
577
- width: this.artboard.width,
578
- height: this.artboard.height,
579
- // A background set on a design is part of the design: flag it
580
- // renderable so exports and server renders paint it, matching the
581
- // editor (the export worker skips it without `exportBackground`).
582
- ...this.artboard.backgroundColor ? {
583
- backgroundColor: this.artboard.backgroundColor,
584
- exportBackground: this.artboard.backgroundColor !== "transparent"
585
- } : {}
586
- }],
587
- activeArtboard: this.artboard.name
588
- };
589
- }
590
- /** Server-render payload for `RenderSession.renderState(placement, …)`.
591
- * Equivalent to `serializeStateForServer(design.toJSON())`. */
592
- toServerRequest() {
593
- return Rt(this.toJSON());
594
- }
595
- }
596
- function Jt(t) {
597
- return new M(t);
598
- }
599
- function qt(t) {
600
- return O(t);
601
- }
602
- const ot = "personalization", Ot = dt("type", [
603
- E({
604
- type: b("text"),
605
- name: f().min(1),
606
- label: f(),
607
- placeholder: f().optional(),
608
- maxLength: ft().int().positive().optional(),
609
- group: f().optional()
610
- }),
611
- E({
612
- type: b("image"),
613
- name: f().min(1),
614
- label: f(),
615
- placeholder: f().optional(),
616
- fit: V([b("cover"), b("contain")]).optional(),
617
- group: f().optional()
618
- }),
619
- E({
620
- type: b("color"),
621
- color: f().min(1),
622
- label: f(),
623
- group: f().optional()
624
- }),
625
- E({
626
- type: b("select"),
627
- name: f().min(1),
628
- label: f(),
629
- property: V([b("text"), b("imageUrl"), b("fontFamily")]),
630
- options: ht(E({ label: f(), value: f() })).min(1),
631
- group: f().optional()
632
- })
633
- ]);
634
- function Qt(t) {
635
- if (!t || typeof t != "object") return [];
636
- const e = t[ot];
637
- if (!Array.isArray(e)) return [];
638
- const n = [];
639
- for (const o of e) {
640
- if (!o || typeof o != "object") continue;
641
- const i = "type" in o ? o : { ...o, type: "text" }, r = Ot.safeParse(i);
642
- r.success && n.push(r.data);
643
- }
644
- return n;
645
- }
646
- function Zt(t, e) {
647
- return { ...t, [ot]: e };
648
- }
649
- export {
650
- Dt as C,
651
- M as D,
652
- ot as T,
653
- _t as U,
654
- zt as a,
655
- jt as b,
656
- Vt as c,
657
- Jt as d,
658
- Xt as e,
659
- Pt as f,
660
- Lt as g,
661
- Gt as i,
662
- qt as l,
663
- Qt as r,
664
- Rt as s,
665
- Kt as u,
666
- Zt as w
667
- };
668
- //# sourceMappingURL=template-fields-DgoBtajp.js.map