@snowcone-app/canvas 0.23.0 → 0.26.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 (50) hide show
  1. package/ERROR_CONTRACT.md +600 -0
  2. package/README.md +2 -1
  3. package/STYLING_CONTRACT.md +59 -8
  4. package/dist/{ContextualToolbars-BDEt9ND0.js → ContextualToolbars-DUhBKVxe.js} +3 -3
  5. package/dist/{ContextualToolbars-BDEt9ND0.js.map → ContextualToolbars-DUhBKVxe.js.map} +1 -1
  6. package/dist/{EditorCanvasImpl-Ckg7WO2j.js → EditorCanvasImpl-BLZV_el0.js} +6 -6
  7. package/dist/{EditorCanvasImpl-Ckg7WO2j.js.map → EditorCanvasImpl-BLZV_el0.js.map} +1 -1
  8. package/dist/{ImagePanel-BwqFDKvW.js → ImagePanel-sLSjaPWS.js} +3 -3
  9. package/dist/{ImagePanel-BwqFDKvW.js.map → ImagePanel-sLSjaPWS.js.map} +1 -1
  10. package/dist/{SnowconeCanvas-D1sLcIja.js → SnowconeCanvas-4ef4a61J.js} +6 -6
  11. package/dist/{SnowconeCanvas-D1sLcIja.js.map → SnowconeCanvas-4ef4a61J.js.map} +1 -1
  12. package/dist/{UnifiedFontService-CC3CSmH5.js → UnifiedFontService-Bx2MxFfy.js} +867 -834
  13. package/dist/{UnifiedFontService-CC3CSmH5.js.map → UnifiedFontService-Bx2MxFfy.js.map} +1 -1
  14. package/dist/advanced.mjs +7 -7
  15. package/dist/api/embed.d.ts +2 -2
  16. package/dist/components/editor/EditorLoadingOverlay.d.ts +12 -0
  17. package/dist/components/editor/EditorPlacements.d.ts +14 -0
  18. package/dist/components/editor/EditorPreview.d.ts +32 -8
  19. package/dist/components/editor/EditorRoot.d.ts +12 -0
  20. package/dist/components/editor/PreviewPrismOverlay.d.ts +23 -0
  21. package/dist/components/editor/RootInternals.d.ts +31 -0
  22. package/dist/components/editor/editorAudit.d.ts +66 -0
  23. package/dist/components/editor/index.d.ts +14 -0
  24. package/dist/components/editor/usePlacements.d.ts +14 -0
  25. package/dist/components/embed-realtime/context.d.ts +11 -2
  26. package/dist/components/embed-realtime/validateEmbed.d.ts +20 -1
  27. package/dist/{custom-icons-BW6hqmkL.js → custom-icons-DeYIrTK1.js} +7 -7
  28. package/dist/{custom-icons-BW6hqmkL.js.map → custom-icons-DeYIrTK1.js.map} +1 -1
  29. package/dist/design/design-builder.d.ts +41 -3
  30. package/dist/design/product-config.d.ts +10 -0
  31. package/dist/{design-builder-2VUjfmdt.js → design-builder-U7Su9MMC.js} +108 -60
  32. package/dist/design-builder-U7Su9MMC.js.map +1 -0
  33. package/dist/editorErrors-B2mb1sVN.js.map +1 -1
  34. package/dist/embed-DXGydZOT.js +1586 -0
  35. package/dist/embed-DXGydZOT.js.map +1 -0
  36. package/dist/embed.mjs +34 -32
  37. package/dist/errors/editorError.d.ts +17 -1
  38. package/dist/index.mjs +6 -6
  39. package/dist/style.css +1 -1
  40. package/dist/{template-fields-CQ6n2P4V.js → template-fields-DJ157D3E.js} +3 -3
  41. package/dist/{template-fields-CQ6n2P4V.js.map → template-fields-DJ157D3E.js.map} +1 -1
  42. package/dist/testing.mjs +1 -1
  43. package/dist/{validation-C9Fe9ahK.js → validation-DnAkXdUg.js} +7 -7
  44. package/dist/{validation-C9Fe9ahK.js.map → validation-DnAkXdUg.js.map} +1 -1
  45. package/example/package.json +1 -1
  46. package/llms.txt +3 -0
  47. package/package.json +6 -3
  48. package/dist/design-builder-2VUjfmdt.js.map +0 -1
  49. package/dist/embed-DxsaqZJF.js +0 -1226
  50. package/dist/embed-DxsaqZJF.js.map +0 -1
