@snowcone-app/canvas 0.11.0 → 0.12.0

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 (30) hide show
  1. package/dist/{CanvasStateV1-D3THl2Zc.js → CanvasStateV1-CKsfggNP.js} +3 -3
  2. package/dist/{CanvasStateV1-D3THl2Zc.js.map → CanvasStateV1-CKsfggNP.js.map} +1 -1
  3. package/dist/{HybridHistoryManager-DANuzx7G.js → HybridHistoryManager-NL3zyEl4.js} +21 -21
  4. package/dist/{HybridHistoryManager-DANuzx7G.js.map → HybridHistoryManager-NL3zyEl4.js.map} +1 -1
  5. package/dist/{ImportManager-NCtkY5Z0.js → ImportManager-eotl7r5d.js} +2 -2
  6. package/dist/{ImportManager-NCtkY5Z0.js.map → ImportManager-eotl7r5d.js.map} +1 -1
  7. package/dist/{ThemeContext-U-VDtIiS.js → ThemeContext-Cl1JPVLZ.js} +2 -2
  8. package/dist/{ThemeContext-U-VDtIiS.js.map → ThemeContext-Cl1JPVLZ.js.map} +1 -1
  9. package/dist/advanced.mjs +8 -8
  10. package/dist/api/embed.d.ts +3 -1
  11. package/dist/api/stable.d.ts +3 -1
  12. package/dist/components/embed-realtime/RealtimeMockup.d.ts +20 -0
  13. package/dist/components/embed-realtime/context.d.ts +9 -0
  14. package/dist/components/embed-realtime/usePieceGuideBundle.d.ts +48 -0
  15. package/dist/design/template-fields.d.ts +39 -1
  16. package/dist/embed.mjs +222 -141
  17. package/dist/embed.mjs.map +1 -1
  18. package/dist/hooks/useDeadBindingWarning.d.ts +2 -2
  19. package/dist/hooks/useFontBinding.d.ts +23 -0
  20. package/dist/index.mjs +24 -23
  21. package/dist/internals.mjs +4 -4
  22. package/dist/template-fields-IgDV7PRY.js +1627 -0
  23. package/dist/template-fields-IgDV7PRY.js.map +1 -0
  24. package/dist/testing.mjs +2 -2
  25. package/dist/utils/UnifiedFontService.d.ts +7 -0
  26. package/dist/{validation-C857Ptxn.js → validation-BpmNOvuY.js} +14 -4
  27. package/dist/{validation-C857Ptxn.js.map → validation-BpmNOvuY.js.map} +1 -1
  28. package/package.json +3 -3
  29. package/dist/template-fields-C8Tj67ek.js +0 -1585
  30. package/dist/template-fields-C8Tj67ek.js.map +0 -1