@@ -1,1226 +0,0 @@
1
- import { E as Ee, o as xe, p as ke, C as Ie, q as M, r as Re, t as Se, v as Ce, w as Oe, x as Ne, g as Te, e as _e } from "./UnifiedFontService-CC3CSmH5.js";
2
- import { y as $ } from "./FontAnalyzer-CwIpgTGd.js";
3
- import { jsx as o, jsxs as C, Fragment as $e } from "react/jsx-runtime";
4
- import { useContext as De, createContext as Ae, useRef as R, useState as O, useMemo as T, useSyncExternalStore as j, useEffect as B, Component as Be, forwardRef as ce, useId as ne, useLayoutEffect as W, useCallback as q } from "react";
5
- import { assertVariantSelected as Le, RenderSession as Ve, createCartDetail as Ue } from "@snowcone-app/sdk";
6
- import { i as le, g as _, B as Fe, E as je, r as qe } from "./editorErrors-B2mb1sVN.js";
7
- import { u as Me, p as ze } from "./usePieceGuideBundle-CbJJg66S.js";
8
- import { s as re, i as Pe } from "./design-builder-2VUjfmdt.js";
9
- import { D as He, C as Ke, g as Ge, e as We } from "./template-fields-CQ6n2P4V.js";
10
- import "./CanvasStateV1-DmZ1Qvvn.js";
11
- const ue = Ae(void 0);
12
- function Q() {
13
- const t = De(ue);
14
- if (!t)
15
- throw new Error(
16
- "useRealtimeMockups / <RealtimeCanvas> / <RealtimeMockup> must be rendered inside <RealtimeEmbed>. Wrap them: <RealtimeEmbed shop=… product=… design=… grantUrl=…>…</RealtimeEmbed>"
17
- );
18
- return t;
19
- }
20
- const Qe = "[RealtimeEmbed]";
21
- function Je(t) {
22
- var r, s;
23
- if ((r = t == null ? void 0 : t.requiredPlacements) != null && r.length)
24
- return t.requiredPlacements.filter((a) => !a.autoFilledByVariant).map((a) => a.label);
25
- const e = new Set(
26
- (((s = t == null ? void 0 : t.options) == null ? void 0 : s.combinations) ?? []).map((a) => a.variantId).filter((a) => typeof a == "string" && a !== "")
27
- );
28
- return !!(t != null && t.variantId) || e.size > 1 ? [] : (t == null ? void 0 : t.placements) ?? [];
29
- }
30
- function me({ shop: t, product: e, design: n, hasGrant: r }) {
31
- var f, u;
32
- const s = [];
33
- if (t || s.push({
34
- severity: "error",
35
- issue: "missing-shop",
36
- message: "`shop` is required (your publishable shop id)."
37
- }), r || s.push({
38
- severity: "error",
39
- issue: "missing-grant",
40
- message: "no grant source — pass `grantUrl` (a proxy to mintRealtimeGrant) or `grant`. Without it the session can never authorize and no mockup renders."
41
- }), (f = e == null ? void 0 : e.mockupIds) != null && f.length || s.push({
42
- severity: "error",
43
- issue: "empty-mockup-ids",
44
- message: 'product.mockupIds is empty — pass the catalog scene codes (product.mockups[].id) you want rendered, e.g. ["FV1qjO"].'
45
- }), e != null && e.options)
46
- try {
47
- Le(e);
48
- } catch (m) {
49
- s.push({
50
- severity: "error",
51
- issue: "variant-required",
52
- message: m instanceof Error ? m.message : String(m)
53
- });
54
- }
55
- const a = n.toJSON(), d = a.activeArtboard, c = Je(e);
56
- c.length > 1 && s.push({
57
- severity: "error",
58
- issue: "multi-placement",
59
- message: `product ${e.productId} requires placements [${c.join(", ")}] but this embed renders a single placement ("${d}"). The render will time out waiting for the others. This product needs the eject path — see https://developers.snowcone.app/embed-mode#eject.`
60
- }), (u = e == null ? void 0 : e.placements) != null && u.length && !e.placements.includes(d) && s.push({
61
- severity: "error",
62
- issue: "placement-mismatch",
63
- message: `the design's placement "${d}" is not in product.placements [${e.placements.join(", ")}]. The canvas artboard name and renderState placement must equal a real product placement label, or the server renders a blank mockup. Fix the \`name\` passed to design({ name }).`
64
- });
65
- const l = a.elements.map((m) => m.name).filter((m) => !!m), i = l.filter((m, y) => l.indexOf(m) !== y);
66
- return i.length && s.push({
67
- severity: "warn",
68
- issue: "duplicate-layer-names",
69
- message: `duplicate layer name(s) [${[...new Set(i)].join(", ")}] — useTextBinding(name) / useImageBinding(name) bind to the FIRST match, so a duplicate name makes a control edit the wrong layer. Keep names unique.`
70
- }), s;
71
- }
72
- function Ye(t) {
73
- for (const e of me(t))
74
- (e.severity === "error" && e.issue !== "placement-mismatch" ? console.error : console.warn)(`${Qe} ${e.message}`);
75
- }
76
- const pe = 8e3;
77
- function Xe(t) {
78
- return `[RealtimeEmbed] No mockup after ${Math.round(pe / 1e3)}s. Common causes: a variant product missing \`variantId\` (pass \`product.options\` so the embed can flag it up front), a multi-placement product (the server waits for every required placement but <RealtimeEmbed> sends only "${t}" — pass \`product.requiredPlacements\` so this is flagged at mount; such products need the eject path), an unreachable or non-public asset URL in the design, or the placement "${t}" not existing on the product. The render may still arrive — this is a heads-up, not a failure.`;
79
- }
80
- function Ze(t) {
81
- if ("grantUrl" in t && t.grantUrl)
82
- return { grantUrl: t.grantUrl };
83
- const e = t.grant;
84
- return typeof e == "function" ? { getToken: e } : { getToken: () => Promise.resolve(e) };
85
- }
86
- function fe({ error: t, onRetry: e }) {
87
- const n = le(t) ? t.code : "EDITOR_ERROR";
88
- return /* @__PURE__ */ C(
89
- "div",
90
- {
91
- "data-sc-part": "error",
92
- role: "alert",
93
- style: {
94
- padding: "16px",
95
- borderRadius: "12px",
96
- border: "1px solid var(--sc-color-destructive, #b91c1c)",
97
- background: "var(--sc-color-surface, #fef2f2)",
98
- color: "var(--sc-color-foreground, #7f1d1d)",
99
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
100
- fontSize: "13px",
101
- lineHeight: 1.5
102
- },
103
- children: [
104
- /* @__PURE__ */ C("p", { style: { margin: 0, fontWeight: 700 }, children: [
105
- "Editor failed: ",
106
- n
107
- ] }),
108
- /* @__PURE__ */ o("p", { style: { margin: "4px 0 0" }, children: t.message }),
109
- e ? /* @__PURE__ */ o(
110
- "button",
111
- {
112
- type: "button",
113
- "data-sc-part": "error-retry",
114
- onClick: e,
115
- style: {
116
- marginTop: "10px",
117
- padding: "6px 14px",
118
- borderRadius: "9999px",
119
- border: "1px solid currentColor",
120
- background: "transparent",
121
- color: "inherit",
122
- font: "inherit",
123
- fontWeight: 600,
124
- cursor: "pointer"
125
- },
126
- children: "Retry"
127
- }
128
- ) : null
129
- ]
130
- }
131
- );
132
- }
133
- class et extends Be {
134
- constructor() {
135
- super(...arguments), this.state = { caught: null };
136
- }
137
- static getDerivedStateFromError(e) {
138
- return { caught: e };
139
- }
140
- componentDidCatch(e) {
141
- var n;
142
- qe(e), typeof $ < "u" && ((n = $.env) == null ? void 0 : n.NODE_ENV) !== "production" && console.error("[Editor.Root] contained a composition error:", e);
143
- }
144
- render() {
145
- return this.state.caught ? /* @__PURE__ */ o(fe, { error: this.state.caught }) : this.props.children;
146
- }
147
- }
148
- function tt({ reportedError: t, onRetry: e, children: n }) {
149
- if (t) {
150
- const r = le(t) && t.code === "E_CHUNK_LOAD_FAILED";
151
- return /* @__PURE__ */ o(fe, { error: t, onRetry: r ? e : void 0 });
152
- }
153
- return /* @__PURE__ */ o(et, { reportedError: null, onRetry: e, children: n });
154
- }
155
- function nt(t) {
156
- const {
157
- shop: e,
158
- product: n,
159
- design: r,
160
- throttleMs: s = 200,
161
- onMockups: a,
162
- onRenderError: d,
163
- onIssue: c,
164
- wsUrl: l,
165
- services: i,
166
- children: f,
167
- internals: u,
168
- __validation: m = "throw"
169
- } = t, y = T(() => r.toJSON().activeArtboard, [r]), b = T(() => {
170
- const p = r.toJSON().artboards[0];
171
- if (p)
172
- return {
173
- name: p.name,
174
- width: p.width,
175
- height: p.height,
176
- ...p.backgroundColor ? { backgroundColor: p.backgroundColor } : {}
177
- };
178
- }, [r]);
179
- j(
180
- u.commerce.subscribe,
181
- u.commerce.getVersion,
182
- u.commerce.getVersion
183
- );
184
- const v = u.commerce.renderVariantId(), w = Me(v ?? n.variantId ?? n.defaultGvid), h = T(() => {
185
- if (!w) return null;
186
- const p = r.toJSON().artboards[0];
187
- return ze(w, p) ?? null;
188
- }, [w, r]), x = R(a), g = R(d), N = R(c);
189
- x.current = a, g.current = d, N.current = c;
190
- const [S, L] = O([]), [D, V] = O(!0), [z, P] = O(null), J = !!("grantUrl" in t && t.grantUrl || "grant" in t && t.grant), Y = Ze(t), be = "grantUrl" in Y ? Y.grantUrl : "getToken", ie = `${n.productId}|${n.mockupIds.join(",")}|${n.variantId ?? ""}`, F = `${e}|${ie}|${y}|${J}`, X = T(() => {
191
- var I;
192
- const p = me({ shop: e, product: n, design: r, hasGrant: J });
193
- if (m !== "throw") return p;
194
- const E = p.find((A) => A.severity === "error");
195
- if (E)
196
- throw new _("E_EMBED_CONFIG", E.message, {
197
- issue: E.issue,
198
- docSlug: "embed-mode"
199
- });
200
- for (const A of p)
201
- typeof $ < "u" && ((I = $.env) == null ? void 0 : I.NODE_ENV) !== "production" && console.warn(`[Editor.Root] ${A.message}`);
202
- return p;
203
- }, [F, m]), se = R(null);
204
- B(() => {
205
- var p;
206
- if (X.length !== 0 && se.current !== F) {
207
- se.current = F;
208
- for (const E of X) (p = N.current) == null || p.call(N, E);
209
- }
210
- }, [X, F]);
211
- const oe = R(null);
212
- B(() => {
213
- var p;
214
- if (m === "warn" && typeof $ < "u" && ((p = $.env) == null ? void 0 : p.NODE_ENV) !== "production") {
215
- if (oe.current === F) return;
216
- oe.current = F, Ye({ shop: e, product: n, design: r, hasGrant: J });
217
- }
218
- }, [F, m]);
219
- const H = T(
220
- () => {
221
- try {
222
- return { session: new Ve({ shop: e, product: n, wsUrl: l, ...Y }) };
223
- } catch (p) {
224
- return { error: p instanceof Error ? p.message : String(p) };
225
- }
226
- },
227
- // eslint-disable-next-line react-hooks/exhaustive-deps
228
- [e, ie, y, l, be]
229
- ), k = "session" in H ? H.session : null, K = "error" in H ? H.error : null, G = T(() => (p, E = s) => {
230
- k && (P(null), V(!0), k.renderState(y, p, E).catch((I) => {
231
- var de;
232
- const A = I instanceof Error ? I.message : String(I);
233
- P(A), V(!1), (de = g.current) == null || de.call(g, A);
234
- }));
235
- }, [k, y, s]);
236
- B(() => {
237
- var p;
238
- if (K) {
239
- P(K), V(!1), (p = g.current) == null || p.call(g, K);
240
- return;
241
- }
242
- if (k)
243
- return k.onMockups((E) => {
244
- var I;
245
- L(E), V(!1), (I = x.current) == null || I.call(x, E);
246
- }), k.onError((E) => {
247
- var I;
248
- P(E), V(!1), (I = g.current) == null || I.call(g, E);
249
- }), G(r.toServerRequest()), () => k.close();
250
- }, [k, K]);
251
- const Z = R({
252
- session: null,
253
- variantId: void 0
254
- });
255
- B(() => {
256
- if (!k || !v) return;
257
- const p = Z.current, E = p.session === k ? p.variantId : n.variantId;
258
- if (v === E) {
259
- Z.current = { session: k, variantId: E };
260
- return;
261
- }
262
- Z.current = { session: k, variantId: v }, k.setProduct({ ...n, variantId: v }), G(re(u.store.getState()));
263
- }, [k, v]), B(() => {
264
- if (!D) return;
265
- const p = setTimeout(() => {
266
- var I, A;
267
- const E = Xe(y);
268
- typeof $ < "u" && ((I = $.env) == null ? void 0 : I.NODE_ENV) !== "production" && console.warn(E), (A = N.current) == null || A.call(N, { severity: "warn", issue: "slow-render", message: E });
269
- }, pe);
270
- return () => clearTimeout(p);
271
- }, [D, y]);
272
- const ve = T(
273
- () => ({
274
- session: k,
275
- design: r,
276
- placement: y,
277
- product: n,
278
- mockups: S,
279
- isRendering: D,
280
- error: z,
281
- throttleMs: s,
282
- pieceGuides: h,
283
- pushRender: G,
284
- stateStore: u.store
285
- }),
286
- [k, r, y, n, S, D, z, s, h, G, u.store]
287
- ), we = j(
288
- u.registry.subscribe,
289
- u.registry.getVersion,
290
- u.registry.getVersion
291
- );
292
- T(() => u.registry.validate(), [u.registry, we]);
293
- const ae = /* @__PURE__ */ o(ke.Provider, { value: u, children: /* @__PURE__ */ o(ue.Provider, { value: ve, children: /* @__PURE__ */ o(Fe.Provider, { value: u.registry, children: /* @__PURE__ */ o(
294
- je,
295
- {
296
- ...b ? { initialArtboardConfig: b } : {},
297
- children: f
298
- }
299
- ) }) }) });
300
- return i ? /* @__PURE__ */ o(Ie, { services: i, children: ae }) : ae;
301
- }
302
- function he(t) {
303
- const { design: e, product: n } = t, r = `${n.productId}|${n.variantId ?? ""}`, s = R(null), a = R(null), [d, c] = O(null);
304
- (s.current === null || s.current.design !== e) && (s.current = {
305
- design: e,
306
- internals: {
307
- store: new He(e.toJSON()),
308
- registry: new Ee()
309
- }
310
- }), (a.current === null || a.current.productIdentity !== r) && (a.current = { productIdentity: r, commerce: new Ke(n) });
311
- const l = T(
312
- () => ({
313
- ...s.current.internals,
314
- commerce: a.current.commerce,
315
- ...xe(c)
316
- }),
317
- // eslint-disable-next-line react-hooks/exhaustive-deps
318
- [s.current.internals, a.current.commerce]
319
- );
320
- return (
321
- // `display: contents` — the .sc-root scope without a layout box, so the
322
- // host's grid/flex sees the parts directly (and the legacy RealtimeEmbed
323
- // wrapper, which had no DOM at all, stays layout-identical).
324
- /* @__PURE__ */ o("div", { className: "sc-root", style: { display: "contents" }, "data-sc-part": "root", children: /* @__PURE__ */ o(tt, { reportedError: d, onRetry: () => c(null), children: /* @__PURE__ */ o(nt, { ...t, internals: l }) }) })
325
- );
326
- }
327
- let rt = () => import("./EditorCanvasImpl-Ckg7WO2j.js").then((t) => t.EditorCanvasImpl);
328
- function ge() {
329
- return rt();
330
- }
331
- const it = ce(function({ children: e, maxHeight: n, floatingToolbar: r = !0, className: s, style: a }, d) {
332
- const c = M("Editor.Canvas"), l = ne();
333
- W(() => c.registry.registerCanvas(l), [c.registry, l]);
334
- const [i, f] = O(null);
335
- return B(() => {
336
- let u = !1;
337
- return ge().then(
338
- (m) => {
339
- u || f(() => m);
340
- },
341
- (m) => {
342
- u || c.reportRuntimeError(
343
- new _(
344
- "E_CHUNK_LOAD_FAILED",
345
- `the editor's rendering chunk failed to load (${m instanceof Error ? m.message : String(m)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
346
- { cause: m instanceof Error ? m.message : String(m), docSlug: "embed-mode" }
347
- )
348
- );
349
- }
350
- ), () => {
351
- u = !0;
352
- };
353
- }, [c]), i ? (
354
- // `builtinControls`: the canvas owns the floating contextual toolbar
355
- // unless the host opts out via `floatingToolbar={false}` or a docked
356
- // <Editor.Controls> is mounted (ADR-0085 §5). Only here — the legacy
357
- // <RealtimeCanvas> wrapper stays chrome-less.
358
- /* @__PURE__ */ o(
359
- i,
360
- {
361
- ref: d,
362
- kit: "embed-only",
363
- partAttr: "canvas",
364
- builtinControls: r,
365
- maxHeight: n,
366
- className: s,
367
- style: a,
368
- children: e
369
- }
370
- )
371
- ) : /* @__PURE__ */ o(
372
- "div",
373
- {
374
- "data-sc-part": "canvas",
375
- "data-sc-state": "loading",
376
- className: s,
377
- style: { minHeight: n ?? 200, ...a },
378
- "aria-busy": "true"
379
- }
380
- );
381
- }), st = 1.2, ee = [1200, 1400, 1600, 1800, 2e3, 2500, 3e3, 4e3];
382
- function ot(t) {
383
- for (const e of ee)
384
- if (e >= t) return e;
385
- return ee[ee.length - 1];
386
- }
387
- function at(t, e) {
388
- if (!(t > 0) || !(e > 0) || e <= t * st) return null;
389
- const n = ot(e);
390
- return `[RealtimeMockup] mockup rendered at ${t}px but displayed at ~${e} device px — the preview will look soft. Pass \`product.width\` (e.g. ${n}) on the embed for a crisp preview.`;
391
- }
392
- function ye({
393
- mockupId: t,
394
- fallback: e = null,
395
- alt: n = "Live product mockup",
396
- className: r,
397
- style: s
398
- }) {
399
- const { mockups: a, error: d, isRendering: c } = Q(), l = R(/* @__PURE__ */ new Set()), i = t ? a.find((u) => u.mockupId === t && u.imageUrl) : a.find((u) => u.imageUrl);
400
- if (!(i != null && i.imageUrl))
401
- return d ? /* @__PURE__ */ o(
402
- "div",
403
- {
404
- "data-sc-part": "preview-error",
405
- "data-sc-preview-state": "error",
406
- role: "alert",
407
- "data-sc-error": d,
408
- className: r,
409
- style: s,
410
- children: e ?? `Render failed: ${d}`
411
- }
412
- ) : /* @__PURE__ */ o("div", { "data-sc-part": "preview", "data-sc-preview-state": "rendering", "aria-busy": "true", style: { display: "contents" }, children: e });
413
- const f = (u) => {
414
- var w;
415
- if (typeof $ > "u" || ((w = $.env) == null ? void 0 : w.NODE_ENV) === "production" || typeof window > "u" || l.current.has(i.mockupId)) return;
416
- const m = u.currentTarget, y = m.getBoundingClientRect().width;
417
- if (y <= 0) return;
418
- const b = Math.round(y * (window.devicePixelRatio || 1)), v = at(m.naturalWidth, b);
419
- v && (l.current.add(i.mockupId), console.warn(v));
420
- };
421
- return (
422
- // eslint-disable-next-line @next/next/no-img-element
423
- /* @__PURE__ */ o(
424
- "img",
425
- {
426
- "data-sc-part": "preview",
427
- "data-sc-preview-state": c ? "rendering" : "ready",
428
- src: i.imageUrl,
429
- alt: n,
430
- className: r,
431
- style: s,
432
- onLoad: f
433
- }
434
- )
435
- );
436
- }
437
- function dt(t) {
438
- const { layer: e, optional: n = !1, placeholder: r, className: s, style: a } = t, d = M("Editor.TextField"), { pushRender: c, throttleMs: l, stateStore: i } = Q(), f = ne(), u = R(null);
439
- if (!Pe(e))
440
- throw new _(
441
- "E_LAYER_TOKEN_REQUIRED",
442
- `the \`layer\` prop must be a token minted by the design — pass layer={design.layer("name")}, not ${typeof e == "string" ? `the bare string "${e}"` : "a plain value"}. Tokens validate the name against the design's layers at mint time.`,
443
- { received: typeof e == "string" ? e : typeof e, docSlug: "embed-mode" }
444
- );
445
- if (u.current !== e.name) {
446
- const b = i.layerKindOf(e.name);
447
- if (b === void 0)
448
- throw new _(
449
- "E_LAYER_NOT_FOUND",
450
- `no layer "${e.name}" in this Root's design — the token was minted from a different design than the one passed to <Editor.Root design={…}>. Bindable layers here: ${JSON.stringify(i.bindableLayerNames())}.`,
451
- { layerName: e.name, validNames: i.bindableLayerNames(), docSlug: "embed-mode" }
452
- );
453
- if (b !== "text")
454
- throw new _(
455
- "E_LAYER_WRONG_KIND",
456
- `layer "${e.name}" is ${b === null ? "not a bindable kind" : `an ${b} layer`} — <Editor.TextField> binds text layers only.`,
457
- { layerName: e.name, actualKind: b, docSlug: "embed-mode" }
458
- );
459
- u.current = e.name;
460
- }
461
- W(() => d.registry.registerTextField(f, { name: e.name, optional: n }), [d.registry, f, e.name, n]);
462
- const m = j(
463
- i.subscribe,
464
- () => i.textOf(e.name) ?? "",
465
- () => i.textOf(e.name) ?? ""
466
- ), y = q(
467
- (b) => {
468
- const v = b.target.value, w = d.registry.getCanvasBridge();
469
- if (w) {
470
- w.setTextByName(e.name, v) || d.reportInvariant(
471
- new _(
472
- "E_LAYER_NOT_FOUND",
473
- `layer "${e.name}" no longer exists in the mounted canvas — it was removed or renamed after this <Editor.TextField> bound to it. Bound layers must not be renamed or deleted while a field is mounted (ADR-0085 §3).`,
474
- { layerName: e.name, docSlug: "embed-mode" }
475
- )
476
- );
477
- return;
478
- }
479
- i.updateTextLayer(e.name, v), c(re(i.getState()), l);
480
- },
481
- [d, e.name, i, c, l]
482
- );
483
- return /* @__PURE__ */ o(
484
- "input",
485
- {
486
- "data-sc-part": "textfield",
487
- "data-sc-layer": e.name,
488
- type: "text",
489
- value: m,
490
- onChange: y,
491
- placeholder: r,
492
- "aria-label": t["aria-label"] ?? e.name,
493
- className: s,
494
- style: a
495
- }
496
- );
497
- }
498
- function ct(t) {
499
- const e = [];
500
- for (const n of t.missingOptionKeys)
501
- e.push(`option:${n}`);
502
- t.missingOptionKeys.length === 0 && !t.selectionValid && e.push("variant");
503
- for (const n of t.textFields) {
504
- if (n.optional) continue;
505
- const r = (t.textOf(n.name) ?? "").trim();
506
- if (r.length === 0) {
507
- e.push(`layer:${n.name}`);
508
- continue;
509
- }
510
- const s = (t.seedTextOf(n.name) ?? "").trim();
511
- s.length > 0 && r === s && e.push(`layer-seed:${n.name}`);
512
- }
513
- return t.hasRenderableElement || e.push("empty-design"), (!Number.isInteger(t.quantity) || t.quantity < 1) && e.push("quantity"), e;
514
- }
515
- function lt(t, e) {
516
- const n = e.getCombinations(), r = e.getAttributes();
517
- if (Object.keys(r).length === 0 || n.length === 0) return null;
518
- const s = Object.fromEntries(
519
- Object.entries(r).map(([a, d]) => [a, { ...d, choices: Ge(d) }])
520
- );
521
- return Ue({
522
- productId: t.productId,
523
- quantity: e.getQuantity(),
524
- selection: e.getSelection(),
525
- combinations: n,
526
- attributes: s
527
- });
528
- }
529
- function ut(t) {
530
- const { onAddToCart: e, defaultQuantity: n, children: r, className: s, style: a } = t, d = M("Editor.Buy"), { product: c, stateStore: l } = Q(), i = d.commerce;
531
- if (!(c != null && c.productId))
532
- throw new _(
533
- "E_BUY_NO_PRODUCT",
534
- "<Editor.Buy> has no product context — pass `product` (with `productId`) on <Editor.Root product={…}>. Buy assembles its cart payload from the Root product and the live design state.",
535
- { docSlug: "embed-mode" }
536
- );
537
- const f = R(!1);
538
- W(() => {
539
- f.current || (f.current = !0, typeof n == "number" && i.setQuantity(n));
540
- }, [i]);
541
- const u = j(l.subscribe, l.getState, l.getState), m = j(i.subscribe, i.getVersion, i.getVersion), y = j(
542
- d.registry.subscribe,
543
- d.registry.getVersion,
544
- d.registry.getVersion
545
- ), b = i.getQuantity(), v = T(
546
- () => ct({
547
- quantity: b,
548
- missingOptionKeys: i.missingOptionKeys(),
549
- selectionValid: i.selectionValid(),
550
- textFields: d.registry.getTextFields(),
551
- textOf: (S) => l.textOf(S),
552
- seedTextOf: (S) => l.seedTextOf(S),
553
- hasRenderableElement: l.hasRenderableElement()
554
- }),
555
- // eslint-disable-next-line react-hooks/exhaustive-deps
556
- [i, b, u, m, y]
557
- ), w = v.length === 0, [h, x] = O(!1), g = R(!1), N = q(() => {
558
- if (g.current || !w) return;
559
- g.current = !0, x(!0);
560
- const S = l.getState(), L = i.resolvedVariantId(), D = {
561
- productId: c.productId,
562
- ...L ? { variantId: L } : {},
563
- quantity: i.getQuantity(),
564
- selection: i.getSelection(),
565
- cartDetail: lt(c, i),
566
- designState: S,
567
- serverRequest: re(S)
568
- };
569
- let V;
570
- try {
571
- V = e(D);
572
- } catch (z) {
573
- throw g.current = !1, x(!1), z;
574
- }
575
- Promise.resolve(V).finally(() => {
576
- g.current = !1, x(!1);
577
- });
578
- }, [w, c, i, l, e]);
579
- return /* @__PURE__ */ o(
580
- "button",
581
- {
582
- type: "button",
583
- "data-sc-part": "buy",
584
- "data-sc-buy-state": h ? "busy" : w ? "ready" : "incomplete",
585
- "data-sc-buy-missing": v.join(",") || void 0,
586
- disabled: !w || h,
587
- "aria-busy": h || void 0,
588
- onClick: N,
589
- className: s,
590
- style: a,
591
- children: r ?? "Add to cart"
592
- }
593
- );
594
- }
595
- function mt({
596
- placement: t = "docked",
597
- className: e,
598
- style: n
599
- }) {
600
- const r = M("Editor.Controls");
601
- Re("Editor.Controls");
602
- const s = ne();
603
- W(
604
- () => r.registry.registerControls(s, t),
605
- [r.registry, s, t]
606
- );
607
- const a = t === "docked", [d, c] = O(null);
608
- return B(() => {
609
- if (!a) return;
610
- let l = !1;
611
- return import("./ContextualToolbars-BDEt9ND0.js").then((i) => i.s).then(
612
- (i) => {
613
- l || c(() => i.ContextualToolbars);
614
- },
615
- (i) => {
616
- l || r.reportRuntimeError(
617
- new _(
618
- "E_CHUNK_LOAD_FAILED",
619
- `the editor's controls chunk failed to load (${i instanceof Error ? i.message : String(i)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
620
- { cause: i instanceof Error ? i.message : String(i), docSlug: "embed-mode" }
621
- )
622
- );
623
- }
624
- ), () => {
625
- l = !0;
626
- };
627
- }, [a, r]), a ? /* @__PURE__ */ o(
628
- "div",
629
- {
630
- "data-sc-part": "controls",
631
- "data-sc-placement": "docked",
632
- className: e,
633
- style: n,
634
- "aria-busy": d ? void 0 : "true",
635
- children: d ? /* @__PURE__ */ o(d, { mode: "embedded" }) : null
636
- }
637
- ) : null;
638
- }
639
- function pt() {
640
- const e = M("useProductSelection").commerce, n = j(e.subscribe, e.getVersion, e.getVersion), r = q((a, d) => e.select(a, d), [e]), s = q((a) => e.setQuantity(a), [e]);
641
- return T(
642
- () => ({
643
- attributes: e.getAttributes(),
644
- selection: e.getSelection(),
645
- select: r,
646
- disabledChoices: e.disabledChoices(),
647
- variantId: e.resolvedVariantId(),
648
- price: e.resolvedPrice(),
649
- quantity: e.getQuantity(),
650
- setQuantity: s,
651
- complete: e.selectionValid(),
652
- missing: e.missingOptionKeys()
653
- }),
654
- // eslint-disable-next-line react-hooks/exhaustive-deps
655
- [e, r, s, n]
656
- );
657
- }
658
- const U = {
659
- /** The one provider boundary — session, design, live state, error boundary. */
660
- Root: he,
661
- /** The editable artboard (the ONE lazy renderer boundary — D9). */
662
- Canvas: it,
663
- /** Live product mockup. ×N under one Root is valid. */
664
- Preview: ye,
665
- /** Form input bound to a design text layer by typed token (T1). */
666
- TextField: dt,
667
- /** Smart commerce part: completeness + payload assembly; submit = host callback. */
668
- Buy: ut,
669
- /** Layer list. The one editor part that works canvas-free (ADR-0085 §4). */
670
- Layers: Ne,
671
- /** Morphing contextual zone — ONE coarse part; placement docked|floating (§5). */
672
- Controls: mt,
673
- /** Undo/redo controls. Requires Editor.Canvas. */
674
- History: Oe,
675
- /** The one ⋯ overflow menu. Requires Editor.Canvas. */
676
- Menu: Ce,
677
- /** The ⊕ add-element menu. Requires Editor.Canvas. */
678
- Add: Se
679
- };
680
- function $t(t) {
681
- return /* @__PURE__ */ o(he, { ...t, __validation: "warn" });
682
- }
683
- const Dt = ce(
684
- function(e, n) {
685
- const { children: r, className: s, style: a, maxHeight: d, ...c } = e, [l, i] = O(null);
686
- return B(() => {
687
- let f = !1;
688
- return ge().then(
689
- (u) => {
690
- f || i(() => u);
691
- },
692
- (u) => {
693
- console.error("[RealtimeCanvas] failed to load the rendering chunk:", u);
694
- }
695
- ), () => {
696
- f = !0;
697
- };
698
- }, []), l ? /* @__PURE__ */ o(l, { ref: n, selfScope: !0, maxHeight: d, className: s, style: a, ...c, children: r }) : /* @__PURE__ */ o(
699
- "div",
700
- {
701
- className: `sc-root${s ? ` ${s}` : ""}`,
702
- style: { minHeight: d ?? 200, ...a },
703
- "aria-busy": "true"
704
- }
705
- );
706
- }
707
- );
708
- function At(t) {
709
- return /* @__PURE__ */ o(ye, { ...t });
710
- }
711
- function Bt() {
712
- const { mockups: t, isRendering: e, error: n } = Q();
713
- return { mockups: t, isRendering: e, error: n };
714
- }
715
- function ft(t) {
716
- return t.type !== "color" && typeof t.width == "number" && t.width > 0 && typeof t.height == "number" && t.height > 0;
717
- }
718
- function Lt(t, e = {}) {
719
- var m, y, b, v, w;
720
- const n = (t.placements ?? []).filter(ft), r = n.map((h) => h.label), s = e.placement ? n.find((h) => h.label === e.placement) : n[0];
721
- if (!s)
722
- throw e.placement ? new _(
723
- "E_PRODUCT_NOT_RENDERABLE",
724
- `product ${t.id} has no renderable placement "${e.placement}". Renderable image placements: [${r.join(", ")}].`,
725
- { productId: t.id, requested: e.placement, validNames: r, docSlug: "embed-mode" }
726
- ) : new _(
727
- "E_PRODUCT_NOT_RENDERABLE",
728
- `product ${t.id} has no renderable image placement (a non-color placement with width/height). The catalog payload may be incomplete — fetch it via getProduct(), not a search hit.`,
729
- { productId: t.id, docSlug: "embed-mode" }
730
- );
731
- const a = ((m = t.options) == null ? void 0 : m.attributes) ?? {}, c = Object.values(a).some(
732
- (h) => {
733
- var x;
734
- return (x = h == null ? void 0 : h.choices) == null ? void 0 : x.some((g) => g == null ? void 0 : g.hex);
735
- }
736
- ) ? (v = (b = (y = t.options) == null ? void 0 : y.combinations) == null ? void 0 : b.find(
737
- (h) => typeof h.variantId == "string" && h.variantId !== ""
738
- )) == null ? void 0 : v.variantId : void 0, l = t.mockups ?? [], i = c ? l.filter((h) => {
739
- var x;
740
- return (x = h.gvids) == null ? void 0 : x.includes(c);
741
- }) : l, f = (i.length > 0 ? i : l).map((h) => h.id);
742
- if (f.length === 0)
743
- throw new _(
744
- "E_PRODUCT_NOT_RENDERABLE",
745
- `product ${t.id} has no mockup scenes — nothing to render a preview into.`,
746
- { productId: t.id, docSlug: "embed-mode" }
747
- );
748
- return {
749
- product: {
750
- productId: t.id,
751
- mockupIds: f,
752
- ...typeof c == "string" ? { variantId: c } : {},
753
- options: t.options,
754
- placements: (w = t.placements) == null ? void 0 : w.map((h) => h.label),
755
- mockups: l,
756
- requiredPlacements: t.requiredPlacements,
757
- ...t.defaultGvid ? { defaultGvid: t.defaultGvid } : {},
758
- ...typeof e.width == "number" ? { width: e.width } : {}
759
- },
760
- placement: {
761
- label: s.label,
762
- width: s.width,
763
- height: s.height,
764
- aspectRatio: s.width / s.height
765
- },
766
- placementLabels: r,
767
- primaryMockupId: f[0],
768
- variantId: c
769
- };
770
- }
771
- function ht(t) {
772
- return new Intl.NumberFormat("en-US", {
773
- style: "currency",
774
- currency: "USD",
775
- minimumFractionDigits: t % 100 === 0 ? 0 : 2,
776
- maximumFractionDigits: t % 100 === 0 ? 0 : 2
777
- }).format(t / 100);
778
- }
779
- const gt = {
780
- display: "inline-flex",
781
- alignItems: "center",
782
- justifyContent: "center",
783
- width: 32,
784
- height: 32,
785
- padding: 0,
786
- borderRadius: 9999,
787
- border: "none",
788
- background: "transparent",
789
- color: "var(--sc-color-foreground, #111)",
790
- font: "inherit",
791
- fontSize: 16,
792
- lineHeight: 1,
793
- cursor: "pointer"
794
- }, yt = {
795
- display: "flex",
796
- alignItems: "center",
797
- gap: 10,
798
- width: "100%",
799
- padding: "8px 10px",
800
- borderRadius: 8,
801
- border: "none",
802
- background: "transparent",
803
- color: "inherit",
804
- font: "inherit",
805
- fontSize: 12,
806
- fontWeight: 500,
807
- textAlign: "left",
808
- cursor: "pointer"
809
- };
810
- function te({
811
- label: t,
812
- onSelect: e
813
- }) {
814
- return /* @__PURE__ */ o(
815
- "button",
816
- {
817
- type: "button",
818
- role: "menuitem",
819
- "data-sc-part": "snowcone-editor-menu-item",
820
- style: yt,
821
- onClick: e,
822
- children: t
823
- }
824
- );
825
- }
826
- function bt({
827
- fullscreen: t,
828
- onToggleFullscreen: e,
829
- viewCount: n,
830
- viewIndex: r,
831
- onSelectView: s,
832
- onExport: a
833
- }) {
834
- const [d, c] = O(!1), l = () => c(!1);
835
- return /* @__PURE__ */ C("div", { style: { position: "relative" }, children: [
836
- /* @__PURE__ */ o(
837
- "button",
838
- {
839
- type: "button",
840
- "aria-label": "Editor menu",
841
- "aria-haspopup": "menu",
842
- "aria-expanded": d,
843
- "data-sc-part": "snowcone-editor-menu",
844
- style: gt,
845
- onClick: () => c((i) => !i),
846
- children: "⋮"
847
- }
848
- ),
849
- d ? /* @__PURE__ */ C($e, { children: [
850
- /* @__PURE__ */ o(
851
- "div",
852
- {
853
- style: { position: "fixed", inset: 0, zIndex: 39 },
854
- onClick: l,
855
- "aria-hidden": "true"
856
- }
857
- ),
858
- /* @__PURE__ */ C(
859
- "div",
860
- {
861
- role: "menu",
862
- "data-sc-part": "snowcone-editor-menu-list",
863
- style: {
864
- position: "absolute",
865
- right: 0,
866
- top: "100%",
867
- marginTop: 4,
868
- minWidth: 176,
869
- padding: 4,
870
- borderRadius: 12,
871
- background: "var(--sc-color-surface, #fff)",
872
- boxShadow: "0 8px 24px rgba(0,0,0,0.14)",
873
- border: "1px solid rgba(0,0,0,0.08)",
874
- zIndex: 40
875
- },
876
- children: [
877
- /* @__PURE__ */ o(
878
- te,
879
- {
880
- label: t ? "Exit fullscreen" : "Fullscreen",
881
- onSelect: () => {
882
- l(), e();
883
- }
884
- }
885
- ),
886
- n > 1 ? Array.from({ length: n }, (i, f) => /* @__PURE__ */ o(
887
- te,
888
- {
889
- label: `View ${f + 1}${f === r ? " ✓" : ""}`,
890
- onSelect: () => {
891
- l(), s(f);
892
- }
893
- },
894
- f
895
- )) : null,
896
- /* @__PURE__ */ o(
897
- te,
898
- {
899
- label: "Export PNG",
900
- onSelect: () => {
901
- l(), a();
902
- }
903
- }
904
- )
905
- ]
906
- }
907
- )
908
- ] }) : null
909
- ] });
910
- }
911
- function vt() {
912
- const { flatLayers: t, selectLayer: e, generateAllPreviews: n } = Te({ previewSize: 96 });
913
- return B(() => {
914
- t.length > 0 && n();
915
- }, [t.length, n]), /* @__PURE__ */ C(
916
- "div",
917
- {
918
- "data-sc-part": "snowcone-editor-idle-dock",
919
- style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 8, minHeight: 44 },
920
- children: [
921
- /* @__PURE__ */ o(U.Add, {}),
922
- t.length > 0 ? /* @__PURE__ */ o("div", { style: { width: 1, height: 28, background: "rgba(0,0,0,0.1)", flexShrink: 0 }, "aria-hidden": "true" }) : null,
923
- t.map((r) => /* @__PURE__ */ o(
924
- "button",
925
- {
926
- type: "button",
927
- "data-sc-part": "snowcone-editor-layer-thumb",
928
- "aria-label": `Select ${r.name}`,
929
- onClick: () => e(r.id),
930
- style: {
931
- width: 44,
932
- height: 44,
933
- padding: 2,
934
- borderRadius: 12,
935
- border: "1px solid rgba(0,0,0,0.1)",
936
- background: "var(--sc-color-surface, #fff)",
937
- overflow: "hidden",
938
- cursor: "pointer",
939
- flexShrink: 0,
940
- display: "inline-flex",
941
- alignItems: "center",
942
- justifyContent: "center"
943
- },
944
- children: r.previewUrl ? (
945
- // eslint-disable-next-line @next/next/no-img-element
946
- /* @__PURE__ */ o("img", { src: r.previewUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "contain" } })
947
- ) : /* @__PURE__ */ o(
948
- "span",
949
- {
950
- style: {
951
- fontSize: 10,
952
- fontWeight: 600,
953
- maxWidth: "100%",
954
- overflow: "hidden",
955
- textOverflow: "ellipsis",
956
- whiteSpace: "nowrap"
957
- },
958
- children: r.name
959
- }
960
- )
961
- },
962
- r.id
963
- ))
964
- ]
965
- }
966
- );
967
- }
968
- function wt({
969
- onAddToCart: t,
970
- defaultQuantity: e
971
- }) {
972
- const { price: n, complete: r } = pt();
973
- return /* @__PURE__ */ C(
974
- U.Buy,
975
- {
976
- onAddToCart: t,
977
- ...typeof e == "number" ? { defaultQuantity: e } : {},
978
- style: {
979
- display: "inline-flex",
980
- alignItems: "center",
981
- gap: 6,
982
- padding: "7px 16px",
983
- marginLeft: 4,
984
- borderRadius: 9999,
985
- border: "none",
986
- background: "var(--sc-color-foreground, #111)",
987
- color: "var(--sc-color-surface, #fff)",
988
- font: "inherit",
989
- fontSize: 12,
990
- fontWeight: 700,
991
- whiteSpace: "nowrap",
992
- cursor: r ? "pointer" : "not-allowed",
993
- opacity: r ? 1 : 0.5
994
- },
995
- children: [
996
- "Add to cart",
997
- typeof n == "number" ? ` · ${ht(n)}` : ""
998
- ]
999
- }
1000
- );
1001
- }
1002
- function Et({
1003
- title: t,
1004
- height: e,
1005
- mockupIds: n,
1006
- onAddToCart: r,
1007
- defaultQuantity: s,
1008
- className: a,
1009
- style: d
1010
- }) {
1011
- const [c, l] = O(!1), [i, f] = O(0), [u, m] = O(!1), y = R(null), b = We(), v = n[Math.min(i, n.length - 1)], w = q(async () => {
1012
- var D;
1013
- const g = await ((D = y.current) == null ? void 0 : D.exportArtboards({ format: "dataUrl" }));
1014
- if (!g) return;
1015
- const [N, S] = Object.entries(g)[0] ?? [];
1016
- if (typeof S != "string") return;
1017
- const L = document.createElement("a");
1018
- L.href = S, L.download = `${N ?? "design"}.png`, L.click();
1019
- }, []), h = {
1020
- display: "flex",
1021
- flexDirection: "column",
1022
- overflow: "hidden",
1023
- background: "var(--sc-color-surface, #fff)",
1024
- color: "var(--sc-color-foreground, #111)",
1025
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
1026
- ...c ? {
1027
- position: "fixed",
1028
- inset: 0,
1029
- zIndex: 9999,
1030
- height: "100dvh",
1031
- paddingBottom: "env(safe-area-inset-bottom)"
1032
- } : {
1033
- position: "relative",
1034
- height: e,
1035
- borderRadius: 16,
1036
- border: "1px solid rgba(0,0,0,0.08)",
1037
- boxShadow: "0 1px 3px rgba(0,0,0,0.08)"
1038
- },
1039
- ...d
1040
- }, x = {
1041
- position: "absolute",
1042
- top: 8,
1043
- right: 12,
1044
- width: 96,
1045
- aspectRatio: "2 / 3",
1046
- borderRadius: 12,
1047
- overflow: "hidden",
1048
- border: "1px solid rgba(0,0,0,0.1)",
1049
- boxShadow: "0 4px 12px rgba(0,0,0,0.12)",
1050
- background: "var(--sc-color-surface, #fff)",
1051
- padding: 0,
1052
- cursor: "pointer",
1053
- zIndex: 2
1054
- };
1055
- return /* @__PURE__ */ C(
1056
- "div",
1057
- {
1058
- "data-sc-part": "snowcone-editor",
1059
- "data-sc-fullscreen": c ? "true" : void 0,
1060
- className: a,
1061
- style: h,
1062
- children: [
1063
- /* @__PURE__ */ C(
1064
- "div",
1065
- {
1066
- "data-sc-part": "snowcone-editor-header",
1067
- style: {
1068
- display: "flex",
1069
- alignItems: "center",
1070
- gap: 2,
1071
- padding: "10px 12px 4px 16px",
1072
- flexShrink: 0
1073
- },
1074
- children: [
1075
- /* @__PURE__ */ o("span", { style: { fontSize: 14, fontWeight: 700, marginRight: "auto" }, children: t }),
1076
- /* @__PURE__ */ o(U.History, {}),
1077
- /* @__PURE__ */ o(
1078
- bt,
1079
- {
1080
- fullscreen: c,
1081
- onToggleFullscreen: () => l((g) => !g),
1082
- viewCount: n.length,
1083
- viewIndex: i,
1084
- onSelectView: f,
1085
- onExport: () => void w()
1086
- }
1087
- ),
1088
- /* @__PURE__ */ o(wt, { onAddToCart: r, defaultQuantity: s })
1089
- ]
1090
- }
1091
- ),
1092
- /* @__PURE__ */ C(
1093
- "div",
1094
- {
1095
- "data-sc-part": "snowcone-editor-stage",
1096
- style: { position: "relative", flex: 1, minHeight: 0, padding: "4px 12px" },
1097
- children: [
1098
- /* @__PURE__ */ o(U.Canvas, { ref: y, style: { height: "100%" } }),
1099
- u ? (
1100
- // Swapped: the mockup IS the stage; the corner chip returns to the
1101
- // artwork. The canvas stays mounted underneath (no document
1102
- // teardown), just covered.
1103
- /* @__PURE__ */ C(
1104
- "div",
1105
- {
1106
- "data-sc-part": "snowcone-editor-swap-stage",
1107
- style: {
1108
- position: "absolute",
1109
- inset: 0,
1110
- zIndex: 1,
1111
- display: "flex",
1112
- alignItems: "center",
1113
- justifyContent: "center",
1114
- background: "var(--sc-color-surface, #fff)",
1115
- padding: 8
1116
- },
1117
- children: [
1118
- /* @__PURE__ */ o(
1119
- U.Preview,
1120
- {
1121
- mockupId: v,
1122
- alt: "Product mockup",
1123
- fallback: /* @__PURE__ */ o("div", { "aria-busy": "true", children: "Rendering…" }),
1124
- style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain", borderRadius: 12 }
1125
- }
1126
- ),
1127
- /* @__PURE__ */ o(
1128
- "button",
1129
- {
1130
- type: "button",
1131
- "aria-label": "Back to artwork",
1132
- "data-sc-part": "snowcone-editor-swap-back",
1133
- onClick: () => m(!1),
1134
- style: {
1135
- ...x,
1136
- display: "flex",
1137
- alignItems: "center",
1138
- justifyContent: "center",
1139
- fontSize: 11,
1140
- fontWeight: 600,
1141
- color: "inherit",
1142
- border: "1px dashed rgba(0,0,0,0.25)"
1143
- },
1144
- children: "artwork"
1145
- }
1146
- )
1147
- ]
1148
- }
1149
- )
1150
- ) : /* @__PURE__ */ o(
1151
- "button",
1152
- {
1153
- type: "button",
1154
- "aria-label": "Show product mockup",
1155
- "data-sc-part": "snowcone-editor-pip",
1156
- onClick: () => m(!0),
1157
- style: x,
1158
- children: /* @__PURE__ */ o(
1159
- U.Preview,
1160
- {
1161
- mockupId: v,
1162
- alt: "Product mockup preview",
1163
- fallback: /* @__PURE__ */ o("span", { style: { fontSize: 10, opacity: 0.5 }, "aria-busy": "true", children: "mockup…" }),
1164
- style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
1165
- }
1166
- )
1167
- }
1168
- )
1169
- ]
1170
- }
1171
- ),
1172
- /* @__PURE__ */ C(
1173
- "div",
1174
- {
1175
- "data-sc-part": "snowcone-editor-zone",
1176
- "data-sc-zone-state": b ? "selected" : "idle",
1177
- style: { flexShrink: 0, padding: "4px 16px 14px" },
1178
- children: [
1179
- /* @__PURE__ */ o("div", { style: { display: b ? void 0 : "none" }, children: /* @__PURE__ */ o(U.Controls, { placement: "docked" }) }),
1180
- b ? null : /* @__PURE__ */ o(vt, {})
1181
- ]
1182
- }
1183
- )
1184
- ]
1185
- }
1186
- );
1187
- }
1188
- function Vt(t) {
1189
- const {
1190
- onAddToCart: e,
1191
- defaultQuantity: n,
1192
- title: r = "Editor",
1193
- height: s = 480,
1194
- className: a,
1195
- style: d,
1196
- ...c
1197
- } = t;
1198
- return /* @__PURE__ */ o(U.Root, { ...c, children: /* @__PURE__ */ o(
1199
- Et,
1200
- {
1201
- title: r,
1202
- height: s,
1203
- mockupIds: c.product.mockupIds,
1204
- onAddToCart: e,
1205
- ...typeof n == "number" ? { defaultQuantity: n } : {},
1206
- className: a,
1207
- style: d
1208
- }
1209
- ) });
1210
- }
1211
- _e();
1212
- export {
1213
- U as E,
1214
- Dt as R,
1215
- Vt as S,
1216
- $t as a,
1217
- At as b,
1218
- me as c,
1219
- pt as d,
1220
- Bt as e,
1221
- ht as f,
1222
- Lt as p,
1223
- Q as u,
1224
- Ye as v
1225
- };
1226
- //# sourceMappingURL=embed-DxsaqZJF.js.map