@@ -1,1585 +0,0 @@
1
- import { c as rt, y as j, z as Le, I as $t, T as ie } from "./HybridHistoryManager-DANuzx7G.js";
2
- import { jsxs as Q, jsx as v, Fragment as ce } from "react/jsx-runtime";
3
- import { useCallback as P, useMemo as ct, useRef as M, useEffect as C, Component as Oe, useState as Z, forwardRef as le, useImperativeHandle as Ye } from "react";
4
- import { u as de, T as Qt, E as Xe, c as wt, b as Bt } from "./ThemeContext-U-VDtIiS.js";
5
- import { ag as Zt, ah as _t, a8 as Ve, a9 as te, a5 as je, O as Ke, x as Ge, y as qe, H as Je, G as Qe, k as Ze, q as _e, a3 as tr, R as ee, s as er, l as rr, t as nr, r as or, v as ar, u as sr, g as ir, X as cr, N as lr, a as dr, n as ur } from "./validation-C857Ptxn.js";
6
- import { C as Mt, E as fr } from "./ImportManager-NCtkY5Z0.js";
7
- import { d as hr, o as Tt, s as X, l as st, u as mr } from "./schemas-B4UXytQB.js";
8
- const re = rt("useCommands");
9
- function gr() {
10
- const t = de(), {
11
- undo: e,
12
- redo: r,
13
- canUndo: o,
14
- canRedo: n,
15
- executeElementUpdate: a,
16
- executeAddElement: s,
17
- executeRemoveElement: i,
18
- executeReorderElement: c,
19
- executeCommandBatch: d,
20
- executeCreateArtboard: w,
21
- executeDeleteArtboard: T,
22
- executeUpdateArtboard: R
23
- } = t, k = P(() => {
24
- re.warn("[useCommands] clearHistory is not yet implemented in EditorContext");
25
- }, []), D = P((p) => {
26
- re.warn("[useCommands] clearArtboardHistory is not yet implemented in EditorContext");
27
- }, []);
28
- return {
29
- // Undo/Redo
30
- undo: e,
31
- redo: r,
32
- canUndo: o,
33
- canRedo: n,
34
- // History management
35
- clearHistory: k,
36
- clearArtboardHistory: D,
37
- // Command execution
38
- executeElementUpdate: a,
39
- executeAddElement: s,
40
- executeRemoveElement: i,
41
- executeReorderElement: c,
42
- executeCreateArtboard: w,
43
- executeDeleteArtboard: T,
44
- executeUpdateArtboard: R,
45
- // Batch execution
46
- executeCommandBatch: d
47
- };
48
- }
49
- const pr = () => {
50
- const { undo: t, redo: e, canUndo: r, canRedo: o } = gr();
51
- return /* @__PURE__ */ Q("div", { className: "sc-undo-redo", style: { display: "inline-flex", gap: 4 }, children: [
52
- /* @__PURE__ */ v(
53
- Zt,
54
- {
55
- icon: _t.undo,
56
- onClick: t,
57
- disabled: !r,
58
- tooltip: "Undo",
59
- "aria-label": "Undo"
60
- }
61
- ),
62
- /* @__PURE__ */ v(
63
- Zt,
64
- {
65
- icon: _t.redo,
66
- onClick: e,
67
- disabled: !o,
68
- tooltip: "Redo",
69
- "aria-label": "Redo"
70
- }
71
- )
72
- ] });
73
- }, ue = rt("KitLayout"), it = "canvas", Dt = 320;
74
- let ne = !1;
75
- function br() {
76
- ne || (ne = !0, Ve({
77
- LayersPanel: Ke,
78
- EffectsPanel: Ge,
79
- ExportPanel: qe,
80
- ImagePanel: Je,
81
- GlyphPanel: Qe,
82
- ArtboardTabs: Ze,
83
- ContextualToolbars: _e,
84
- ZoomControls: tr,
85
- MenuButton: ee,
86
- CropPanel: er
87
- }), te("add-element-menu", {
88
- component: ee,
89
- label: "Add Element",
90
- icon: "gravity-ui:plus",
91
- position: "topbar"
92
- }), te("undo-redo-controls", {
93
- component: pr,
94
- label: "Undo / Redo",
95
- icon: "lucide:undo-2",
96
- position: "topbar"
97
- }));
98
- }
99
- function xt({ ids: t }) {
100
- return !t || t.length === 0 ? null : /* @__PURE__ */ v(ce, { children: t.map((e) => {
101
- var n;
102
- if (e === it) return null;
103
- const r = je(e);
104
- if (!r)
105
- return typeof j < "u" && ((n = j.env) == null ? void 0 : n.NODE_ENV) === "development" && ue.warn(
106
- `Kit layout references unknown section "${e}" — skipping. Register it with registerSection("${e}", { ... }).`
107
- ), null;
108
- const o = r.component;
109
- return /* @__PURE__ */ v(o, {}, e);
110
- }) });
111
- }
112
- function yr({ kit: t, canvas: e }) {
113
- var T, R, k, D;
114
- br();
115
- const r = t.layout.slots, o = !!((T = r.topbar) != null && T.some((p) => p !== it)), n = !!((R = r.left) != null && R.some((p) => p !== it)), a = !!((k = r.right) != null && k.some((p) => p !== it)), s = !!((D = r.bottombar) != null && D.some((p) => p !== it)), i = ct(
116
- () => !o && !n && !a && !s,
117
- [o, n, a, s]
118
- ), c = M(null), d = o || n || a || s, w = M(!1);
119
- return C(() => {
120
- var b;
121
- if (typeof j > "u" || ((b = j.env) == null ? void 0 : b.NODE_ENV) !== "development" || !d) return;
122
- const p = c.current;
123
- if (!p || typeof ResizeObserver > "u") return;
124
- const F = (E) => {
125
- E > 0 && E < Dt && !w.current ? (w.current = !0, ue.warn(
126
- `Kit "${t.name}" declares chrome (toolbar / panels) but its container is only ${Math.round(E)}px wide — below the ~${Dt}px needed to render it, so the toolbar / panels are suppressed and the editor looks like a bare canvas. Give the editor more container width, or pass kit="embed-only" if a chrome-less canvas is intended.`
127
- )) : E >= Dt && (w.current = !1);
128
- };
129
- F(p.getBoundingClientRect().width);
130
- const S = new ResizeObserver((E) => {
131
- for (const x of E) F(x.contentRect.width);
132
- });
133
- return S.observe(p), () => S.disconnect();
134
- }, [d, t.name]), i ? /* @__PURE__ */ v(ce, { children: e }) : /* @__PURE__ */ Q("div", { ref: c, className: "sc-kit-layout", style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
135
- o && /* @__PURE__ */ v(
136
- "div",
137
- {
138
- className: "sc-kit-topbar toolbar",
139
- style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", flexShrink: 0 },
140
- children: /* @__PURE__ */ v(xt, { ids: r.topbar })
141
- }
142
- ),
143
- /* @__PURE__ */ Q("div", { className: "sc-kit-body", style: { display: "flex", flex: 1, minHeight: 0 }, children: [
144
- n && /* @__PURE__ */ v("div", { className: "sc-kit-left", style: { flexShrink: 0, overflow: "auto" }, children: /* @__PURE__ */ v(xt, { ids: r.left }) }),
145
- /* @__PURE__ */ v("div", { className: "sc-kit-canvas", style: { flex: 1, minWidth: 0, position: "relative" }, children: e }),
146
- a && /* @__PURE__ */ v("div", { className: "sc-kit-right", style: { flexShrink: 0, overflow: "auto" }, children: /* @__PURE__ */ v(xt, { ids: r.right }) })
147
- ] }),
148
- s && /* @__PURE__ */ v(
149
- "div",
150
- {
151
- className: "sc-kit-bottombar toolbar",
152
- style: { display: "flex", alignItems: "center", gap: 8, flexShrink: 0 },
153
- children: /* @__PURE__ */ v(xt, { ids: r.bottombar })
154
- }
155
- )
156
- ] });
157
- }
158
- const xr = rt("ErrorBoundary");
159
- class vr extends Oe {
160
- constructor(e) {
161
- super(e), this.resetError = () => {
162
- this.setState({
163
- hasError: !1,
164
- error: null
165
- });
166
- }, this.state = {
167
- hasError: !1,
168
- error: null
169
- };
170
- }
171
- static getDerivedStateFromError(e) {
172
- return {
173
- hasError: !0,
174
- error: e
175
- };
176
- }
177
- componentDidCatch(e, r) {
178
- if (xr.error("Caught error:", e, r), this.props.onError && this.props.onError(e, r), this.props.onCanvasError) {
179
- const o = {
180
- category: "unknown",
181
- message: e.message,
182
- originalError: e,
183
- recoverable: !1
184
- };
185
- this.props.onCanvasError(o);
186
- }
187
- }
188
- render() {
189
- return this.state.hasError && this.state.error ? this.props.renderError ? this.props.renderError(this.state.error, this.resetError) : this.props.fallback ? typeof this.props.fallback == "function" ? this.props.fallback(this.state.error, this.resetError) : this.props.fallback : /* @__PURE__ */ Q(
190
- "div",
191
- {
192
- style: {
193
- padding: "24px",
194
- border: "2px solid var(--danger)",
195
- borderRadius: "8px",
196
- backgroundColor: "color-mix(in oklch, var(--danger) 10%, var(--background))",
197
- color: "var(--danger)",
198
- fontFamily: "system-ui, sans-serif"
199
- },
200
- children: [
201
- /* @__PURE__ */ v("h2", { style: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: 600 }, children: "Something went wrong" }),
202
- /* @__PURE__ */ v(
203
- "pre",
204
- {
205
- style: {
206
- margin: "0 0 16px 0",
207
- padding: "12px",
208
- backgroundColor: "color-mix(in oklch, var(--danger) 15%, var(--background))",
209
- borderRadius: "4px",
210
- fontSize: "14px",
211
- overflow: "auto",
212
- maxHeight: "200px"
213
- },
214
- children: this.state.error.message
215
- }
216
- ),
217
- /* @__PURE__ */ v(
218
- "button",
219
- {
220
- onClick: this.resetError,
221
- style: {
222
- padding: "8px 16px",
223
- backgroundColor: "var(--danger)",
224
- color: "var(--danger-foreground)",
225
- border: "none",
226
- borderRadius: "4px",
227
- fontSize: "14px",
228
- fontWeight: 500,
229
- cursor: "pointer"
230
- },
231
- children: "Try again"
232
- }
233
- )
234
- ]
235
- }
236
- ) : this.props.children;
237
- }
238
- }
239
- const wr = rt("useAutoExport");
240
- function Er(t) {
241
- const { config: e, historyManager: r, artboards: o, elements: n, onExport: a, onExportScheduled: s, isCanvasReady: i = !1 } = t, c = M(null), d = M(!1), w = M({
242
- totalExports: 0,
243
- skippedExports: 0,
244
- lastExportTime: null,
245
- avgExportInterval: 0
246
- }), [T, R] = Z(w.current), k = M(a);
247
- k.current = a, C(() => {
248
- const b = new rr({
249
- ...nr,
250
- ...e
251
- });
252
- return b.onExport(async () => {
253
- try {
254
- await k.current(), w.current = b.getStats();
255
- } catch (E) {
256
- wr.error("[useAutoExport] Export failed:", E);
257
- }
258
- }), c.current = b, () => {
259
- b.destroy();
260
- };
261
- }, []), C(() => {
262
- i && d.current && c.current && (d.current = !1, c.current.forceExport());
263
- }, [i]), C(() => {
264
- c.current && e && c.current.updateConfig(e);
265
- }, [e == null ? void 0 : e.enabled, e == null ? void 0 : e.debounceMs, e == null ? void 0 : e.maxWaitMs]), C(() => {
266
- c.current && s && c.current.onExportScheduled(s);
267
- }, [s]), C(() => {
268
- if (!r || !c.current)
269
- return;
270
- const b = r.onCommandExecuted(() => {
271
- if (c.current) {
272
- if (!i) {
273
- d.current = !0;
274
- return;
275
- }
276
- c.current.scheduleExport();
277
- }
278
- });
279
- return () => {
280
- b();
281
- };
282
- }, [r, i]), C(() => {
283
- if (c.current) {
284
- if (!i) {
285
- d.current = !0;
286
- return;
287
- }
288
- c.current.scheduleExport();
289
- }
290
- }, [n, o, i]), C(() => Le((E) => {
291
- if (!c.current || !i) return;
292
- n.some((B) => B.id === E) && c.current.scheduleExport();
293
- }), [n, i]);
294
- const D = P((b) => {
295
- c.current && c.current.updateConfig(b);
296
- }, []), p = P(async () => {
297
- c.current && (await c.current.forceExport(), w.current = c.current.getStats(), R(w.current));
298
- }, []), F = P(() => {
299
- c.current && (c.current.resetStats(), w.current = c.current.getStats(), R(w.current));
300
- }, []), S = P(() => {
301
- c.current && c.current.resetChangeDetection();
302
- }, []);
303
- return {
304
- stats: T,
305
- updateConfig: D,
306
- forceExport: p,
307
- resetStats: F,
308
- resetChangeDetection: S
309
- };
310
- }
311
- const fe = rt("useContentReady");
312
- function Ar(t) {
313
- var r;
314
- const e = /* @__PURE__ */ new Set();
315
- for (const o of t)
316
- if ("fontFamily" in o && typeof o.fontFamily == "string" && o.fontFamily && e.add(o.fontFamily), "getRichText" in o && typeof o.getRichText == "function")
317
- try {
318
- const n = o.getRichText();
319
- if (n != null && n.spans)
320
- for (const a of n.spans)
321
- (r = a.style) != null && r.fontFamily && e.add(a.style.fontFamily);
322
- } catch {
323
- }
324
- return Array.from(e);
325
- }
326
- async function kr(t) {
327
- if (t.length === 0 || typeof document > "u" || !document.fonts) return;
328
- const e = [];
329
- for (const r of t) {
330
- const o = `16px "${r}"`;
331
- e.push(document.fonts.load(o));
332
- }
333
- await Promise.all(e), document.fonts.ready && await Promise.race([
334
- document.fonts.ready,
335
- new Promise((r) => setTimeout(r, 250))
336
- ]), fe.debug(`Awaited ${e.length} font(s)`);
337
- }
338
- function Sr() {
339
- return new Promise((t) => requestAnimationFrame(() => t()));
340
- }
341
- function Rr(t) {
342
- const { isCanvasReady: e, elements: r, hasInitialElements: o, initialElementsLoaded: n } = t, [a, s] = Z(!1), i = M(!1);
343
- return C(() => {
344
- if (a || !e || o && !n || o && r.length === 0 || i.current) return;
345
- i.current = !0;
346
- let c = !1;
347
- return (async () => {
348
- try {
349
- const d = Ar(r);
350
- if (d.length > 0 && (await kr(d), c) || (await Sr(), c)) return;
351
- s(!0);
352
- } catch (d) {
353
- fe.error("Error during content readiness check:", d), c || s(!0);
354
- } finally {
355
- i.current = !1;
356
- }
357
- })(), () => {
358
- c = !0, i.current = !1;
359
- };
360
- }, [e, r, o, n, a]), a;
361
- }
362
- const Cr = {
363
- // Vertical alignments (map to top/center/bottom center)
364
- "far-top": "t",
365
- top: "t",
366
- center: "c",
367
- bottom: "b",
368
- "far-bottom": "b",
369
- // Horizontal alignments (map to left/center/right middle)
370
- "far-left": "l",
371
- left: "l",
372
- right: "r",
373
- "far-right": "r",
374
- // Direct 9-point values
375
- tl: "tl",
376
- t: "t",
377
- tr: "tr",
378
- l: "l",
379
- c: "c",
380
- r: "r",
381
- bl: "bl",
382
- b: "b",
383
- br: "br"
384
- };
385
- function Ir(t) {
386
- return t && Cr[t] || "c";
387
- }
388
- function Mr(t, e) {
389
- const r = e.scaleMode || "cover", o = (r === "contain" ? e.marginTop : 0) || 0, n = (r === "contain" ? e.marginRight : 0) || 0, a = (r === "contain" ? e.marginBottom : 0) || 0, s = (r === "contain" ? e.marginLeft : 0) || 0, i = e.width - s - n, c = e.height - o - a, d = i / t.width, w = c / t.height, T = r === "contain" ? Math.min(d, w) : Math.max(d, w), R = e.scale || 1, k = T * R, D = t.width * k, p = t.height * k, F = e.align || "c";
390
- let S = 0, b = 0;
391
- const E = D - i, x = p - c;
392
- switch (F) {
393
- // Top row
394
- case "tl":
395
- S = 0, b = 0;
396
- break;
397
- case "t":
398
- S = -E / 2, b = 0;
399
- break;
400
- case "tr":
401
- S = -E, b = 0;
402
- break;
403
- // Middle row
404
- case "l":
405
- S = 0, b = -x / 2;
406
- break;
407
- case "c":
408
- // Center (default)
409
- default:
410
- S = -E / 2, b = -x / 2;
411
- break;
412
- case "r":
413
- S = -E, b = -x / 2;
414
- break;
415
- // Bottom row
416
- case "bl":
417
- S = 0, b = -x;
418
- break;
419
- case "b":
420
- S = -E / 2, b = -x;
421
- break;
422
- case "br":
423
- S = -E, b = -x;
424
- break;
425
- }
426
- S += s, b += o;
427
- const B = e.offsetX || 0, H = e.offsetY || 0, O = E, lt = x;
428
- return S += B * O, b += H * lt, S = Math.round(S), b = Math.round(b), {
429
- // Final scale to apply to artwork
430
- scale: k,
431
- // Final artwork dimensions after scaling
432
- width: Math.round(D),
433
- height: Math.round(p),
434
- // Position to draw artwork (relative to artboard origin)
435
- x: S,
436
- y: b,
437
- // Debug info
438
- debug: {
439
- coverScale: T,
440
- userScale: R,
441
- overflow: { x: E, y: x },
442
- alignment: F,
443
- appliedOffset: { x: B * O, y: H * lt }
444
- }
445
- };
446
- }
447
- const Tr = {
448
- timeout: 3e4,
449
- retries: 1,
450
- retryDelay: 1e3,
451
- validateUrl: !0
452
- };
453
- function Dr(t) {
454
- try {
455
- const e = new URL(t);
456
- return ["http:", "https:", "data:", "blob:"].includes(e.protocol);
457
- } catch {
458
- return !1;
459
- }
460
- }
461
- async function Nr(t, e = {}) {
462
- const r = { ...Tr, ...e };
463
- if (r.validateUrl && !Dr(t))
464
- return {
465
- success: !1,
466
- error: new Error(`Invalid image URL: ${t}`)
467
- };
468
- let o;
469
- for (let n = 0; n <= r.retries; n++)
470
- try {
471
- return await Fr(t, r.timeout);
472
- } catch (a) {
473
- o = a instanceof Error ? a : new Error(String(a)), n < r.retries && await $r(r.retryDelay);
474
- }
475
- return {
476
- success: !1,
477
- error: o || new Error("Failed to load image after retries")
478
- };
479
- }
480
- function Fr(t, e) {
481
- return new Promise((r) => {
482
- const o = new Image();
483
- o.crossOrigin = "anonymous";
484
- let n = null, a = !1;
485
- const s = () => {
486
- n && (clearTimeout(n), n = null);
487
- }, i = () => {
488
- if (a) return;
489
- a = !0, s();
490
- const d = o.naturalWidth || o.width, w = o.naturalHeight || o.height;
491
- r({
492
- success: !0,
493
- element: o,
494
- width: d,
495
- height: w,
496
- aspectRatio: w > 0 ? d / w : 1
497
- });
498
- }, c = (d) => {
499
- a || (a = !0, s(), r({
500
- success: !1,
501
- error: new Error(d)
502
- }));
503
- };
504
- o.onload = i, o.onerror = () => {
505
- c(`Failed to load image: ${t}`);
506
- }, n = setTimeout(() => {
507
- c(`Image load timed out after ${e}ms: ${t}`);
508
- }, e), o.src = t;
509
- });
510
- }
511
- async function Hr(t, e, r = {}, o = {}) {
512
- var R, k, D, p, F, S, b;
513
- const n = await Nr(t, o);
514
- if (!n.success || !n.width || !n.height)
515
- return {
516
- success: !1,
517
- error: n.error || new Error("Failed to get image dimensions")
518
- };
519
- const a = {
520
- width: e.artboard.width,
521
- height: e.artboard.height,
522
- scale: e.scale,
523
- align: Ir(e.alignment),
524
- offsetX: e.offsetX,
525
- offsetY: e.offsetY,
526
- scaleMode: e.scaleMode,
527
- marginTop: e.marginTop,
528
- marginRight: e.marginRight,
529
- marginBottom: e.marginBottom,
530
- marginLeft: e.marginLeft
531
- }, s = Mr(
532
- { width: n.width, height: n.height },
533
- a
534
- ), i = e.artboard.x + s.x, c = e.artboard.y + s.y, d = i + s.width / 2, w = c + s.height / 2;
535
- return {
536
- success: !0,
537
- element: new $t({
538
- ...r,
539
- x: d,
540
- y: w,
541
- imageUrl: t,
542
- imageAspectRatio: n.aspectRatio,
543
- preserveDimensions: !0,
544
- // Don't recalculate dimensions on image load
545
- transformData: {
546
- type: "image",
547
- width: s.width,
548
- height: s.height,
549
- cropX: ((R = r.transformData) == null ? void 0 : R.cropX) ?? 0,
550
- cropY: ((k = r.transformData) == null ? void 0 : k.cropY) ?? 0,
551
- cropWidth: ((D = r.transformData) == null ? void 0 : D.cropWidth) ?? 1,
552
- cropHeight: ((p = r.transformData) == null ? void 0 : p.cropHeight) ?? 1,
553
- flipHorizontal: ((F = r.transformData) == null ? void 0 : F.flipHorizontal) ?? !1,
554
- flipVertical: ((S = r.transformData) == null ? void 0 : S.flipVertical) ?? !1,
555
- borderRadius: ((b = r.transformData) == null ? void 0 : b.borderRadius) ?? 0
556
- }
557
- }),
558
- placement: {
559
- artworkSize: { width: n.width, height: n.height },
560
- finalSize: { width: s.width, height: s.height },
561
- position: { x: d, y: w },
562
- scale: s.scale
563
- }
564
- };
565
- }
566
- function $r(t) {
567
- return new Promise((e) => setTimeout(e, t));
568
- }
569
- const q = rt("SnowconeCanvas");
570
- function Nt(t, e, r, o) {
571
- if (o <= 0 || !isFinite(o))
572
- return r;
573
- const n = t * r, a = e * r;
574
- if (Math.max(n, a) <= o)
575
- return r;
576
- const i = Math.max(t, e), c = o / i;
577
- return Math.min(c, r);
578
- }
579
- const he = le((t, e) => {
580
- const {
581
- exportConfig: r,
582
- imageConfig: o,
583
- layoutConfig: n,
584
- kit: a,
585
- artboards: s,
586
- activeArtboard: i,
587
- onArtboardChange: c,
588
- initialElements: d,
589
- onChange: w,
590
- onSelectionChange: T,
591
- autoExportInterval: R,
592
- onExport: k,
593
- onExportScheduled: D,
594
- onExportStatus: p,
595
- onExportReady: F,
596
- onImageLoad: S,
597
- onImageError: b,
598
- onError: E,
599
- onReady: x,
600
- className: B,
601
- style: H,
602
- enableShortcuts: O,
603
- overlay: lt
604
- } = t, Wt = R, nt = t.autoExportConfig ?? (r == null ? void 0 : r.autoExportConfig), _ = t.autoExportFormat ?? (r == null ? void 0 : r.format) ?? "dataUrl", Et = t.autoExportAll ?? (r == null ? void 0 : r.exportAll) ?? !1, K = t.exportScale ?? (r == null ? void 0 : r.scale) ?? 2, U = t.maxExportSize ?? (r == null ? void 0 : r.maxSize) ?? 4e3, dt = t.exportImageFormat ?? (r == null ? void 0 : r.imageFormat) ?? "png", ut = t.exportImageQuality ?? (r == null ? void 0 : r.imageQuality) ?? 0.92, z = t.initialImage ?? (o == null ? void 0 : o.src), Ut = t.initialImageAlignment ?? (o == null ? void 0 : o.alignment) ?? "center", zt = t.initialImageScale ?? (o == null ? void 0 : o.scale) ?? 1, Lt = t.initialImageScaleMode ?? (o == null ? void 0 : o.scaleMode) ?? "cover", ye = t.width ?? (n == null ? void 0 : n.width) ?? 1200, xe = t.height ?? (n == null ? void 0 : n.height) ?? 1200, ve = t.viewPadding ?? (n == null ? void 0 : n.viewPadding) ?? 0.9, we = t.artboardBorderRadius ?? (n == null ? void 0 : n.artboardBorderRadius) ?? 0, Ee = t.fixedMargin ?? (n == null ? void 0 : n.fixedMargin), Ae = t.maxHeight ?? (n == null ? void 0 : n.maxHeight), ke = t.showRotationHandle ?? (n == null ? void 0 : n.showRotationHandle) ?? !0, Se = t.hideCanvas ?? (n == null ? void 0 : n.hideCanvas) ?? !1, Ot = t.canvasWrapperClassName ?? (n == null ? void 0 : n.canvasWrapperClassName), Yt = t.canvasWrapperStyle ?? (n == null ? void 0 : n.canvasWrapperStyle), Re = t.canvasCutouts ?? (n == null ? void 0 : n.canvasCutouts), Xt = t.pieceGuides, Ce = t.pieceFocus, Ie = O ?? !0, ot = ct(() => or(a ?? "pro-studio"), [a]);
605
- C(() => {
606
- var l;
607
- if (typeof j < "u" && ((l = j.env) == null ? void 0 : l.NODE_ENV) === "development" && ot) {
608
- const u = ar(ot);
609
- u.valid || q.warn("Kit validation warnings:", u.errors), u.warnings.length > 0 && q.warn("Kit validation warnings:", u.warnings);
610
- }
611
- }, [ot]);
612
- const ft = M(E);
613
- ft.current = E;
614
- const W = P((l) => {
615
- var u;
616
- q.error(`[${l.category}] ${l.message}`, l.originalError), (u = ft.current) == null || u.call(ft, l);
617
- }, []);
618
- C(() => (Mt.onRenderError = W, () => {
619
- Mt.onRenderError === W && (Mt.onRenderError = null);
620
- }), [W]);
621
- const {
622
- elements: G,
623
- setElements: ht,
624
- selectedId: Vt,
625
- artboardManager: I,
626
- refreshArtboards: Me,
627
- historyManager: Te,
628
- isCanvasReady: At,
629
- setCanvasReady: mt
630
- } = de(), { createArtboard: De, selectArtboard: Ne, artboards: $ } = sr(), { exportArtboard: tt, exportAllArtboards: gt, exportArtboardAsBlob: et, exportAllArtboardsAsBlobs: pt } = ir();
631
- Ye(e, () => ({
632
- exportArtboards: async (l = {}) => {
633
- const u = l.format || _, A = l.scale || K, f = l.all ?? !1;
634
- let m = A;
635
- if (U > 0 && isFinite(U))
636
- for (const h of $) {
637
- const y = Nt(h.width, h.height, A, U);
638
- m = Math.min(m, y);
639
- }
640
- const g = { scale: m, format: dt, quality: ut };
641
- if (f) {
642
- const h = u === "blob" ? await pt(g) : await gt(g), y = {};
643
- for (const [N, L] of Object.entries(h)) {
644
- const Y = $.find((at) => at.id === N);
645
- Y && (y[Y.name] = L);
646
- }
647
- return y;
648
- } else {
649
- const h = I.getActiveArtboard();
650
- if (!h)
651
- throw new Error("[SnowconeCanvas] No active artboard found");
652
- if (u === "blob") {
653
- const y = await et(h.id, g);
654
- return { [h.name]: y };
655
- } else {
656
- const y = await tt(h.id, g);
657
- return { [h.name]: y };
658
- }
659
- }
660
- }
661
- }), [tt, gt, et, pt, _, K, U, dt, ut, I, $]), C(() => {
662
- F && F(async () => {
663
- const u = I.getActiveArtboard();
664
- if (!u)
665
- throw new Error("[SnowconeCanvas] No active artboard found");
666
- const A = Nt(
667
- u.width,
668
- u.height,
669
- K,
670
- U
671
- ), f = _ === "blob" ? await et(u.id, { scale: A }) : await tt(u.id, { scale: A });
672
- return { [u.name]: f };
673
- });
674
- }, [F, tt, et, I, _, K, U]);
675
- const [jt, bt] = Z("idle"), [Kt, kt] = Z(null), St = M(void 0), Rt = M(!1), Gt = M(!1), [Fe, He] = Z(!1), $e = M(0), Be = M(0), Ct = M(!1);
676
- C(() => {
677
- if (Rt.current) return;
678
- Rt.current = !0;
679
- const l = s || [{ name: "Design", width: ye, height: xe }], u = I.getAllArtboards();
680
- if (u.length > 0) {
681
- const A = l[0], f = u[0], m = Xt ? "transparent" : void 0, g = (h) => h.exportBackground ?? (!!h.backgroundColor && h.backgroundColor !== "transparent");
682
- f.name = A.name, f.width = A.width, f.height = A.height, f.clipShape = A.clipShape, f.exportBackground = g(A), A.backgroundColor ? f.backgroundColor = A.backgroundColor : m && (f.backgroundColor = m);
683
- for (let h = 1; h < l.length; h++)
684
- De(l[h].width, l[h].height, {
685
- name: l[h].name,
686
- backgroundColor: l[h].backgroundColor ?? m,
687
- exportBackground: g(l[h]),
688
- clipShape: l[h].clipShape
689
- });
690
- !i && l.length > 1 && I.setActiveArtboard(f.id);
691
- }
692
- Me();
693
- }, []), C(() => {
694
- if (!(!d || d.length === 0) && !Gt.current && Rt.current) {
695
- Gt.current = !0, He(!0);
696
- try {
697
- const l = fr.createManyFromJSON(d), u = I.getAllArtboards();
698
- if (u.length > 0) {
699
- const f = new Map(u.map((m) => [m.name, m]));
700
- l.forEach((m, g) => {
701
- var N;
702
- const h = (N = d[g]) == null ? void 0 : N.artboard, y = h && f.get(h) || u[0];
703
- I.addElementToArtboard(m.id, y.id);
704
- });
705
- }
706
- ht(l);
707
- const A = /* @__PURE__ */ new Set();
708
- for (const f of l) {
709
- const m = f.fontFamily;
710
- typeof m == "string" && m.trim().length > 0 && A.add(m);
711
- }
712
- if (A.size > 0 && typeof document < "u") {
713
- const f = document.fonts ?? null, m = Array.from(A).map((g) => {
714
- const h = `font-${g.replace(/\s+/g, "-").toLowerCase()}`;
715
- let y = document.getElementById(h);
716
- return y || (y = document.createElement("link"), y.id = h, y.rel = "stylesheet", y.href = `https://fonts.googleapis.com/css2?family=${g.replace(
717
- /\s+/g,
718
- "+"
719
- )}:wght@400;700&display=swap`, document.head.appendChild(y)), new Promise((N) => {
720
- if (y.sheet) {
721
- N(g);
722
- return;
723
- }
724
- const L = () => N(g);
725
- y.addEventListener("load", L, { once: !0 }), y.addEventListener("error", L, { once: !0 }), setTimeout(L, 4e3);
726
- });
727
- });
728
- Promise.all(m).then((g) => {
729
- if (f)
730
- return Promise.all(
731
- g.flatMap((h) => [
732
- f.load(`400 16px "${h}"`).catch(() => {
733
- }),
734
- f.load(`700 16px "${h}"`).catch(() => {
735
- })
736
- ])
737
- );
738
- }).then(() => {
739
- ht((g) => g.slice());
740
- });
741
- }
742
- } catch (l) {
743
- q.error("Failed to load initial elements:", l);
744
- const u = l instanceof Error ? l : new Error(String(l));
745
- W({
746
- category: "import",
747
- message: `Failed to load initial elements: ${u.message}`,
748
- originalError: u,
749
- recoverable: !0
750
- });
751
- }
752
- }
753
- }, [d, I, ht, W]), C(() => {
754
- if (!i) {
755
- Ct.current = !0;
756
- return;
757
- }
758
- const l = $.find((u) => u.name === i);
759
- if (l) {
760
- const u = I.getActiveArtboard();
761
- (u == null ? void 0 : u.name) !== i && Ne(l.id), Ct.current = !0;
762
- }
763
- }, [i, $]), C(() => {
764
- if (!z || St.current === z) return;
765
- if (G.length > 0) {
766
- St.current = z;
767
- return;
768
- }
769
- St.current = z, (async () => {
770
- bt("loading"), kt(null);
771
- try {
772
- const u = I.getAllArtboards();
773
- if (u.length === 0)
774
- throw new Error("No artboards available");
775
- const A = [];
776
- for (const f of u) {
777
- const m = s == null ? void 0 : s.find((y) => y.name === f.name), g = (m == null ? void 0 : m.scaleMode) || Lt, h = await Hr(
778
- z,
779
- {
780
- artboard: {
781
- width: f.width,
782
- height: f.height,
783
- x: f.x,
784
- y: f.y
785
- },
786
- alignment: (m == null ? void 0 : m.fitAlign) || Ut,
787
- scale: zt,
788
- scaleMode: g,
789
- marginTop: m == null ? void 0 : m.fitMarginTop,
790
- marginRight: m == null ? void 0 : m.fitMarginRight,
791
- marginBottom: m == null ? void 0 : m.fitMarginBottom,
792
- marginLeft: m == null ? void 0 : m.fitMarginLeft
793
- }
794
- );
795
- h.success && h.element ? (I.addElementToArtboard(h.element.id, f.id), A.push(h.element)) : q.error("Failed to load image for artboard:", f.name, h.error);
796
- }
797
- if (A.length > 0)
798
- ht((f) => [...f, ...A]), bt("success"), S == null || S(z);
799
- else {
800
- const f = new Error("Failed to load image into any artboard");
801
- bt("error"), kt(f), b == null || b(z, f), W({
802
- category: "image",
803
- message: f.message,
804
- originalError: f,
805
- recoverable: !0
806
- });
807
- }
808
- } catch (u) {
809
- const A = u instanceof Error ? u : new Error(String(u));
810
- bt("error"), kt(A), b == null || b(z, A), W({
811
- category: "image",
812
- message: A.message,
813
- originalError: A,
814
- recoverable: !0
815
- });
816
- }
817
- })();
818
- }, [z, Ut, zt, Lt]);
819
- const yt = M(T);
820
- yt.current = T, C(() => {
821
- var l;
822
- (l = yt.current) == null || l.call(yt, Vt);
823
- }, [Vt]);
824
- const qt = M(w);
825
- qt.current = w, C(() => {
826
- const l = qt.current;
827
- if (!l) return;
828
- const u = I.getActiveArtboard(), A = {
829
- // Stamp each element with the NAME of the artboard it lives on (the
830
- // manager is the membership source of truth). Without this the public
831
- // state is flat and a multi-placement design cannot round-trip: restore
832
- // collapses everything onto the first artboard, and per-placement render
833
- // states can't be derived from onChange. Pinned by
834
- // SnowconeCanvas.artboard-membership.test.tsx.
835
- elements: G.map((f) => {
836
- const m = f.toJSON(), g = I.getArtboardForElement(f.id);
837
- return g ? { ...m, artboard: g.name } : m;
838
- }),
839
- artboards: $.map((f) => ({
840
- name: f.name,
841
- width: f.width,
842
- height: f.height,
843
- clipShape: f.clipShape,
844
- backgroundColor: f.backgroundColor,
845
- // Carried so serializeStateForServer / saved states keep an
846
- // explicitly-set background renderable (mockup matches editor).
847
- exportBackground: f.exportBackground
848
- })),
849
- activeArtboard: (u == null ? void 0 : u.name) || "Design"
850
- };
851
- l(A);
852
- }, [G, $]), C(() => {
853
- if (!c || !Ct.current) return;
854
- const l = I.getActiveArtboard();
855
- l && c(l.name);
856
- }, [I.getActiveArtboardId()]);
857
- const Pe = P(async () => {
858
- if (!k && !p) return;
859
- const l = ++Be.current, u = Date.now();
860
- let A = K;
861
- if (U > 0 && isFinite(U))
862
- for (const g of $) {
863
- const h = Nt(g.width, g.height, K, U);
864
- A = Math.min(A, h);
865
- }
866
- const f = I.getActiveArtboard(), m = (f == null ? void 0 : f.id) ?? "unknown";
867
- p == null || p({ status: "rendering", artboardId: m }), $e.current = u;
868
- try {
869
- let g;
870
- const h = {
871
- format: dt,
872
- scale: A,
873
- quality: ut
874
- };
875
- if (_ === "blob")
876
- if (Et) {
877
- const y = await pt(h);
878
- g = {};
879
- for (const [N, L] of Object.entries(y)) {
880
- const Y = $.find((at) => at.id === N);
881
- Y && (g[Y.name] = L);
882
- }
883
- } else {
884
- const y = I.getActiveArtboard();
885
- if (!y) return;
886
- const N = await et(y.id, h);
887
- g = { [y.name]: N };
888
- }
889
- else if (Et) {
890
- const y = await gt(h);
891
- g = {};
892
- for (const [N, L] of Object.entries(y)) {
893
- const Y = $.find((at) => at.id === N);
894
- Y && (g[Y.name] = L);
895
- }
896
- } else {
897
- const y = I.getActiveArtboard();
898
- if (!y) return;
899
- const N = await tt(y.id, h);
900
- g = { [y.name]: N };
901
- }
902
- k == null || k(g), p == null || p({ status: "complete", artboardId: m, result: g });
903
- } catch (g) {
904
- const h = g instanceof Error ? g.message : String(g);
905
- if (h.includes("Canvas ref not available"))
906
- return;
907
- const y = Date.now() - u;
908
- q.error("Export failed (export #" + l + "):", {
909
- duration: `${y}ms`,
910
- error: h
911
- });
912
- const N = g instanceof Error ? g : new Error(h);
913
- p == null || p({ status: "error", artboardId: m, error: N }), W({
914
- category: "export",
915
- message: N.message,
916
- originalError: N,
917
- artboardId: m,
918
- recoverable: !0
919
- });
920
- }
921
- }, [k, p, Et, _, K, U, dt, ut, tt, gt, et, pt, $, I, G, nt, W]);
922
- C(() => {
923
- Wt !== void 0 && Wt > 0 && q.warn(
924
- "autoExportInterval is deprecated and ignored. Use autoExportConfig={{ enabled: true, debounceMs: 100, maxWaitMs: 1000 }} instead."
925
- );
926
- }, []), C(() => {
927
- }, [G]);
928
- const We = P(() => {
929
- if (D == null || D(), p) {
930
- const l = I.getActiveArtboard();
931
- p({ status: "scheduled", artboardId: (l == null ? void 0 : l.id) ?? "unknown" });
932
- }
933
- }, [D, p, I]), Ue = !!d && d.length > 0, It = Rr({
934
- isCanvasReady: At,
935
- elements: G,
936
- hasInitialElements: Ue,
937
- initialElementsLoaded: Fe
938
- }), Jt = M(!1);
939
- C(() => {
940
- It && !Jt.current && (Jt.current = !0, x == null || x());
941
- }, [It, x]), Er({
942
- config: nt ? {
943
- enabled: nt.enabled ?? !0,
944
- debounceMs: nt.debounceMs ?? 100,
945
- maxWaitMs: nt.maxWaitMs ?? 1e3
946
- } : {
947
- enabled: !1
948
- // Only use smart export if autoExportConfig is provided
949
- },
950
- historyManager: Te,
951
- artboards: $,
952
- elements: G,
953
- onExport: Pe,
954
- onExportScheduled: We,
955
- isCanvasReady: It
956
- // Gate exports until content is fully ready (fonts loaded, elements deserialized)
957
- }), C(() => {
958
- if ($.length > 0 && !At) {
959
- const l = requestAnimationFrame(() => {
960
- mt(!0);
961
- });
962
- return () => cancelAnimationFrame(l);
963
- }
964
- }, [$.length, At, mt]), C(() => () => {
965
- mt(!1);
966
- }, [mt]);
967
- const ze = P((l) => {
968
- W({
969
- category: "unknown",
970
- message: l.message,
971
- originalError: l,
972
- recoverable: !1
973
- });
974
- }, [W]);
975
- return jt === "loading" ? /* @__PURE__ */ v(
976
- "div",
977
- {
978
- className: B,
979
- style: {
980
- ...H,
981
- display: "flex",
982
- alignItems: "center",
983
- justifyContent: "center",
984
- backgroundColor: "var(--surface, #f5f5f5)"
985
- },
986
- children: /* @__PURE__ */ v(cr, { size: 48 })
987
- }
988
- ) : jt === "error" && Kt ? /* @__PURE__ */ v(
989
- "div",
990
- {
991
- className: B,
992
- style: {
993
- ...H,
994
- display: "flex",
995
- alignItems: "center",
996
- justifyContent: "center",
997
- backgroundColor: "var(--surface, #f5f5f5)",
998
- color: "var(--danger, #dc2626)",
999
- padding: "1rem"
1000
- },
1001
- children: /* @__PURE__ */ Q("div", { className: "text-center", children: [
1002
- /* @__PURE__ */ v("div", { className: "font-medium", children: "Failed to load image" }),
1003
- /* @__PURE__ */ v("div", { className: "text-sm mt-1 opacity-70", children: Kt.message })
1004
- ] })
1005
- }
1006
- ) : /* @__PURE__ */ v(lr, { kit: ot, children: /* @__PURE__ */ v("div", { className: `app-modern ${B || ""}`, style: { ...H, position: "relative" }, children: /* @__PURE__ */ Q(
1007
- vr,
1008
- {
1009
- onError: ze,
1010
- fallback: (l, u) => /* @__PURE__ */ Q(
1011
- "div",
1012
- {
1013
- style: {
1014
- display: "flex",
1015
- flexDirection: "column",
1016
- alignItems: "center",
1017
- justifyContent: "center",
1018
- padding: "24px",
1019
- minHeight: "200px",
1020
- backgroundColor: "var(--surface, #f5f5f5)",
1021
- color: "var(--foreground, #333)",
1022
- fontFamily: "system-ui, sans-serif",
1023
- textAlign: "center"
1024
- },
1025
- children: [
1026
- /* @__PURE__ */ v("div", { style: { fontSize: "18px", fontWeight: 600, marginBottom: "8px" }, children: "Something went wrong" }),
1027
- /* @__PURE__ */ v("div", { style: { fontSize: "14px", opacity: 0.7, marginBottom: "16px", maxWidth: "400px" }, children: l.message }),
1028
- /* @__PURE__ */ v(
1029
- "button",
1030
- {
1031
- onClick: u,
1032
- style: {
1033
- padding: "8px 20px",
1034
- backgroundColor: "var(--primary, #333)",
1035
- color: "var(--primary-foreground, #fff)",
1036
- border: "none",
1037
- borderRadius: "6px",
1038
- fontSize: "14px",
1039
- fontWeight: 500,
1040
- cursor: "pointer"
1041
- },
1042
- children: "Try again"
1043
- }
1044
- )
1045
- ]
1046
- }
1047
- ),
1048
- children: [
1049
- !Se && /* @__PURE__ */ (() => {
1050
- const l = /* @__PURE__ */ v(
1051
- ur,
1052
- {
1053
- style: { width: "100%" },
1054
- fitPadding: ve,
1055
- artboardBorderRadius: we,
1056
- fixedMargin: Ee,
1057
- maxHeight: Ae,
1058
- showRotationHandle: ke,
1059
- enableShortcuts: Ie,
1060
- canvasCutouts: Re,
1061
- pieceGuides: Xt,
1062
- pieceFocus: Ce
1063
- }
1064
- );
1065
- return /* @__PURE__ */ v(yr, { kit: ot, canvas: Ot || Yt ? /* @__PURE__ */ v("div", { className: Ot, style: Yt, children: l }) : l });
1066
- })(),
1067
- lt
1068
- ]
1069
- }
1070
- ) }) });
1071
- });
1072
- he.displayName = "SnowconeCanvasInner";
1073
- const Br = le((t, e) => {
1074
- var c;
1075
- const { inheritTheme: r, externalProvider: o, services: n } = t, a = t.viewPadding ?? ((c = t.layoutConfig) == null ? void 0 : c.viewPadding), s = /* @__PURE__ */ v(he, { ref: e, ...t }), i = o ? /* @__PURE__ */ v(Qt, { defaultTheme: "light", passive: r, children: s }) : /* @__PURE__ */ v(Qt, { defaultTheme: "light", passive: r, children: /* @__PURE__ */ v(Xe, { viewPadding: a, children: s }) });
1076
- return n ? /* @__PURE__ */ v(dr, { services: n, children: i }) : i;
1077
- });
1078
- Br.displayName = "SnowconeCanvas";
1079
- const Pr = 3e3, oe = {
1080
- text: "useTextBinding",
1081
- image: "useImageBinding"
1082
- }, Wr = {
1083
- text: "setText",
1084
- image: "setImageUrl"
1085
- }, Ft = /* @__PURE__ */ new Set();
1086
- function ae(t) {
1087
- return t instanceof ie ? "text" : t instanceof $t ? "image" : "other";
1088
- }
1089
- function Ur(t, e, r) {
1090
- const o = `[${oe[t]}]`, n = `the binding is dead (isConnected: false, ${Wr[t]} is a no-op)`, a = r.getAllByName(e);
1091
- if (a.length > 0) {
1092
- const c = [...new Set(a.map(ae))], d = t === "text" ? "image" : "text", w = c.length === 1 && c[0] === d ? ` Bind it with ${oe[d]}("${e}") instead.` : "";
1093
- return `${o} the layer named "${e}" is ${c.join("/")}, not ${t} — ${n}.${w}`;
1094
- }
1095
- const s = r.getAll().filter((c) => c.name).map((c) => `"${c.name}" (${ae(c)})`), i = 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().${t}(…, { name: '${e}' }) or the element's \`name\` property.`;
1096
- return `${o} no ${t} layer named "${e}" exists — ${n}. ${i}`;
1097
- }
1098
- function me(t, e, r, o) {
1099
- C(() => {
1100
- var s;
1101
- if (o || typeof j > "u" || ((s = j.env) == null ? void 0 : s.NODE_ENV) === "production") return;
1102
- const n = `${t}:${e}`;
1103
- if (Ft.has(n)) return;
1104
- const a = setTimeout(() => {
1105
- Ft.has(n) || (Ft.add(n), console.warn(Ur(t, e, r)));
1106
- }, Pr);
1107
- return () => clearTimeout(a);
1108
- }, [t, e, r, o]);
1109
- }
1110
- const zr = () => {
1111
- }, Lr = [];
1112
- function ln(t) {
1113
- const { elementStore: e } = wt(), { executeElementUpdate: r } = Bt(), o = ct(() => e.getAllByName(t).filter((s) => s instanceof ie), [e, t]), n = P(
1114
- (a) => {
1115
- for (const s of o) {
1116
- const i = s.clone();
1117
- i.setText(a), r(s, i);
1118
- }
1119
- },
1120
- [o, r]
1121
- );
1122
- return me("text", t, e, o.length > 0), o.length === 0 ? { text: "", setText: zr, element: null, elements: Lr, isConnected: !1 } : {
1123
- text: o[0].getText(),
1124
- setText: n,
1125
- element: o[0],
1126
- elements: o,
1127
- isConnected: !0
1128
- };
1129
- }
1130
- const Or = () => {
1131
- }, Yr = [];
1132
- function Xr(t, e, r) {
1133
- const o = t / e;
1134
- if (r >= o) {
1135
- const n = e, a = n * r, s = t / a;
1136
- return {
1137
- width: a,
1138
- height: n,
1139
- cropX: (1 - s) / 2,
1140
- cropY: 0,
1141
- cropWidth: s,
1142
- cropHeight: 1
1143
- };
1144
- } else {
1145
- const n = t, a = n / r, s = e / a;
1146
- return {
1147
- width: n,
1148
- height: a,
1149
- cropX: 0,
1150
- cropY: (1 - s) / 2,
1151
- cropWidth: 1,
1152
- cropHeight: s
1153
- };
1154
- }
1155
- }
1156
- function dn(t, e) {
1157
- const r = (e == null ? void 0 : e.fit) ?? "cover", { elementStore: o, setElements: n } = wt(), { executeElementUpdate: a } = Bt(), s = ct(() => o.getAllByName(t).filter((k) => k instanceof $t), [o, t]), i = M(/* @__PURE__ */ new Map());
1158
- for (const R of s)
1159
- if (!i.current.has(R.id)) {
1160
- const k = R.transformData;
1161
- i.current.set(R.id, {
1162
- width: k.width * k.cropWidth,
1163
- height: k.height * k.cropHeight
1164
- });
1165
- }
1166
- const c = M(s);
1167
- c.current = s;
1168
- const d = M(a);
1169
- d.current = a;
1170
- const w = M(n);
1171
- w.current = n;
1172
- const T = P(
1173
- (R) => {
1174
- const k = c.current, D = d.current;
1175
- for (const p of k) {
1176
- const F = i.current.get(p.id);
1177
- if (!F) continue;
1178
- const { width: S, height: b } = F, E = p.clone();
1179
- E.imageLoaded = !1, E.imageElement = null, E.isCropping = !1, E.imageUrl = R, r === "cover" ? (E.preserveDimensions = !0, E.onLoadCallback = (x) => {
1180
- const B = x.imageAspectRatio || 1, H = Xr(S, b, B);
1181
- x.transformData.width = H.width, x.transformData.height = H.height, x.transformData.cropX = H.cropX, x.transformData.cropY = H.cropY, x.transformData.cropWidth = H.cropWidth, x.transformData.cropHeight = H.cropHeight, D(p, x);
1182
- }) : (E.preserveDimensions = !1, E.onLoadCallback = (x) => {
1183
- const B = x.transformData.width, H = x.transformData.height, O = Math.min(S / B, b / H, 1);
1184
- O < 1 && (x.transformData.width = B * O, x.transformData.height = H * O), x.transformData.cropX = 0, x.transformData.cropY = 0, x.transformData.cropWidth = 1, x.transformData.cropHeight = 1, D(p, x);
1185
- }), E.loadImage(R);
1186
- }
1187
- },
1188
- [r]
1189
- // Stable deps only — imageElements/executeElementUpdate/setElements accessed via refs
1190
- );
1191
- return me("image", t, o, s.length > 0), s.length === 0 ? { imageUrl: "", setImageUrl: Or, element: null, elements: Yr, isConnected: !1 } : {
1192
- imageUrl: s[0].imageUrl,
1193
- setImageUrl: T,
1194
- element: s[0],
1195
- elements: s,
1196
- isConnected: !0
1197
- };
1198
- }
1199
- const Vr = [];
1200
- function vt(t) {
1201
- let e = t.trim().toUpperCase();
1202
- return e.length === 4 && e.startsWith("#") && (e = "#" + e[1] + e[1] + e[2] + e[2] + e[3] + e[3]), e;
1203
- }
1204
- function V(t, e) {
1205
- return typeof t == "string" && vt(t) === e;
1206
- }
1207
- function ge(t, e) {
1208
- var o, n, a;
1209
- const r = t;
1210
- return V(r.color, e) || V((o = r.transformData) == null ? void 0 : o.fillColor, e) || V((n = r.transformData) == null ? void 0 : n.strokeColor, e) || ((a = r.stroke) == null ? void 0 : a.enabled) === !0 && V(r.stroke.color, e);
1211
- }
1212
- function jr(t, e, r) {
1213
- var s, i, c;
1214
- if (!ge(t, e)) return null;
1215
- const o = t.clone(), n = t, a = o;
1216
- return V(n.color, e) && (typeof a.setColor == "function" ? a.setColor(r) : a.color = r), V((s = n.transformData) == null ? void 0 : s.fillColor, e) && (a.transformData = { ...a.transformData, fillColor: r }), V((i = n.transformData) == null ? void 0 : i.strokeColor, e) && (a.transformData = { ...a.transformData, strokeColor: r }), ((c = n.stroke) == null ? void 0 : c.enabled) === !0 && V(n.stroke.color, e) && (a.stroke = { ...a.stroke, color: r }), o;
1217
- }
1218
- function un(t) {
1219
- const { elementStore: e } = wt(), { executeElementUpdate: r } = Bt(), [o, n] = Z(() => vt(t)), [a, s] = Z(t);
1220
- t !== a && (s(t), n(vt(t)));
1221
- const i = ct(
1222
- () => e.getAll().filter((d) => ge(d, o)),
1223
- [e, o]
1224
- ), c = P(
1225
- (d) => {
1226
- const w = vt(d);
1227
- if (w !== o) {
1228
- for (const T of e.getAll()) {
1229
- const R = jr(T, o, d);
1230
- R && r(T, R);
1231
- }
1232
- n(w);
1233
- }
1234
- },
1235
- [e, o, r]
1236
- );
1237
- return i.length === 0 ? { color: o, setColor: c, elements: Vr, isConnected: !1 } : { color: o, setColor: c, elements: i, isConnected: !0 };
1238
- }
1239
- function fn(t) {
1240
- const { elementStore: e } = wt();
1241
- return t ? e.getByName(t) ?? null : null;
1242
- }
1243
- const Kr = 1;
1244
- function J(t) {
1245
- return typeof t == "number" && Number.isFinite(t) ? Math.round(t * 100) / 100 : t;
1246
- }
1247
- function pe(t) {
1248
- return {
1249
- ...t,
1250
- ...t.x !== void 0 && { x: J(t.x) },
1251
- ...t.y !== void 0 && { y: J(t.y) },
1252
- ...t.width !== void 0 && { width: J(t.width) },
1253
- ...t.height !== void 0 && { height: J(t.height) },
1254
- ...t.rotation !== void 0 && { rotation: J(t.rotation) }
1255
- };
1256
- }
1257
- function Gr(t) {
1258
- var o;
1259
- const e = (o = t.artboards) != null && o.length ? t.artboards : [{ name: "Front", width: 800, height: 800 }], r = e.map(() => []);
1260
- for (const n of t.elements || []) {
1261
- const a = n == null ? void 0 : n.artboard, s = typeof a == "string" ? e.findIndex((i) => (i.name || "Front") === a) : -1;
1262
- r[s >= 0 ? s : 0].push(qr(n));
1263
- }
1264
- return {
1265
- schemaVersion: Kr,
1266
- artboards: e.map((n, a) => ({
1267
- id: `artboard-${a + 1}`,
1268
- name: n.name || "Front",
1269
- x: 0,
1270
- y: 0,
1271
- width: J(n.width || 800),
1272
- height: J(n.height || 800),
1273
- backgroundColor: n.backgroundColor || "transparent",
1274
- // Pass the artboard's flag through — the server worker only paints the
1275
- // background when this is true. Hardcoding `false` here silently dropped
1276
- // explicitly-set artboard backgrounds from every realtime mockup while
1277
- // the editor kept showing them (WYSIWYG drift).
1278
- exportBackground: n.exportBackground ?? !1,
1279
- elements: r[a],
1280
- distressTexture: n.distressTexture,
1281
- imageMask: n.imageMask
1282
- }))
1283
- };
1284
- }
1285
- function qr(t) {
1286
- const e = t.type || t.transformType;
1287
- if (e === "image")
1288
- return Jr(t);
1289
- const { artboard: r, ...o } = t;
1290
- return pe({
1291
- ...o,
1292
- type: e
1293
- });
1294
- }
1295
- function Jr(t) {
1296
- var d;
1297
- const e = t.transformData || {}, r = t.masks && t.masks.length > 0, o = e.width || 0, n = e.height || 0, a = r ? o : o * (e.cropWidth ?? 1), s = r ? n : n * (e.cropHeight ?? 1), i = {
1298
- id: t.id,
1299
- type: "image",
1300
- x: t.x || 0,
1301
- y: t.y || 0,
1302
- width: a,
1303
- height: s,
1304
- rotation: t.rotation || 0,
1305
- imageUrl: t.imageUrl,
1306
- imageAspectRatio: t.imageAspectRatio
1307
- };
1308
- return !r && (e.cropX !== 0 || e.cropY !== 0 || e.cropWidth !== 1 || e.cropHeight !== 1) && (i.cropX = e.cropX, i.cropY = e.cropY, i.cropWidth = e.cropWidth, i.cropHeight = e.cropHeight, i.needsCropPixelConversion = !0), e.flipHorizontal && (i.flipHorizontal = e.flipHorizontal), e.flipVertical && (i.flipVertical = e.flipVertical), e.borderRadius && (i.borderRadius = e.borderRadius), t.opacity !== void 0 && (i.opacity = t.opacity), t.distressEffect && (i.distressEffect = t.distressEffect), ((d = t.masks) == null ? void 0 : d.length) > 0 && (i.masks = t.masks), t.blendMode && (i.blendMode = t.blendMode), t.knockoutParts && (i.knockoutParts = t.knockoutParts), t.stroke && (i.stroke = t.stroke), pe(i);
1309
- }
1310
- const se = "center";
1311
- function Qr(t) {
1312
- return t === "top-left" || t === "left" || t === "bottom-left" ? "left" : t === "top-right" || t === "right" || t === "bottom-right" ? "right" : "center";
1313
- }
1314
- function Zr(t) {
1315
- return t === "top-left" || t === "top" || t === "top-right" ? "top" : t === "bottom-left" || t === "bottom" || t === "bottom-right" ? "bottom" : "middle";
1316
- }
1317
- function Ht(t, e, r, o, n, a) {
1318
- const s = Qr(t), i = Zr(t), c = s === "left" ? e / 2 + a : s === "right" ? o - e / 2 - a : o / 2, d = i === "top" ? r / 2 + a : i === "bottom" ? n - r / 2 - a : n / 2;
1319
- return { x: c, y: d };
1320
- }
1321
- function _r(t, e, r) {
1322
- const o = e / r;
1323
- if (t >= o) {
1324
- const s = r * t, i = e / s;
1325
- return {
1326
- width: s,
1327
- height: r,
1328
- cropX: (1 - i) / 2,
1329
- cropY: 0,
1330
- cropWidth: i,
1331
- cropHeight: 1
1332
- };
1333
- }
1334
- const n = e / t, a = r / n;
1335
- return {
1336
- width: e,
1337
- height: n,
1338
- cropX: 0,
1339
- cropY: (1 - a) / 2,
1340
- cropWidth: 1,
1341
- cropHeight: a
1342
- };
1343
- }
1344
- class Pt {
1345
- constructor(e) {
1346
- if (this.els = [], this.idCounter = 0, !(e.width > 0) || !(e.height > 0))
1347
- throw new Error("design(): artboard width and height must be positive numbers");
1348
- this.artboard = {
1349
- name: e.name || "Front",
1350
- width: e.width,
1351
- height: e.height,
1352
- backgroundColor: e.backgroundColor
1353
- };
1354
- }
1355
- /**
1356
- * Seed a builder from a previously saved canvas state — the editor shape
1357
- * `<SnowconeCanvas onChange>` emits (and `toJSON()` produces).
1358
- *
1359
- * This is how a saved editable template re-enters the turnkey embed path:
1360
- * load the template's state JSON, lift it back into a `DesignBuilder`, and
1361
- * hand it to `<RealtimeEmbed design={…}>`. The active artboard becomes the
1362
- * builder's artboard; elements tagged for other artboards are skipped
1363
- * (untagged elements are assumed to belong to the active one).
1364
- */
1365
- static fromState(e) {
1366
- const r = e.artboards.find((n) => n.name === e.activeArtboard) ?? e.artboards[0];
1367
- if (!r)
1368
- throw new Error("DesignBuilder.fromState(): state has no artboards");
1369
- const o = new Pt({
1370
- name: r.name,
1371
- width: r.width,
1372
- height: r.height,
1373
- backgroundColor: r.backgroundColor
1374
- });
1375
- for (const n of e.elements)
1376
- n.artboard && n.artboard !== r.name || o.els.push({ ...n });
1377
- return o;
1378
- }
1379
- nextId(e) {
1380
- return `${e}-${++this.idCounter}`;
1381
- }
1382
- /** Full-bleed background image (fills the whole artboard). Cover-fit (no
1383
- * distortion) when `aspectRatio` is supplied. Inserted at the bottom of the
1384
- * stack regardless of call order. */
1385
- background(e, r = {}) {
1386
- const { width: o, height: n } = this.artboard;
1387
- let a;
1388
- if (r.aspectRatio && r.aspectRatio > 0) {
1389
- const i = _r(r.aspectRatio, o, n);
1390
- a = {
1391
- type: "image",
1392
- width: i.width,
1393
- height: i.height,
1394
- cropX: i.cropX,
1395
- cropY: i.cropY,
1396
- cropWidth: i.cropWidth,
1397
- cropHeight: i.cropHeight,
1398
- flipHorizontal: !1,
1399
- flipVertical: !1,
1400
- borderRadius: 0
1401
- };
1402
- } else
1403
- a = {
1404
- type: "image",
1405
- width: o,
1406
- height: n,
1407
- cropX: 0,
1408
- cropY: 0,
1409
- cropWidth: 1,
1410
- cropHeight: 1,
1411
- flipHorizontal: !1,
1412
- flipVertical: !1,
1413
- borderRadius: 0
1414
- };
1415
- const s = {
1416
- id: this.nextId("bg"),
1417
- transformType: "image",
1418
- x: o / 2,
1419
- y: n / 2,
1420
- rotation: 0,
1421
- opacity: r.opacity ?? 1,
1422
- imageUrl: e,
1423
- imageAspectRatio: r.aspectRatio,
1424
- transformData: a,
1425
- name: r.name ?? "Background"
1426
- };
1427
- return this.els.unshift(s), this;
1428
- }
1429
- /** An anchored image element (not full-bleed). */
1430
- image(e, r = {}) {
1431
- const { width: o, height: n } = this.artboard, a = r.width ?? Math.round(o / 3), s = r.height ?? (r.aspectRatio ? Math.round(a / r.aspectRatio) : a), { x: i, y: c } = Ht(r.anchor ?? se, a, s, o, n, r.padding ?? 0), d = {
1432
- id: this.nextId("img"),
1433
- transformType: "image",
1434
- x: i,
1435
- y: c,
1436
- rotation: r.rotation ?? 0,
1437
- opacity: r.opacity ?? 1,
1438
- imageUrl: e,
1439
- imageAspectRatio: r.aspectRatio,
1440
- transformData: {
1441
- type: "image",
1442
- width: a,
1443
- height: s,
1444
- cropX: 0,
1445
- cropY: 0,
1446
- cropWidth: 1,
1447
- cropHeight: 1,
1448
- flipHorizontal: !1,
1449
- flipVertical: !1,
1450
- borderRadius: 0
1451
- },
1452
- ...r.name ? { name: r.name } : {}
1453
- };
1454
- return this.els.push(d), this;
1455
- }
1456
- /** An anchored text layer. Horizontal centring is exact (textAlign); vertical
1457
- * anchoring uses the font size as the line-height estimate. */
1458
- text(e, r = {}) {
1459
- const { width: o, height: n } = this.artboard, a = r.fontSize ?? Math.round(this.artboard.height / 12), { x: s, y: i } = Ht(r.anchor ?? "top", 0, a, o, n, r.padding ?? Math.round(n / 20)), c = {
1460
- id: this.nextId("text"),
1461
- transformType: "custom",
1462
- x: s,
1463
- y: i,
1464
- rotation: r.rotation ?? 0,
1465
- text: e,
1466
- fontSize: a,
1467
- fontFamily: r.fontFamily ?? "Anton",
1468
- color: r.color ?? "#ffffff",
1469
- textAlign: r.textAlign ?? "center",
1470
- bold: r.bold ?? !1,
1471
- italic: r.italic ?? !1,
1472
- transformData: { type: "custom" },
1473
- ...r.name ? { name: r.name } : {}
1474
- };
1475
- return this.els.push(c), this;
1476
- }
1477
- /** An anchored vector shape. */
1478
- shape(e, r = {}) {
1479
- const { width: o, height: n } = this.artboard, a = r.size ?? Math.round(o / 6), s = typeof a == "number" ? a : a.width, i = typeof a == "number" ? a : a.height, { x: c, y: d } = Ht(r.anchor ?? se, s, i, o, n, r.padding ?? 0), w = {
1480
- type: "shape",
1481
- shapeType: e,
1482
- width: s,
1483
- height: i,
1484
- fillColor: r.fill ?? "#000000",
1485
- fillOpacity: r.opacity ?? 1,
1486
- ...r.points !== void 0 ? { points: r.points } : {},
1487
- ...r.sides !== void 0 ? { sides: r.sides } : {}
1488
- }, T = {
1489
- id: this.nextId("shape"),
1490
- transformType: "shape",
1491
- x: c,
1492
- y: d,
1493
- rotation: r.rotation ?? 0,
1494
- transformData: w,
1495
- ...r.name ? { name: r.name } : {}
1496
- };
1497
- return this.els.push(T), this;
1498
- }
1499
- /** The composed elements, in render order (background first). Editor shape. */
1500
- get elements() {
1501
- return this.els.map((e) => ({ ...e }));
1502
- }
1503
- /** Editor-shape canvas state for `<SnowconeCanvas initialElements=… />` and
1504
- * `serializeStateForServer`. `activeArtboard` is the artboard name. */
1505
- toJSON() {
1506
- return {
1507
- elements: this.elements,
1508
- artboards: [{
1509
- name: this.artboard.name,
1510
- width: this.artboard.width,
1511
- height: this.artboard.height,
1512
- // A background set on a design is part of the design: flag it
1513
- // renderable so exports and server renders paint it, matching the
1514
- // editor (the export worker skips it without `exportBackground`).
1515
- ...this.artboard.backgroundColor ? {
1516
- backgroundColor: this.artboard.backgroundColor,
1517
- exportBackground: this.artboard.backgroundColor !== "transparent"
1518
- } : {}
1519
- }],
1520
- activeArtboard: this.artboard.name
1521
- };
1522
- }
1523
- /** Server-render payload for `RenderSession.renderState(placement, …)`.
1524
- * Equivalent to `serializeStateForServer(design.toJSON())`. */
1525
- toServerRequest() {
1526
- return Gr(this.toJSON());
1527
- }
1528
- }
1529
- function hn(t) {
1530
- return new Pt(t);
1531
- }
1532
- const be = "personalization", tn = hr("type", [
1533
- Tt({
1534
- type: st("text"),
1535
- name: X().min(1),
1536
- label: X(),
1537
- placeholder: X().optional()
1538
- }),
1539
- Tt({
1540
- type: st("image"),
1541
- name: X().min(1),
1542
- label: X(),
1543
- placeholder: X().optional(),
1544
- fit: mr([st("cover"), st("contain")]).optional()
1545
- }),
1546
- Tt({
1547
- type: st("color"),
1548
- color: X().min(1),
1549
- label: X()
1550
- })
1551
- ]);
1552
- function mn(t) {
1553
- if (!t || typeof t != "object") return [];
1554
- const e = t[be];
1555
- if (!Array.isArray(e)) return [];
1556
- const r = [];
1557
- for (const o of e) {
1558
- if (!o || typeof o != "object") continue;
1559
- const n = "type" in o ? o : { ...o, type: "text" }, a = tn.safeParse(n);
1560
- a.success && r.push(a.data);
1561
- }
1562
- return r;
1563
- }
1564
- function gn(t, e) {
1565
- return { ...t, [be]: e };
1566
- }
1567
- export {
1568
- Kr as C,
1569
- Pt as D,
1570
- vr as E,
1571
- Br as S,
1572
- be as T,
1573
- un as a,
1574
- gr as b,
1575
- Rr as c,
1576
- hn as d,
1577
- fn as e,
1578
- dn as f,
1579
- ln as g,
1580
- mn as r,
1581
- Gr as s,
1582
- Er as u,
1583
- gn as w
1584
- };
1585
- //# sourceMappingURL=template-fields-C8Tj67ek.js.map