@snowcone-app/canvas 0.36.7 → 0.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1799 @@
1
+ import { E as Ke, p as Ge, q as We, C as He, r as te, t as ne, v as Je, w as Qe, x as Ye, y as Xe, z as Ze, h as et, e as tt } from "./useCommands-CQi4Pyd2.js";
2
+ import { E as I, y as Ce, z as P } from "./FontAnalyzer-CEYA_6fY.js";
3
+ import { jsx as c, jsxs as F, Fragment as nt } from "react/jsx-runtime";
4
+ import { useContext as rt, createContext as it, useRef as O, useState as T, useMemo as L, useCallback as K, useLayoutEffect as re, useEffect as U, useSyncExternalStore as J, Component as st, forwardRef as $e, useId as ge, memo as ot, isValidElement as Oe } from "react";
5
+ import { assertVariantSelected as at, RenderSession as dt, createCartDetail as lt } from "@snowcone-app/sdk";
6
+ import { s as de, B as ct, E as ut, r as mt, i as pt } from "./design-builder-CqgTpp9B.js";
7
+ import { u as ft, p as ht } from "./usePieceGuideBundle-BUQKb0Ja.js";
8
+ import { D as gt, C as yt, f as bt, d as vt } from "./template-fields-D8uOutD4.js";
9
+ import "./ExportManager-CDX5_up8.js";
10
+ import "./CanvasStateV1-DLE5d1-d.js";
11
+ const Ne = it(void 0);
12
+ function Q() {
13
+ const e = rt(Ne);
14
+ if (!e)
15
+ throw new Error(
16
+ "useRealtimeMockups / <RealtimeCanvas> / <RealtimeMockup> must be rendered inside <RealtimeEmbed>. Wrap them: <RealtimeEmbed shop=… product=… design=… grantUrl=…>…</RealtimeEmbed>"
17
+ );
18
+ return e;
19
+ }
20
+ function _e(e) {
21
+ var t;
22
+ return Array.isArray((t = e.bundle) == null ? void 0 : t.members) && e.bundle.members.length > 0;
23
+ }
24
+ const G = "bundles";
25
+ function wt(e) {
26
+ return Object.keys(e).sort().map((t) => `${t}:${e[t]}`).join(",");
27
+ }
28
+ function ye(e) {
29
+ const t = e.indexOf(".");
30
+ return t <= 0 || t === e.length - 1 ? null : { role: e.slice(0, t), key: e.slice(t + 1) };
31
+ }
32
+ function Et(e) {
33
+ return e.type !== "color" && typeof e.width == "number" && e.width > 0 && typeof e.height == "number" && e.height > 0;
34
+ }
35
+ function kt(e, t) {
36
+ var i, s;
37
+ const n = (s = (i = e.options) == null ? void 0 : i.combinations) == null ? void 0 : s.find((o) => o.variantId === t);
38
+ if (!n) return t;
39
+ const r = Object.entries(n).filter(([o, l]) => o !== "variantId" && o !== "price" && (typeof l == "string" || typeof l == "number")).map(([o, l]) => `${o} ${String(l)}`);
40
+ return r.length > 0 ? r.join(" · ") : t;
41
+ }
42
+ function yn(e, t = {}) {
43
+ var E, A, C, D, V;
44
+ if (!_e(e))
45
+ throw new I(
46
+ "E_NOT_A_BUNDLE",
47
+ `product ${e.id} has no bundle block — picks / variants are bundle parameters the server refuses on a single product (E_NOT_A_BUNDLE). Use productToRenderConfig() for it.`,
48
+ { productId: e.id, docSlug: G }
49
+ );
50
+ const n = e.bundle.members, r = n.map((m) => m.role);
51
+ for (const [m, R] of [
52
+ ["picks", t.picks],
53
+ ["variants", t.variants]
54
+ ])
55
+ for (const N of Object.keys(R ?? {}))
56
+ if (!r.includes(N))
57
+ throw new I(
58
+ "E_BUNDLE_ROLE_NOT_FOUND",
59
+ `bundle ${e.id} has no role "${N}" (in ${m}). Roles: [${r.join(", ")}].`,
60
+ { productId: e.id, field: m, role: N, validNames: r, docSlug: G }
61
+ );
62
+ const i = {}, s = {}, o = {}, l = e.placements ?? [], u = [];
63
+ for (const m of n) {
64
+ const R = m.products.map((w) => w.productId), N = (E = t.picks) == null ? void 0 : E[m.role], _ = N === void 0 ? m.products[0] : m.products.find((w) => w.productId === N);
65
+ if (!_)
66
+ throw new I(
67
+ "E_BUNDLE_PICK_NOT_FOUND",
68
+ `bundle ${e.id} role "${m.role}" has no alternative "${N}". Alternatives: [${R.join(", ")}] — pass picks: { ${m.role}: "${R[0]}" }.`,
69
+ { productId: e.id, role: m.role, requested: N, validNames: R, docSlug: G }
70
+ );
71
+ i[m.role] = _.productId;
72
+ const Y = (_.variants ?? []).map((w) => w.gvid).filter((w) => typeof w == "string" && w !== ""), q = Y.length > 0 ? Y : _.defaultGvid ? [_.defaultGvid] : [], M = ((A = t.variants) == null ? void 0 : A[m.role]) ?? _.defaultGvid ?? Y[0];
73
+ if (!M || !q.includes(M)) {
74
+ const w = (C = t.variants) == null ? void 0 : C[m.role];
75
+ throw new I(
76
+ "E_BUNDLE_VARIANT_NOT_FOUND",
77
+ q.length === 0 ? `bundle ${e.id} role "${m.role}": alternative ${_.productId} publishes no variant — nothing can render this slot (catalog data is incomplete).` : `bundle ${e.id} role "${m.role}" (${_.productId}) has no variant "${w}". Variants: [${q.join(", ")}] — pass variants: { ${m.role}: "${q[0]}" }.`,
78
+ {
79
+ productId: e.id,
80
+ role: m.role,
81
+ memberProductId: _.productId,
82
+ requested: w,
83
+ validNames: q,
84
+ docSlug: G
85
+ }
86
+ );
87
+ }
88
+ s[m.role] = M, o[m.role] = ((V = (D = _.variants) == null ? void 0 : D.find((w) => w.gvid === M)) == null ? void 0 : V.gvidForMockup) || M;
89
+ const ie = l.filter(
90
+ (w) => w.role === m.role && w.memberProductId === _.productId && typeof w.key == "string" && w.key !== "" && Et(w)
91
+ ).map((w) => ({
92
+ key: w.key,
93
+ role: m.role,
94
+ label: w.label,
95
+ memberProductId: _.productId,
96
+ width: w.width,
97
+ height: w.height,
98
+ aspectRatio: w.width / w.height
99
+ }));
100
+ u.push({
101
+ role: m.role,
102
+ required: m.required !== !1,
103
+ alternatives: R,
104
+ pick: _.productId,
105
+ variantId: M,
106
+ variantChoices: q.map((w) => ({ gvid: w, label: kt(_, w) })),
107
+ placements: ie
108
+ });
109
+ }
110
+ const a = new Set(u.flatMap((m) => m.placements.map((R) => R.key))), h = new Map(u.flatMap((m) => m.placements.map((R) => [R.key, R]))), p = [];
111
+ for (const m of l) {
112
+ if (typeof m.key != "string" || !a.has(m.key)) continue;
113
+ const R = h.get(m.key);
114
+ R && !p.includes(R) && p.push(R);
115
+ }
116
+ const b = p.map((m) => m.key);
117
+ let k;
118
+ if (t.placement !== void 0) {
119
+ if (!ye(t.placement))
120
+ throw new I(
121
+ "E_BUNDLE_ROLE_REQUIRED",
122
+ `placement "${t.placement}" names no role — on bundle ${e.id} a placement is addressed by its role-scoped key. Keys: [${b.join(", ")}].`,
123
+ { productId: e.id, requested: t.placement, validNames: b, docSlug: G }
124
+ );
125
+ if (k = p.find((m) => m.key === t.placement), !k)
126
+ throw new I(
127
+ "E_PRODUCT_NOT_RENDERABLE",
128
+ `bundle ${e.id} has no renderable placement "${t.placement}" for picks ${JSON.stringify(i)}. Keys: [${b.join(", ")}].`,
129
+ { productId: e.id, requested: t.placement, picks: i, validNames: b, docSlug: G }
130
+ );
131
+ } else
132
+ k = p[0];
133
+ if (!k)
134
+ throw new I(
135
+ "E_PRODUCT_NOT_RENDERABLE",
136
+ `bundle ${e.id} has no renderable image placement for picks ${JSON.stringify(i)} (a role-scoped, non-color placement with width/height). The catalog payload may be incomplete — fetch it via getProduct(), not a search hit.`,
137
+ { productId: e.id, picks: i, docSlug: G }
138
+ );
139
+ const d = e.mockups ?? [], v = d.filter(
140
+ (m) => !m.picks || Object.entries(m.picks).every(([R, N]) => i[R] === N)
141
+ ), g = (v.length > 0 ? v : d).map((m) => m.id);
142
+ if (g.length === 0)
143
+ throw new I(
144
+ "E_PRODUCT_NOT_RENDERABLE",
145
+ `bundle ${e.id} has no mockup scenes — nothing to render a preview into.`,
146
+ { productId: e.id, docSlug: G }
147
+ );
148
+ const f = (e.requiredPlacements ?? []).filter((m) => typeof m.key == "string" && m.key !== "" && a.has(m.key)).map((m) => ({ label: m.key, key: m.key, type: m.type, autoFilledByVariant: m.autoFilledByVariant }));
149
+ return {
150
+ kind: "bundle",
151
+ product: {
152
+ productId: e.id,
153
+ mockupIds: g,
154
+ picks: i,
155
+ variants: s,
156
+ placements: b,
157
+ mockups: d,
158
+ ...f.length > 0 ? { requiredPlacements: f } : {},
159
+ ...typeof t.width == "number" ? { width: t.width } : {}
160
+ },
161
+ picks: i,
162
+ variants: s,
163
+ variantToken: wt(o),
164
+ roles: u,
165
+ placement: k,
166
+ placements: p,
167
+ placementKeys: b,
168
+ roleNames: r,
169
+ primaryMockupId: g[0]
170
+ };
171
+ }
172
+ const Rt = "[RealtimeEmbed]";
173
+ function Te(e) {
174
+ var r, i;
175
+ if ((r = e == null ? void 0 : e.requiredPlacements) != null && r.length)
176
+ return e.requiredPlacements.filter((s) => !s.autoFilledByVariant).map((s) => s.label);
177
+ const t = new Set(
178
+ (((i = e == null ? void 0 : e.options) == null ? void 0 : i.combinations) ?? []).map((s) => s.variantId).filter((s) => typeof s == "string" && s !== "")
179
+ );
180
+ return !!(e != null && e.variantId) || t.size > 1 ? [] : (e == null ? void 0 : e.placements) ?? [];
181
+ }
182
+ function xt(e) {
183
+ return !!(e != null && e.picks) || !!(e != null && e.variants);
184
+ }
185
+ function It(e) {
186
+ const t = /* @__PURE__ */ new Set();
187
+ for (const n of e.placements ?? []) {
188
+ const r = ye(n);
189
+ r && t.add(r.role);
190
+ }
191
+ for (const n of Object.keys(e.picks ?? {})) t.add(n);
192
+ for (const n of Object.keys(e.variants ?? {})) t.add(n);
193
+ return [...t];
194
+ }
195
+ function Ae({ shop: e, product: t, design: n, hasGrant: r }) {
196
+ var d, v;
197
+ const i = [];
198
+ if (e || i.push({
199
+ severity: "error",
200
+ issue: "missing-shop",
201
+ message: "`shop` is required (your publishable shop id)."
202
+ }), r || i.push({
203
+ severity: "error",
204
+ issue: "missing-grant",
205
+ message: "no grant source — pass `grantUrl` (a proxy to mintRealtimeGrant) or `grant`. Without it the session can never authorize and no mockup renders."
206
+ }), (d = t == null ? void 0 : t.mockupIds) != null && d.length || i.push({
207
+ severity: "error",
208
+ issue: "empty-mockup-ids",
209
+ message: 'product.mockupIds is empty — pass the catalog scene codes (product.mockups[].id) you want rendered, e.g. ["FV1qjO"].'
210
+ }), t != null && t.options)
211
+ try {
212
+ at(t);
213
+ } catch (g) {
214
+ i.push({
215
+ severity: "error",
216
+ issue: "variant-required",
217
+ message: g instanceof Error ? g.message : String(g)
218
+ });
219
+ }
220
+ const s = n.toJSON(), o = s.artboards.map((g) => g.name), l = xt(t);
221
+ let u = [];
222
+ if (l) {
223
+ const g = t.placements ?? [];
224
+ u = o.filter((E) => !ye(E)), u.length > 0 && i.push({
225
+ severity: "error",
226
+ issue: "bundle-role-required",
227
+ message: `product ${t.productId} is a bundle, but the design's artboard(s) [${u.join(", ")}] name no role. On a bundle every placement is addressed by its role-scoped key (<role>.<key>)${g.length ? ` — keys: [${g.join(", ")}]` : ""}. Seed the design from bundleToRenderConfig(product).placement.key, not from a bare label.`
228
+ }), t.variantId && i.push({
229
+ severity: "error",
230
+ issue: "bundle-role-required",
231
+ message: `product ${t.productId} is a bundle, but product.variantId ("${t.variantId}") names no slot — the server refuses it (E_BUNDLE_ROLE_REQUIRED). Name the variant per role instead: product.variants = { <role>: <gvid> } (bundleToRenderConfig fills it), and leave variantId unset.`
232
+ });
233
+ const S = It(t).filter((E) => {
234
+ var A;
235
+ return !((A = t.variants) != null && A[E]);
236
+ });
237
+ S.length > 0 && i.push({
238
+ severity: "error",
239
+ issue: "bundle-variant-required",
240
+ message: `product ${t.productId} is a bundle, but role(s) [${S.join(", ")}] have no entry in product.variants (${JSON.stringify(t.variants ?? {})}). Every role needs a variant — the server keys the render on the per-role set. Build the product with bundleToRenderConfig(product, { picks, variants }), which fills each slot's default.`
241
+ });
242
+ }
243
+ const a = Te(t), h = a.filter((g) => !o.includes(g));
244
+ a.length > 1 && o.length > 1 && h.length > 0 && i.push({
245
+ severity: "error",
246
+ issue: "multi-placement",
247
+ message: `product ${t.productId} requires placements [${a.join(", ")}] but the design only has artboards [${o.join(", ")}] — missing [${h.join(", ")}]. The render would time out waiting for the missing canvas_states. Add each missing placement to the seed: design(…).artboard('` + h[0] + "', { width, height })… (ADR-0088), or use the eject path — see https://developers.snowcone.app/embed-mode#eject."
248
+ });
249
+ const p = (v = t == null ? void 0 : t.placements) != null && v.length ? o.filter((g) => !t.placements.includes(g) && !u.includes(g)) : [];
250
+ p.length > 0 && i.push({
251
+ severity: "error",
252
+ issue: "placement-mismatch",
253
+ message: `the design's artboard(s) [${p.join(", ")}] are not in product.placements [${t.placements.join(", ")}]. Every artboard name must equal a real product placement label (renderState targets placements by that name), or the server renders a blank mockup. Fix the \`name\` passed to design({ name }) / design().artboard(name, …).`
254
+ });
255
+ const b = s.elements.map((g) => g.name).filter((g) => !!g), k = b.filter((g, f) => b.indexOf(g) !== f);
256
+ return k.length && i.push({
257
+ severity: "warn",
258
+ issue: "duplicate-layer-names",
259
+ message: `duplicate layer name(s) [${[...new Set(k)].join(", ")}] — useTextBinding(name) / useImageBinding(name) bind to the FIRST match, so a duplicate name makes a control edit the wrong layer. Keep names unique.`
260
+ }), i;
261
+ }
262
+ function St(e) {
263
+ for (const t of Ae(e))
264
+ (t.severity === "error" && t.issue !== "placement-mismatch" ? console.error : console.warn)(`${Rt} ${t.message}`);
265
+ }
266
+ function De({ registry: e, store: t, product: n }) {
267
+ const r = [], i = e.getParts(), s = e.getTextFields(), o = e.requiresCanvasPartNames();
268
+ o.length > 0 && !e.hasCanvas() && r.push({
269
+ code: "A_REQUIRES_CANVAS",
270
+ severity: "error",
271
+ message: `${o.join(", ")} ${o.length === 1 ? "is" : "are"} mounted with no <Editor.Canvas> under this Root — they operate on the editable artboard (selection, editor commands) and throw E_REQUIRES_CANVAS.`,
272
+ fix: `Mount <Editor.Canvas/> under the same <Editor.Root>, or remove ${o.join(", ")}. Canvas-free parts: Editor.Preview, Editor.Buy, Editor.TextField, Editor.Layers.`
273
+ });
274
+ const l = i.filter((d) => d.name === "Editor.Canvas").some((d) => d.meta.floatingToolbar !== !1);
275
+ e.hasDockedControls() && l && r.push({
276
+ code: "A_CONTROLS_INERT",
277
+ severity: "warn",
278
+ message: "a docked <Editor.Controls> is mounted while <Editor.Canvas> still has floatingToolbar={true} (the default) — both contextual zones are armed, and the floating toolbar is suppressed only as a side effect of the docked Controls being mounted this frame (it silently reappears when the Controls unmounts).",
279
+ fix: 'Declare contextual-zone ownership in the type surface: pass floatingToolbar={false} on <Editor.Canvas> when a <Editor.Controls placement="docked"> owns the zone.'
280
+ });
281
+ const u = (n == null ? void 0 : n.mockupIds) ?? [];
282
+ if (u.length > 1) {
283
+ const d = i.filter((v) => v.name === "Editor.Preview" && v.meta.mockupId === void 0);
284
+ d.length > 0 && r.push({
285
+ code: "A_PREVIEW_AMBIGUOUS",
286
+ severity: "warn",
287
+ message: `${d.length} <Editor.Preview> part${d.length === 1 ? " has" : "s have"} no mockupId while the product renders ${u.length} mockup scenes [${u.join(", ")}] — each shows whichever scene renders first, an ambiguous fan-out.`,
288
+ fix: `Pin each <Editor.Preview> to a scene: mockupId="${u[0]}" (one of product.mockupIds).`
289
+ });
290
+ }
291
+ const a = i.some((d) => d.name === "Editor.Buy");
292
+ if (s.length > 0 && !a) {
293
+ const d = s.map((v) => v.name);
294
+ r.push({
295
+ code: "A_NO_BUY",
296
+ severity: "info",
297
+ message: `<Editor.TextField> parts are mounted (layers [${d.join(", ")}]) but no <Editor.Buy> — personalization has no commerce gate, so nothing enforces completeness (C2/C3) before the design is bought.`,
298
+ fix: "Mount <Editor.Buy onAddToCart={…}/> under the same <Editor.Root> (it assembles the payload and gates on completeness), or ignore if this composition is deliberately non-commerce."
299
+ });
300
+ }
301
+ const h = [];
302
+ for (const d of s)
303
+ t.layerKindOf(d.name) === void 0 ? r.push({
304
+ code: "A_LAYER_UNMATCHED",
305
+ severity: "error",
306
+ message: `<Editor.TextField layer={design.layer("${d.name}")}> matches zero elements in the current design state — the layer was removed or renamed after the field mounted, so the binding is dead and writes will fail. Bindable layers now: ${JSON.stringify(t.bindableLayerNames())}.`,
307
+ fix: `Restore a text layer named "${d.name}" to the design, or bind the field to an existing layer: layer={design.layer(<one of ${JSON.stringify(t.bindableLayerNames())}>)}.`
308
+ }) : h.push(d);
309
+ const p = h.filter((d) => !d.optional);
310
+ if (p.length > 0 && p.every((d) => t.textOf(d.name) === t.seedTextOf(d.name))) {
311
+ const d = p.map((v) => v.name);
312
+ r.push({
313
+ code: "A_TEXT_AT_SEED",
314
+ severity: "info",
315
+ message: `every required <Editor.TextField> still equals its seed value (layers [${d.join(", ")}]) — <Editor.Buy>'s completeness rule C3 gates checkout until the shopper's text differs from the seed (placeholder text must not be bought).`,
316
+ fix: "Nothing to change for a fresh mount — this clears when the shopper edits the fields. For a field that should NOT gate Buy, pass optional on that <Editor.TextField>."
317
+ });
318
+ }
319
+ t.hasRenderableElement() || r.push({
320
+ code: "A_EMPTY_DESIGN",
321
+ severity: "warn",
322
+ message: "the design state has zero renderable elements (no non-empty text, no image with a URL, no shape) — every mockup renders blank.",
323
+ fix: "Seed content on the builder passed to <Editor.Root design={…}> — design({…}).text(…)/.background(…)/.shape(…) — or add elements through <Editor.Add>."
324
+ });
325
+ const b = /* @__PURE__ */ new Map();
326
+ for (const d of i) b.set(d.name, (b.get(d.name) ?? 0) + 1);
327
+ const k = [...b.entries()].map(([d, v]) => ({ name: d, count: v })).sort((d, v) => d.name.localeCompare(v.name));
328
+ return { findings: r, parts: k };
329
+ }
330
+ const ee = [];
331
+ function Ct(e) {
332
+ return ee.push(e), () => {
333
+ const t = ee.indexOf(e);
334
+ t !== -1 && ee.splice(t, 1);
335
+ };
336
+ }
337
+ function bn() {
338
+ const e = ee[ee.length - 1];
339
+ if (!e)
340
+ throw new I(
341
+ "E_AUDIT_NO_ROOT",
342
+ "auditEditor() found no mounted <Editor.Root>. Mount a Root first — or, to audit at mount time, pass onAudit on the Root: <Editor.Root onAudit={(report) => …}>.",
343
+ { docSlug: "embed-mode" }
344
+ );
345
+ return e.compute();
346
+ }
347
+ const Be = 8e3;
348
+ function $t(e) {
349
+ return `[RealtimeEmbed] No mockup after ${Math.round(Be / 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 "${e}" — 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 "${e}" not existing on the product. The render may still arrive — this is a heads-up, not a failure.`;
350
+ }
351
+ function Ot(e) {
352
+ if ("grantUrl" in e && e.grantUrl)
353
+ return { grantUrl: e.grantUrl };
354
+ const t = e.grant;
355
+ return typeof t == "function" ? { getToken: t } : { getToken: () => Promise.resolve(t) };
356
+ }
357
+ function Le({ error: e, onRetry: t }) {
358
+ const n = Ce(e) ? e.code : "EDITOR_ERROR";
359
+ return /* @__PURE__ */ F(
360
+ "div",
361
+ {
362
+ "data-sc-part": "error",
363
+ role: "alert",
364
+ style: {
365
+ padding: "16px",
366
+ borderRadius: "12px",
367
+ border: "1px solid var(--sc-color-destructive, #b91c1c)",
368
+ background: "var(--sc-color-surface, #fef2f2)",
369
+ color: "var(--sc-color-foreground, #7f1d1d)",
370
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
371
+ fontSize: "13px",
372
+ lineHeight: 1.5
373
+ },
374
+ children: [
375
+ /* @__PURE__ */ F("p", { style: { margin: 0, fontWeight: 700 }, children: [
376
+ "Editor failed: ",
377
+ n
378
+ ] }),
379
+ /* @__PURE__ */ c("p", { style: { margin: "4px 0 0" }, children: e.message }),
380
+ t ? /* @__PURE__ */ c(
381
+ "button",
382
+ {
383
+ type: "button",
384
+ "data-sc-part": "error-retry",
385
+ onClick: t,
386
+ style: {
387
+ marginTop: "10px",
388
+ padding: "6px 14px",
389
+ borderRadius: "9999px",
390
+ border: "1px solid currentColor",
391
+ background: "transparent",
392
+ color: "inherit",
393
+ font: "inherit",
394
+ fontWeight: 600,
395
+ cursor: "pointer"
396
+ },
397
+ children: "Retry"
398
+ }
399
+ ) : null
400
+ ]
401
+ }
402
+ );
403
+ }
404
+ class Nt extends st {
405
+ constructor() {
406
+ super(...arguments), this.state = { caught: null };
407
+ }
408
+ static getDerivedStateFromError(t) {
409
+ return { caught: t };
410
+ }
411
+ componentDidCatch(t) {
412
+ var n;
413
+ mt(t), typeof P < "u" && ((n = P.env) == null ? void 0 : n.NODE_ENV) !== "production" && console.error("[Editor.Root] contained a composition error:", t);
414
+ }
415
+ render() {
416
+ return this.state.caught ? /* @__PURE__ */ c(Le, { error: this.state.caught }) : this.props.children;
417
+ }
418
+ }
419
+ function _t({ reportedError: e, onRetry: t, children: n }) {
420
+ if (e) {
421
+ const r = Ce(e) && e.code === "E_CHUNK_LOAD_FAILED";
422
+ return /* @__PURE__ */ c(Le, { error: e, onRetry: r ? t : void 0 });
423
+ }
424
+ return /* @__PURE__ */ c(Nt, { reportedError: null, onRetry: t, children: n });
425
+ }
426
+ function Tt(e) {
427
+ const {
428
+ shop: t,
429
+ product: n,
430
+ design: r,
431
+ throttleMs: i = 200,
432
+ onMockups: s,
433
+ onRenderError: o,
434
+ onIssue: l,
435
+ wsUrl: u,
436
+ services: a,
437
+ children: h,
438
+ internals: p,
439
+ recordAudit: b,
440
+ __validation: k = "throw"
441
+ } = e, d = L(() => r.toJSON().activeArtboard, [r]), v = L(() => r.toJSON().artboards.map((y) => y.name), [r]), [g, f] = T(null), S = g && g.design === r ? g.label : d, E = L(
442
+ () => (y) => {
443
+ if (!v.includes(y))
444
+ throw new I(
445
+ "E_UNKNOWN_PLACEMENT",
446
+ `no placement "${y}" in this design — its artboards are ${JSON.stringify(v)}. Add it to the seed with design(…).artboard('${y}', { width, height }) (ADR-0088).`,
447
+ { placement: y, validNames: v, docSlug: "embed-mode" }
448
+ );
449
+ f({ design: r, label: y });
450
+ },
451
+ [r, v]
452
+ ), A = L(() => {
453
+ const y = r.toJSON().artboards[0];
454
+ if (y)
455
+ return {
456
+ name: y.name,
457
+ width: y.width,
458
+ height: y.height,
459
+ ...y.backgroundColor ? { backgroundColor: y.backgroundColor } : {}
460
+ };
461
+ }, [r]);
462
+ J(
463
+ p.commerce.subscribe,
464
+ p.commerce.getVersion,
465
+ p.commerce.getVersion
466
+ );
467
+ const C = p.commerce.renderVariantId(), D = ft(C ?? n.variantId ?? n.defaultGvid), V = L(() => {
468
+ if (!D) return null;
469
+ const y = r.toJSON().artboards[0];
470
+ return ht(D, y) ?? null;
471
+ }, [D, r]), m = O(s), R = O(o), N = O(l);
472
+ m.current = s, R.current = o, N.current = l;
473
+ const [_, Y] = T([]), [q, M] = T(!0), [ie, w] = T(null), le = !!("grantUrl" in e && e.grantUrl || "grant" in e && e.grant), ce = Ot(e), qe = "grantUrl" in ce ? ce.grantUrl : "getToken", be = `${n.productId}|${n.mockupIds.join(",")}|${n.variantId ?? ""}`, X = n.picks || n.variants ? JSON.stringify([n.picks ?? null, n.variants ?? null]) : "", H = `${t}|${be}|${X}|${d}|${le}`, ue = L(() => {
474
+ var B;
475
+ const y = Ae({ shop: t, product: n, design: r, hasGrant: le });
476
+ if (k !== "throw") return y;
477
+ const $ = y.find((z) => z.severity === "error");
478
+ if ($)
479
+ throw new I("E_EMBED_CONFIG", $.message, {
480
+ issue: $.issue,
481
+ docSlug: "embed-mode"
482
+ });
483
+ for (const z of y)
484
+ typeof P < "u" && ((B = P.env) == null ? void 0 : B.NODE_ENV) !== "production" && console.warn(`[Editor.Root] ${z.message}`);
485
+ return y;
486
+ }, [H, k]), ve = O(null);
487
+ U(() => {
488
+ var y;
489
+ if (ue.length !== 0 && ve.current !== H) {
490
+ ve.current = H;
491
+ for (const $ of ue) (y = N.current) == null || y.call(N, $);
492
+ }
493
+ }, [ue, H]);
494
+ const we = O(null);
495
+ U(() => {
496
+ var y;
497
+ if (k === "warn" && typeof P < "u" && ((y = P.env) == null ? void 0 : y.NODE_ENV) !== "production") {
498
+ if (we.current === H) return;
499
+ we.current = H, St({ shop: t, product: n, design: r, hasGrant: le });
500
+ }
501
+ }, [H, k]);
502
+ const se = L(
503
+ () => {
504
+ try {
505
+ return { session: new dt({ shop: t, product: n, wsUrl: u, ...ce }) };
506
+ } catch (y) {
507
+ return { error: y instanceof Error ? y.message : String(y) };
508
+ }
509
+ },
510
+ // eslint-disable-next-line react-hooks/exhaustive-deps
511
+ [t, be, d, u, qe]
512
+ ), x = "session" in se ? se.session : null, oe = "error" in se ? se.error : null, Z = L(() => (y, $ = i) => {
513
+ if (!x) return;
514
+ w(null), M(!0);
515
+ const B = (j) => {
516
+ var Ie;
517
+ const ae = j instanceof Error ? j.message : String(j);
518
+ w(ae), M(!1), (Ie = R.current) == null || Ie.call(R, ae);
519
+ }, z = (j, ae) => void x.renderState(j, { schemaVersion: y.schemaVersion, artboards: [ae] }, $).catch(B), pe = y.artboards ?? [];
520
+ if (pe.length > 1) {
521
+ for (const j of pe)
522
+ z(typeof j.name == "string" && j.name ? j.name : d, j);
523
+ return;
524
+ }
525
+ const Re = Te(n), xe = pe[0];
526
+ if (xe && Re.length > 1) {
527
+ for (const j of Re)
528
+ z(j, { ...xe, name: j });
529
+ return;
530
+ }
531
+ x.renderState(d, y, $).catch(B);
532
+ }, [x, d, n, i]);
533
+ U(() => {
534
+ var y;
535
+ if (oe) {
536
+ w(oe), M(!1), (y = R.current) == null || y.call(R, oe);
537
+ return;
538
+ }
539
+ if (x)
540
+ return x.onMockups(($) => {
541
+ var B;
542
+ Y($), M(!1), (B = m.current) == null || B.call(m, $);
543
+ }), x.onError(($) => {
544
+ var B;
545
+ w($), M(!1), (B = R.current) == null || B.call(R, $);
546
+ }), Z(r.toServerRequest()), () => x.close();
547
+ }, [x, oe]);
548
+ const me = O({
549
+ session: null,
550
+ variantId: void 0
551
+ });
552
+ U(() => {
553
+ if (!x || !C) return;
554
+ const y = me.current, $ = y.session === x ? y.variantId : n.variantId;
555
+ if (C === $) {
556
+ me.current = { session: x, variantId: $ };
557
+ return;
558
+ }
559
+ me.current = { session: x, variantId: C }, x.setProduct({ ...n, variantId: C }), Z(de(p.store.getState()));
560
+ }, [x, C]);
561
+ const Ee = O({ session: null, key: "" });
562
+ U(() => {
563
+ if (!x || !X) return;
564
+ const y = Ee.current;
565
+ if (y.session === x && y.key === X) return;
566
+ const $ = y.session !== x;
567
+ Ee.current = { session: x, key: X }, !$ && (x.setProduct(n), Z(de(p.store.getState())));
568
+ }, [x, X]), U(() => {
569
+ if (!q) return;
570
+ const y = setTimeout(() => {
571
+ var B, z;
572
+ const $ = $t(d);
573
+ typeof P < "u" && ((B = P.env) == null ? void 0 : B.NODE_ENV) !== "production" && console.warn($), (z = N.current) == null || z.call(N, { severity: "warn", issue: "slow-render", message: $ });
574
+ }, Be);
575
+ return () => clearTimeout(y);
576
+ }, [q, d]);
577
+ const Pe = L(
578
+ () => ({
579
+ session: x,
580
+ design: r,
581
+ // The ACTIVE placement (ADR-0088): Editor.Canvas follows it as its
582
+ // controlled artboard; single-placement designs never see it move.
583
+ placement: S,
584
+ placements: v,
585
+ selectPlacement: E,
586
+ product: n,
587
+ mockups: _,
588
+ isRendering: q,
589
+ error: ie,
590
+ throttleMs: i,
591
+ pieceGuides: V,
592
+ pushRender: Z,
593
+ stateStore: p.store
594
+ }),
595
+ [x, r, S, v, E, n, _, q, ie, i, V, Z, p.store]
596
+ ), ze = J(
597
+ p.registry.subscribe,
598
+ p.registry.getVersion,
599
+ p.registry.getVersion
600
+ );
601
+ L(() => {
602
+ b(
603
+ De({ registry: p.registry, store: p.store, product: n })
604
+ ), p.registry.validate();
605
+ }, [p.registry, ze]);
606
+ const ke = /* @__PURE__ */ c(We.Provider, { value: p, children: /* @__PURE__ */ c(Ne.Provider, { value: Pe, children: /* @__PURE__ */ c(ct.Provider, { value: p.registry, children: /* @__PURE__ */ c(
607
+ ut,
608
+ {
609
+ ...A ? { initialArtboardConfig: A } : {},
610
+ children: h
611
+ }
612
+ ) }) }) });
613
+ return a ? /* @__PURE__ */ c(He, { services: a, children: ke }) : ke;
614
+ }
615
+ function Ue(e) {
616
+ const { design: t, product: n, onAudit: r } = e, i = `${n.productId}|${n.variantId ?? ""}`, s = O(null), o = O(null), [l, u] = T(null);
617
+ (s.current === null || s.current.design !== t) && (s.current = {
618
+ design: t,
619
+ internals: {
620
+ store: new gt(t.toJSON()),
621
+ registry: new Ke()
622
+ }
623
+ }), (o.current === null || o.current.productIdentity !== i) && (o.current = { productIdentity: i, commerce: new yt(n) });
624
+ const a = L(
625
+ () => ({
626
+ ...s.current.internals,
627
+ commerce: o.current.commerce,
628
+ ...Ge(u)
629
+ }),
630
+ // eslint-disable-next-line react-hooks/exhaustive-deps
631
+ [s.current.internals, o.current.commerce]
632
+ ), h = O(n);
633
+ h.current = n;
634
+ const p = O(r);
635
+ p.current = r;
636
+ const b = O(null), k = K((g) => {
637
+ b.current = g;
638
+ }, []), d = K(
639
+ () => De({
640
+ registry: s.current.internals.registry,
641
+ store: s.current.internals.store,
642
+ product: h.current
643
+ }),
644
+ []
645
+ );
646
+ re(() => Ct({ compute: d }), [d]);
647
+ const v = O(!1);
648
+ return U(() => {
649
+ let g = !1;
650
+ return queueMicrotask(() => {
651
+ var f;
652
+ g || v.current || (v.current = !0, (f = p.current) == null || f.call(p, b.current ?? d()));
653
+ }), () => {
654
+ g = !0;
655
+ };
656
+ }, [d]), // `display: contents` — the .sc-root scope without a layout box, so the
657
+ // host's grid/flex sees the parts directly (and the legacy RealtimeEmbed
658
+ // wrapper, which had no DOM at all, stays layout-identical).
659
+ /* @__PURE__ */ c("div", { className: "sc-root", style: { display: "contents" }, "data-sc-part": "root", children: /* @__PURE__ */ c(_t, { reportedError: l, onRetry: () => u(null), children: /* @__PURE__ */ c(Tt, { ...e, internals: a, recordAudit: k }) }) });
660
+ }
661
+ let At = () => import("./EditorCanvasImpl-BRdFWZA5.js").then((e) => e.EditorCanvasImpl);
662
+ function Fe() {
663
+ return At();
664
+ }
665
+ const Dt = $e(function({ children: t, maxHeight: n, floatingToolbar: r = !0, className: i, style: s }, o) {
666
+ const l = te("Editor.Canvas");
667
+ ne("Editor.Canvas", { floatingToolbar: r });
668
+ const u = ge();
669
+ re(() => l.registry.registerCanvas(u), [l.registry, u]);
670
+ const [a, h] = T(null);
671
+ return U(() => {
672
+ let p = !1;
673
+ return Fe().then(
674
+ (b) => {
675
+ p || h(() => b);
676
+ },
677
+ (b) => {
678
+ p || l.reportRuntimeError(
679
+ new I(
680
+ "E_CHUNK_LOAD_FAILED",
681
+ `the editor's rendering chunk failed to load (${b instanceof Error ? b.message : String(b)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
682
+ { cause: b instanceof Error ? b.message : String(b), docSlug: "embed-mode" }
683
+ )
684
+ );
685
+ }
686
+ ), () => {
687
+ p = !0;
688
+ };
689
+ }, [l]), a ? (
690
+ // `builtinControls`: the canvas owns the floating contextual toolbar
691
+ // unless the host opts out via `floatingToolbar={false}` or a docked
692
+ // <Editor.Controls> is mounted (ADR-0085 §5). Only here — the legacy
693
+ // <RealtimeCanvas> wrapper stays chrome-less.
694
+ /* @__PURE__ */ c(
695
+ a,
696
+ {
697
+ ref: o,
698
+ kit: "embed-only",
699
+ partAttr: "canvas",
700
+ builtinControls: r,
701
+ maxHeight: n,
702
+ className: i,
703
+ style: s,
704
+ children: t
705
+ }
706
+ )
707
+ ) : /* @__PURE__ */ c(
708
+ "div",
709
+ {
710
+ "data-sc-part": "canvas",
711
+ "data-sc-state": "loading",
712
+ className: i,
713
+ style: { minHeight: n ?? 200, ...s },
714
+ "aria-busy": "true"
715
+ }
716
+ );
717
+ });
718
+ function Bt() {
719
+ const { placements: e, placement: t, selectPlacement: n } = Q();
720
+ return L(
721
+ () => ({
722
+ placements: e.map((r) => ({ label: r, active: r === t })),
723
+ activePlacement: t,
724
+ select: n
725
+ }),
726
+ [e, t, n]
727
+ );
728
+ }
729
+ function Lt({ className: e, style: t }) {
730
+ const { placements: n, select: r } = Bt();
731
+ return n.length < 2 ? null : /* @__PURE__ */ c(
732
+ "div",
733
+ {
734
+ "data-sc-part": "placements",
735
+ className: e,
736
+ style: { display: "flex", alignItems: "center", gap: "4px", ...t },
737
+ children: n.map(({ label: i, active: s }) => /* @__PURE__ */ c(
738
+ "button",
739
+ {
740
+ type: "button",
741
+ "data-sc-part": "placements-item",
742
+ "data-sc-active": s ? "true" : "false",
743
+ "aria-pressed": s,
744
+ onClick: () => r(i),
745
+ style: {
746
+ font: "inherit",
747
+ fontSize: "13px",
748
+ fontWeight: s ? 700 : 400,
749
+ color: "var(--sc-color-foreground, inherit)",
750
+ background: "transparent",
751
+ border: "none",
752
+ borderRadius: "9999px",
753
+ padding: "2px 8px",
754
+ cursor: s ? "default" : "pointer",
755
+ textDecoration: s ? "underline" : "none",
756
+ textUnderlineOffset: "4px"
757
+ },
758
+ children: i
759
+ },
760
+ i
761
+ ))
762
+ }
763
+ );
764
+ }
765
+ const Ut = 200, Ft = 400;
766
+ function Vt() {
767
+ return Array.from({ length: 10 }, () => ({
768
+ left: 2 + Math.random() * 96,
769
+ top: 2 + Math.random() * 96,
770
+ delay: (Math.random() * 3.5).toFixed(1),
771
+ duration: (3 + Math.random() * 2).toFixed(1),
772
+ size: 3 + Math.random() * 5
773
+ }));
774
+ }
775
+ let Se = !1;
776
+ function jt() {
777
+ if (!(Se || typeof CSS > "u" || typeof CSS.registerProperty != "function")) {
778
+ Se = !0;
779
+ try {
780
+ CSS.registerProperty({ name: "--sc-prism-mount", syntax: "<number>", initialValue: "0", inherits: !0 });
781
+ } catch {
782
+ }
783
+ try {
784
+ CSS.registerProperty({ name: "--sc-prism-band", syntax: "<number>", initialValue: "0", inherits: !1 });
785
+ } catch {
786
+ }
787
+ }
788
+ }
789
+ const Ve = ot(function({
790
+ visible: t = !0,
791
+ onExited: n,
792
+ variant: r = "dark"
793
+ }) {
794
+ jt();
795
+ const [i, s] = T([]);
796
+ U(() => {
797
+ s(Vt());
798
+ }, []);
799
+ const [o, l] = T(!1);
800
+ U(() => {
801
+ const h = requestAnimationFrame(() => l(!0));
802
+ return () => cancelAnimationFrame(h);
803
+ }, []);
804
+ const u = K(
805
+ (h) => {
806
+ h.propertyName === "background-color" && (t || n == null || n());
807
+ },
808
+ [t, n]
809
+ );
810
+ let a = "";
811
+ return t ? o && (a = "sc-prism-fade-in") : a = "sc-prism-fade-out", /* @__PURE__ */ F(
812
+ "div",
813
+ {
814
+ "data-sc-part": "preview-loading",
815
+ className: ["sc-prism-overlay", r === "light" ? "sc-prism-on-light" : "", a].filter(Boolean).join(" "),
816
+ onTransitionEnd: u,
817
+ "aria-hidden": "true",
818
+ children: [
819
+ Array.from({ length: 6 }, (h, p) => /* @__PURE__ */ c("div", { className: "sc-prism-band" }, `b-${p}`)),
820
+ i.map((h, p) => /* @__PURE__ */ c(
821
+ "div",
822
+ {
823
+ className: "sc-prism-particle",
824
+ style: {
825
+ left: `${h.left}%`,
826
+ top: `${h.top}%`,
827
+ animationDelay: `${h.delay}s`,
828
+ animationDuration: `${h.duration}s`,
829
+ width: `${h.size}px`,
830
+ height: `${h.size}px`
831
+ }
832
+ },
833
+ `p-${p}`
834
+ ))
835
+ ]
836
+ }
837
+ );
838
+ });
839
+ function je(e) {
840
+ const [t, n] = T(!1), [r, i] = T(!1), s = O(null), o = O(null), l = O(!1);
841
+ l.current = t, U(() => (e ? (i(!1), o.current && clearTimeout(o.current), s.current = setTimeout(() => n(!0), Ut)) : (s.current && (clearTimeout(s.current), s.current = null), l.current && (i(!0), o.current = setTimeout(() => i(!1), Ft))), () => {
842
+ s.current && clearTimeout(s.current), o.current && clearTimeout(o.current);
843
+ }), [e]);
844
+ const u = K(() => n(!1), []);
845
+ return {
846
+ showOverlay: t,
847
+ overlayVisible: e || r,
848
+ handleExited: u
849
+ };
850
+ }
851
+ const Mt = 1.2, fe = [1200, 1400, 1600, 1800, 2e3, 2500, 3e3, 4e3];
852
+ function qt(e) {
853
+ for (const t of fe)
854
+ if (t >= e) return t;
855
+ return fe[fe.length - 1];
856
+ }
857
+ function Pt(e, t) {
858
+ if (!(e > 0) || !(t > 0) || t <= e * Mt) return null;
859
+ const n = qt(t);
860
+ return `[RealtimeMockup] mockup rendered at ${e}px but displayed at ~${t} device px — the preview will look soft. Pass \`product.width\` (e.g. ${n}) on the embed for a crisp preview.`;
861
+ }
862
+ function zt(e) {
863
+ if (e === void 0 || e === !0 || e === "dark") return "dark";
864
+ if (e === "light" || e === "host-owned" || Oe(e)) return e;
865
+ throw new I(
866
+ "E_PREVIEW_NO_LOADER",
867
+ `Editor.Preview: invalid loadingOverlay value ${JSON.stringify(e)}. A mockup preview always has a render-in-flight treatment (the loader paints over the still-visible previous mockup). Omit the prop for the default rainbow overlay, pass "dark" | "light", pass a custom ReactElement, or declare "host-owned" if the host renders its own indicator off data-sc-preview-state / <Editor.LoadingOverlay>.`,
868
+ { docSlug: "preview-loading-overlay" }
869
+ );
870
+ }
871
+ function Me({
872
+ mockupId: e,
873
+ fallback: t = null,
874
+ alt: n = "Live product mockup",
875
+ loadingOverlay: r,
876
+ className: i,
877
+ style: s
878
+ }) {
879
+ const { mockups: o, error: l, isRendering: u } = Q();
880
+ ne("Editor.Preview", { ...e !== void 0 ? { mockupId: e } : {} });
881
+ const a = zt(r), h = a === "dark" || a === "light" ? a : null, p = Oe(a) ? a : null, b = a !== "host-owned", { showOverlay: k, overlayVisible: d, handleExited: v } = je(
882
+ b && u
883
+ ), g = O(/* @__PURE__ */ new Set()), f = e ? o.find((E) => E.mockupId === e && E.imageUrl) : o.find((E) => E.imageUrl), S = (E) => {
884
+ var m;
885
+ if (!f || typeof P > "u" || ((m = P.env) == null ? void 0 : m.NODE_ENV) === "production" || typeof window > "u" || g.current.has(f.mockupId)) return;
886
+ const A = E.currentTarget, C = A.getBoundingClientRect().width;
887
+ if (C <= 0) return;
888
+ const D = Math.round(C * (window.devicePixelRatio || 1)), V = Pt(A.naturalWidth, D);
889
+ V && (g.current.add(f.mockupId), console.warn(V));
890
+ };
891
+ if (b) {
892
+ const E = f != null && f.imageUrl ? u ? "rendering" : "ready" : l ? "error" : "rendering";
893
+ return /* @__PURE__ */ F(
894
+ "div",
895
+ {
896
+ "data-sc-part": "preview",
897
+ "data-sc-preview-state": E,
898
+ "aria-busy": E === "rendering" || void 0,
899
+ className: `sc-preview-frame${i ? ` ${i}` : ""}`,
900
+ style: s,
901
+ children: [
902
+ f != null && f.imageUrl ? (
903
+ // eslint-disable-next-line @next/next/no-img-element
904
+ /* @__PURE__ */ c("img", { "data-sc-part": "preview-image", src: f.imageUrl, alt: n, onLoad: S })
905
+ ) : l ? /* @__PURE__ */ c("div", { "data-sc-part": "preview-error", role: "alert", "data-sc-error": l, children: t ?? `Render failed: ${l}` }) : t,
906
+ p ? (
907
+ // Custom indicator: same show-delay + linger window; no exit fade
908
+ // (the host owns its animation), so it unmounts when the window
909
+ // closes.
910
+ k && d ? /* @__PURE__ */ c(
911
+ "div",
912
+ {
913
+ "data-sc-part": "preview-loading",
914
+ className: "sc-preview-custom-loading",
915
+ "aria-hidden": "true",
916
+ children: p
917
+ }
918
+ ) : null
919
+ ) : k && h ? /* @__PURE__ */ c(
920
+ Ve,
921
+ {
922
+ visible: d,
923
+ variant: h,
924
+ onExited: v
925
+ }
926
+ ) : null
927
+ ]
928
+ }
929
+ );
930
+ }
931
+ return f != null && f.imageUrl ? (
932
+ // eslint-disable-next-line @next/next/no-img-element
933
+ /* @__PURE__ */ c(
934
+ "img",
935
+ {
936
+ "data-sc-part": "preview",
937
+ "data-sc-preview-state": u ? "rendering" : "ready",
938
+ src: f.imageUrl,
939
+ alt: n,
940
+ className: i,
941
+ style: s,
942
+ onLoad: S
943
+ }
944
+ )
945
+ ) : l ? /* @__PURE__ */ c(
946
+ "div",
947
+ {
948
+ "data-sc-part": "preview-error",
949
+ "data-sc-preview-state": "error",
950
+ role: "alert",
951
+ "data-sc-error": l,
952
+ className: i,
953
+ style: s,
954
+ children: t ?? `Render failed: ${l}`
955
+ }
956
+ ) : /* @__PURE__ */ c("div", { "data-sc-part": "preview", "data-sc-preview-state": "rendering", "aria-busy": "true", style: { display: "contents" }, children: t });
957
+ }
958
+ function Kt(e) {
959
+ const { layer: t, optional: n = !1, placeholder: r, className: i, style: s } = e, o = te("Editor.TextField");
960
+ ne("Editor.TextField");
961
+ const { pushRender: l, throttleMs: u, stateStore: a } = Q(), h = ge(), p = O(null);
962
+ if (!pt(t))
963
+ throw new I(
964
+ "E_LAYER_TOKEN_REQUIRED",
965
+ `the \`layer\` prop must be a token minted by the design — pass layer={design.layer("name")}, not ${typeof t == "string" ? `the bare string "${t}"` : "a plain value"}. Tokens validate the name against the design's layers at mint time.`,
966
+ { received: typeof t == "string" ? t : typeof t, docSlug: "embed-mode" }
967
+ );
968
+ if (p.current !== t.name) {
969
+ const d = a.layerKindOf(t.name);
970
+ if (d === void 0)
971
+ throw new I(
972
+ "E_LAYER_NOT_FOUND",
973
+ `no layer "${t.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(a.bindableLayerNames())}.`,
974
+ { layerName: t.name, validNames: a.bindableLayerNames(), docSlug: "embed-mode" }
975
+ );
976
+ if (d !== "text")
977
+ throw new I(
978
+ "E_LAYER_WRONG_KIND",
979
+ `layer "${t.name}" is ${d === null ? "not a bindable kind" : `an ${d} layer`} — <Editor.TextField> binds text layers only.`,
980
+ { layerName: t.name, actualKind: d, docSlug: "embed-mode" }
981
+ );
982
+ p.current = t.name;
983
+ }
984
+ re(() => o.registry.registerTextField(h, { name: t.name, optional: n }), [o.registry, h, t.name, n]);
985
+ const b = J(
986
+ a.subscribe,
987
+ () => a.textOf(t.name) ?? "",
988
+ () => a.textOf(t.name) ?? ""
989
+ ), k = K(
990
+ (d) => {
991
+ const v = d.target.value, g = o.registry.getCanvasBridge();
992
+ if (g) {
993
+ g.setTextByName(t.name, v) || o.reportInvariant(
994
+ new I(
995
+ "E_LAYER_NOT_FOUND",
996
+ `layer "${t.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).`,
997
+ { layerName: t.name, docSlug: "embed-mode" }
998
+ )
999
+ );
1000
+ return;
1001
+ }
1002
+ a.updateTextLayer(t.name, v), l(de(a.getState()), u);
1003
+ },
1004
+ [o, t.name, a, l, u]
1005
+ );
1006
+ return /* @__PURE__ */ c(
1007
+ "input",
1008
+ {
1009
+ "data-sc-part": "textfield",
1010
+ "data-sc-layer": t.name,
1011
+ type: "text",
1012
+ value: b,
1013
+ onChange: k,
1014
+ placeholder: r,
1015
+ "aria-label": e["aria-label"] ?? t.name,
1016
+ className: i,
1017
+ style: s
1018
+ }
1019
+ );
1020
+ }
1021
+ function Gt(e) {
1022
+ const t = [];
1023
+ for (const n of e.missingOptionKeys)
1024
+ t.push(`option:${n}`);
1025
+ e.missingOptionKeys.length === 0 && !e.selectionValid && t.push("variant");
1026
+ for (const n of e.textFields) {
1027
+ if (n.optional) continue;
1028
+ const r = (e.textOf(n.name) ?? "").trim();
1029
+ if (r.length === 0) {
1030
+ t.push(`layer:${n.name}`);
1031
+ continue;
1032
+ }
1033
+ const i = (e.seedTextOf(n.name) ?? "").trim();
1034
+ i.length > 0 && r === i && t.push(`layer-seed:${n.name}`);
1035
+ }
1036
+ return e.hasRenderableElement || t.push("empty-design"), (!Number.isInteger(e.quantity) || e.quantity < 1) && t.push("quantity"), t;
1037
+ }
1038
+ function Wt(e, t) {
1039
+ const n = t.getCombinations(), r = t.getAttributes();
1040
+ if (Object.keys(r).length === 0 || n.length === 0) return null;
1041
+ const i = Object.fromEntries(
1042
+ Object.entries(r).map(([s, o]) => [s, { ...o, choices: bt(o) }])
1043
+ );
1044
+ return lt({
1045
+ productId: e.productId,
1046
+ quantity: t.getQuantity(),
1047
+ selection: t.getSelection(),
1048
+ combinations: n,
1049
+ attributes: i
1050
+ });
1051
+ }
1052
+ function Ht(e) {
1053
+ const { onAddToCart: t, defaultQuantity: n, children: r, className: i, style: s } = e, o = te("Editor.Buy");
1054
+ ne("Editor.Buy");
1055
+ const { product: l, stateStore: u } = Q(), a = o.commerce;
1056
+ if (!(l != null && l.productId))
1057
+ throw new I(
1058
+ "E_BUY_NO_PRODUCT",
1059
+ "<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.",
1060
+ { docSlug: "embed-mode" }
1061
+ );
1062
+ const h = O(!1);
1063
+ re(() => {
1064
+ h.current || (h.current = !0, typeof n == "number" && a.setQuantity(n));
1065
+ }, [a]);
1066
+ const p = J(u.subscribe, u.getState, u.getState), b = J(a.subscribe, a.getVersion, a.getVersion), k = J(
1067
+ o.registry.subscribe,
1068
+ o.registry.getVersion,
1069
+ o.registry.getVersion
1070
+ ), d = a.getQuantity(), v = L(
1071
+ () => Gt({
1072
+ quantity: d,
1073
+ missingOptionKeys: a.missingOptionKeys(),
1074
+ selectionValid: a.selectionValid(),
1075
+ textFields: o.registry.getTextFields(),
1076
+ textOf: (C) => u.textOf(C),
1077
+ seedTextOf: (C) => u.seedTextOf(C),
1078
+ hasRenderableElement: u.hasRenderableElement()
1079
+ }),
1080
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1081
+ [a, d, p, b, k]
1082
+ ), g = v.length === 0, [f, S] = T(!1), E = O(!1), A = K(() => {
1083
+ if (E.current || !g) return;
1084
+ E.current = !0, S(!0);
1085
+ const C = u.getState(), D = a.resolvedVariantId(), V = {
1086
+ productId: l.productId,
1087
+ ...D ? { variantId: D } : {},
1088
+ quantity: a.getQuantity(),
1089
+ selection: a.getSelection(),
1090
+ cartDetail: Wt(l, a),
1091
+ designState: C,
1092
+ serverRequest: de(C)
1093
+ };
1094
+ let m;
1095
+ try {
1096
+ m = t(V);
1097
+ } catch (R) {
1098
+ throw E.current = !1, S(!1), R;
1099
+ }
1100
+ Promise.resolve(m).finally(() => {
1101
+ E.current = !1, S(!1);
1102
+ });
1103
+ }, [g, l, a, u, t]);
1104
+ return /* @__PURE__ */ c(
1105
+ "button",
1106
+ {
1107
+ type: "button",
1108
+ "data-sc-part": "buy",
1109
+ "data-sc-buy-state": f ? "busy" : g ? "ready" : "incomplete",
1110
+ "data-sc-buy-missing": v.join(",") || void 0,
1111
+ disabled: !g || f,
1112
+ "aria-busy": f || void 0,
1113
+ onClick: A,
1114
+ className: i,
1115
+ style: s,
1116
+ children: r ?? "Add to cart"
1117
+ }
1118
+ );
1119
+ }
1120
+ function Jt({
1121
+ placement: e = "docked",
1122
+ className: t,
1123
+ style: n
1124
+ }) {
1125
+ const r = te("Editor.Controls");
1126
+ Je("Editor.Controls"), ne("Editor.Controls");
1127
+ const i = ge();
1128
+ re(
1129
+ () => r.registry.registerControls(i, e),
1130
+ [r.registry, i, e]
1131
+ );
1132
+ const s = e === "docked", [o, l] = T(null);
1133
+ return U(() => {
1134
+ if (!s) return;
1135
+ let u = !1;
1136
+ return import("./ContextualToolbars-CG5Gmv_i.js").then((a) => a.s).then(
1137
+ (a) => {
1138
+ u || l(() => a.ContextualToolbars);
1139
+ },
1140
+ (a) => {
1141
+ u || r.reportRuntimeError(
1142
+ new I(
1143
+ "E_CHUNK_LOAD_FAILED",
1144
+ `the editor's controls chunk failed to load (${a instanceof Error ? a.message : String(a)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
1145
+ { cause: a instanceof Error ? a.message : String(a), docSlug: "embed-mode" }
1146
+ )
1147
+ );
1148
+ }
1149
+ ), () => {
1150
+ u = !0;
1151
+ };
1152
+ }, [s, r]), s ? /* @__PURE__ */ c(
1153
+ "div",
1154
+ {
1155
+ "data-sc-part": "controls",
1156
+ "data-sc-placement": "docked",
1157
+ className: t,
1158
+ style: n,
1159
+ "aria-busy": o ? void 0 : "true",
1160
+ children: o ? /* @__PURE__ */ c(o, { mode: "embedded" }) : null
1161
+ }
1162
+ ) : null;
1163
+ }
1164
+ function Qt({
1165
+ variant: e = "dark",
1166
+ className: t,
1167
+ style: n
1168
+ }) {
1169
+ const { isRendering: r } = Q(), { showOverlay: i, overlayVisible: s, handleExited: o } = je(r);
1170
+ return i ? /* @__PURE__ */ c(
1171
+ "div",
1172
+ {
1173
+ "data-sc-part": "loading-overlay",
1174
+ className: `sc-loading-overlay-host${t ? ` ${t}` : ""}`,
1175
+ style: n,
1176
+ "aria-hidden": "true",
1177
+ children: /* @__PURE__ */ c(Ve, { visible: s, variant: e, onExited: o })
1178
+ }
1179
+ ) : null;
1180
+ }
1181
+ function Yt() {
1182
+ const t = te("useProductSelection").commerce, n = J(t.subscribe, t.getVersion, t.getVersion), r = K((s, o) => t.select(s, o), [t]), i = K((s) => t.setQuantity(s), [t]);
1183
+ return L(
1184
+ () => ({
1185
+ attributes: t.getAttributes(),
1186
+ selection: t.getSelection(),
1187
+ select: r,
1188
+ disabledChoices: t.disabledChoices(),
1189
+ variantId: t.resolvedVariantId(),
1190
+ price: t.resolvedPrice(),
1191
+ quantity: t.getQuantity(),
1192
+ setQuantity: i,
1193
+ complete: t.selectionValid(),
1194
+ missing: t.missingOptionKeys()
1195
+ }),
1196
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1197
+ [t, r, i, n]
1198
+ );
1199
+ }
1200
+ const W = {
1201
+ /** The one provider boundary — session, design, live state, error boundary. */
1202
+ Root: Ue,
1203
+ /** The editable artboard (the ONE lazy renderer boundary — D9). */
1204
+ Canvas: Dt,
1205
+ /** Live product mockup. ×N under one Root is valid. */
1206
+ Preview: Me,
1207
+ /** Placement switcher tabs (ADR-0088). Renders nothing for a
1208
+ * single-placement design; headless floor = usePlacements(). */
1209
+ Placements: Lt,
1210
+ /** Form input bound to a design text layer by typed token (T1). */
1211
+ TextField: Kt,
1212
+ /** Smart commerce part: completeness + payload assembly; submit = host callback. */
1213
+ Buy: Ht,
1214
+ /** Layer list. The one editor part that works canvas-free (ADR-0085 §4). */
1215
+ Layers: Ze,
1216
+ /** Morphing contextual zone — ONE coarse part; placement docked|floating (§5). */
1217
+ Controls: Jt,
1218
+ /** Undo/redo controls. Requires Editor.Canvas. */
1219
+ History: Xe,
1220
+ /** The one ⋯ overflow menu. Requires Editor.Canvas. */
1221
+ Menu: Ye,
1222
+ /** The ⊕ add-element menu. Requires Editor.Canvas. */
1223
+ Add: Qe,
1224
+ /** Standalone render-in-flight rainbow for host-owned preview surfaces.
1225
+ * Fills its positioned parent; rides the session (canvas-free). */
1226
+ LoadingOverlay: Qt
1227
+ };
1228
+ function vn(e) {
1229
+ return /* @__PURE__ */ c(Ue, { ...e, __validation: "warn" });
1230
+ }
1231
+ const wn = $e(
1232
+ function(t, n) {
1233
+ const { children: r, className: i, style: s, maxHeight: o, ...l } = t, [u, a] = T(null);
1234
+ return U(() => {
1235
+ let h = !1;
1236
+ return Fe().then(
1237
+ (p) => {
1238
+ h || a(() => p);
1239
+ },
1240
+ (p) => {
1241
+ console.error("[RealtimeCanvas] failed to load the rendering chunk:", p);
1242
+ }
1243
+ ), () => {
1244
+ h = !0;
1245
+ };
1246
+ }, []), u ? /* @__PURE__ */ c(u, { ref: n, selfScope: !0, maxHeight: o, className: i, style: s, ...l, children: r }) : /* @__PURE__ */ c(
1247
+ "div",
1248
+ {
1249
+ className: `sc-root${i ? ` ${i}` : ""}`,
1250
+ style: { minHeight: o ?? 200, ...s },
1251
+ "aria-busy": "true"
1252
+ }
1253
+ );
1254
+ }
1255
+ );
1256
+ function En(e) {
1257
+ return /* @__PURE__ */ c(Me, { ...e });
1258
+ }
1259
+ function kn() {
1260
+ const { mockups: e, isRendering: t, error: n } = Q();
1261
+ return { mockups: e, isRendering: t, error: n };
1262
+ }
1263
+ function Xt(e) {
1264
+ return e.type !== "color" && typeof e.width == "number" && e.width > 0 && typeof e.height == "number" && e.height > 0;
1265
+ }
1266
+ function Rn(e, t = {}) {
1267
+ var b, k, d, v, g;
1268
+ if (_e(e))
1269
+ throw new I(
1270
+ "E_BUNDLE_CONFIG_REQUIRED",
1271
+ `product ${e.id} is a bundle (roles: [${e.bundle.members.map((f) => f.role).join(", ")}]) — it has per-role variants and role-scoped placement keys, which the single-product config cannot describe. Use bundleToRenderConfig(product, { picks?, variants?, placement?, width? }) instead.`,
1272
+ {
1273
+ productId: e.id,
1274
+ validNames: e.bundle.members.map((f) => f.role),
1275
+ docSlug: "bundles"
1276
+ }
1277
+ );
1278
+ const n = (e.placements ?? []).filter(Xt), r = n.map((f) => f.label), i = t.placement ? n.find((f) => f.label === t.placement) : n[0];
1279
+ if (!i)
1280
+ throw t.placement ? new I(
1281
+ "E_PRODUCT_NOT_RENDERABLE",
1282
+ `product ${e.id} has no renderable placement "${t.placement}". Renderable image placements: [${r.join(", ")}].`,
1283
+ { productId: e.id, requested: t.placement, validNames: r, docSlug: "embed-mode" }
1284
+ ) : new I(
1285
+ "E_PRODUCT_NOT_RENDERABLE",
1286
+ `product ${e.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.`,
1287
+ { productId: e.id, docSlug: "embed-mode" }
1288
+ );
1289
+ const s = ((b = e.options) == null ? void 0 : b.attributes) ?? {}, l = Object.values(s).some(
1290
+ (f) => {
1291
+ var S;
1292
+ return (S = f == null ? void 0 : f.choices) == null ? void 0 : S.some((E) => E == null ? void 0 : E.hex);
1293
+ }
1294
+ ) ? (v = (d = (k = e.options) == null ? void 0 : k.combinations) == null ? void 0 : d.find(
1295
+ (f) => typeof f.variantId == "string" && f.variantId !== ""
1296
+ )) == null ? void 0 : v.variantId : void 0, u = e.mockups ?? [], a = l ? u.filter((f) => {
1297
+ var S;
1298
+ return (S = f.gvids) == null ? void 0 : S.includes(l);
1299
+ }) : u, h = (a.length > 0 ? a : u).map((f) => f.id);
1300
+ if (h.length === 0)
1301
+ throw new I(
1302
+ "E_PRODUCT_NOT_RENDERABLE",
1303
+ `product ${e.id} has no mockup scenes — nothing to render a preview into.`,
1304
+ { productId: e.id, docSlug: "embed-mode" }
1305
+ );
1306
+ return {
1307
+ product: {
1308
+ productId: e.id,
1309
+ mockupIds: h,
1310
+ ...typeof l == "string" ? { variantId: l } : {},
1311
+ options: e.options,
1312
+ placements: (g = e.placements) == null ? void 0 : g.map((f) => f.label),
1313
+ mockups: u,
1314
+ requiredPlacements: e.requiredPlacements,
1315
+ ...e.defaultGvid ? { defaultGvid: e.defaultGvid } : {},
1316
+ ...typeof t.width == "number" ? { width: t.width } : {}
1317
+ },
1318
+ placement: {
1319
+ label: i.label,
1320
+ width: i.width,
1321
+ height: i.height,
1322
+ aspectRatio: i.width / i.height
1323
+ },
1324
+ placementLabels: r,
1325
+ placements: n.map((f) => ({
1326
+ label: f.label,
1327
+ width: f.width,
1328
+ height: f.height,
1329
+ aspectRatio: f.width / f.height
1330
+ })),
1331
+ primaryMockupId: h[0],
1332
+ variantId: l
1333
+ };
1334
+ }
1335
+ function Zt(e) {
1336
+ return new Intl.NumberFormat("en-US", {
1337
+ style: "currency",
1338
+ currency: "USD",
1339
+ minimumFractionDigits: e % 100 === 0 ? 0 : 2,
1340
+ maximumFractionDigits: e % 100 === 0 ? 0 : 2
1341
+ }).format(e / 100);
1342
+ }
1343
+ const en = {
1344
+ display: "inline-flex",
1345
+ alignItems: "center",
1346
+ justifyContent: "center",
1347
+ width: 32,
1348
+ height: 32,
1349
+ padding: 0,
1350
+ borderRadius: 9999,
1351
+ border: "none",
1352
+ background: "transparent",
1353
+ color: "var(--sc-color-foreground, #111)",
1354
+ font: "inherit",
1355
+ fontSize: 16,
1356
+ lineHeight: 1,
1357
+ cursor: "pointer"
1358
+ }, tn = {
1359
+ display: "flex",
1360
+ alignItems: "center",
1361
+ gap: 10,
1362
+ width: "100%",
1363
+ padding: "8px 10px",
1364
+ borderRadius: 8,
1365
+ border: "none",
1366
+ background: "transparent",
1367
+ color: "inherit",
1368
+ font: "inherit",
1369
+ fontSize: 12,
1370
+ fontWeight: 500,
1371
+ textAlign: "left",
1372
+ cursor: "pointer"
1373
+ };
1374
+ function he({
1375
+ label: e,
1376
+ onSelect: t
1377
+ }) {
1378
+ return /* @__PURE__ */ c(
1379
+ "button",
1380
+ {
1381
+ type: "button",
1382
+ role: "menuitem",
1383
+ "data-sc-part": "snowcone-editor-menu-item",
1384
+ style: tn,
1385
+ onClick: t,
1386
+ children: e
1387
+ }
1388
+ );
1389
+ }
1390
+ function nn({
1391
+ fullscreen: e,
1392
+ onToggleFullscreen: t,
1393
+ viewCount: n,
1394
+ viewIndex: r,
1395
+ onSelectView: i,
1396
+ onExport: s
1397
+ }) {
1398
+ const [o, l] = T(!1), u = () => l(!1);
1399
+ return /* @__PURE__ */ F("div", { style: { position: "relative" }, children: [
1400
+ /* @__PURE__ */ c(
1401
+ "button",
1402
+ {
1403
+ type: "button",
1404
+ "aria-label": "Editor menu",
1405
+ "aria-haspopup": "menu",
1406
+ "aria-expanded": o,
1407
+ "data-sc-part": "snowcone-editor-menu",
1408
+ style: en,
1409
+ onClick: () => l((a) => !a),
1410
+ children: "⋮"
1411
+ }
1412
+ ),
1413
+ o ? /* @__PURE__ */ F(nt, { children: [
1414
+ /* @__PURE__ */ c(
1415
+ "div",
1416
+ {
1417
+ style: { position: "fixed", inset: 0, zIndex: 39 },
1418
+ onClick: u,
1419
+ "aria-hidden": "true"
1420
+ }
1421
+ ),
1422
+ /* @__PURE__ */ F(
1423
+ "div",
1424
+ {
1425
+ role: "menu",
1426
+ "data-sc-part": "snowcone-editor-menu-list",
1427
+ style: {
1428
+ position: "absolute",
1429
+ right: 0,
1430
+ top: "100%",
1431
+ marginTop: 4,
1432
+ minWidth: 176,
1433
+ padding: 4,
1434
+ borderRadius: 12,
1435
+ background: "var(--sc-color-surface, #fff)",
1436
+ boxShadow: "0 8px 24px rgba(0,0,0,0.14)",
1437
+ border: "1px solid rgba(0,0,0,0.08)",
1438
+ zIndex: 40
1439
+ },
1440
+ children: [
1441
+ /* @__PURE__ */ c(
1442
+ he,
1443
+ {
1444
+ label: e ? "Exit fullscreen" : "Fullscreen",
1445
+ onSelect: () => {
1446
+ u(), t();
1447
+ }
1448
+ }
1449
+ ),
1450
+ n > 1 ? Array.from({ length: n }, (a, h) => /* @__PURE__ */ c(
1451
+ he,
1452
+ {
1453
+ label: `View ${h + 1}${h === r ? " ✓" : ""}`,
1454
+ onSelect: () => {
1455
+ u(), i(h);
1456
+ }
1457
+ },
1458
+ h
1459
+ )) : null,
1460
+ /* @__PURE__ */ c(
1461
+ he,
1462
+ {
1463
+ label: "Export PNG",
1464
+ onSelect: () => {
1465
+ u(), s();
1466
+ }
1467
+ }
1468
+ )
1469
+ ]
1470
+ }
1471
+ )
1472
+ ] }) : null
1473
+ ] });
1474
+ }
1475
+ function rn() {
1476
+ const { flatLayers: e, selectLayer: t, generateAllPreviews: n } = et({ previewSize: 96 });
1477
+ return U(() => {
1478
+ e.length > 0 && n();
1479
+ }, [e.length, n]), /* @__PURE__ */ F(
1480
+ "div",
1481
+ {
1482
+ "data-sc-part": "snowcone-editor-idle-dock",
1483
+ style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 8, minHeight: 44 },
1484
+ children: [
1485
+ /* @__PURE__ */ c(W.Add, {}),
1486
+ e.length > 0 ? /* @__PURE__ */ c("div", { style: { width: 1, height: 28, background: "rgba(0,0,0,0.1)", flexShrink: 0 }, "aria-hidden": "true" }) : null,
1487
+ e.map((r) => /* @__PURE__ */ c(
1488
+ "button",
1489
+ {
1490
+ type: "button",
1491
+ "data-sc-part": "snowcone-editor-layer-thumb",
1492
+ "aria-label": `Select ${r.name}`,
1493
+ onClick: () => t(r.id),
1494
+ style: {
1495
+ width: 44,
1496
+ height: 44,
1497
+ padding: 2,
1498
+ borderRadius: 12,
1499
+ border: "1px solid rgba(0,0,0,0.1)",
1500
+ background: "var(--sc-color-surface, #fff)",
1501
+ overflow: "hidden",
1502
+ cursor: "pointer",
1503
+ flexShrink: 0,
1504
+ display: "inline-flex",
1505
+ alignItems: "center",
1506
+ justifyContent: "center"
1507
+ },
1508
+ children: r.previewUrl ? (
1509
+ // eslint-disable-next-line @next/next/no-img-element
1510
+ /* @__PURE__ */ c("img", { src: r.previewUrl, alt: "", style: { width: "100%", height: "100%", objectFit: "contain" } })
1511
+ ) : /* @__PURE__ */ c(
1512
+ "span",
1513
+ {
1514
+ style: {
1515
+ fontSize: 10,
1516
+ fontWeight: 600,
1517
+ maxWidth: "100%",
1518
+ overflow: "hidden",
1519
+ textOverflow: "ellipsis",
1520
+ whiteSpace: "nowrap"
1521
+ },
1522
+ children: r.name
1523
+ }
1524
+ )
1525
+ },
1526
+ r.id
1527
+ ))
1528
+ ]
1529
+ }
1530
+ );
1531
+ }
1532
+ function sn({
1533
+ onAddToCart: e,
1534
+ defaultQuantity: t
1535
+ }) {
1536
+ const { price: n, complete: r } = Yt();
1537
+ return /* @__PURE__ */ F(
1538
+ W.Buy,
1539
+ {
1540
+ onAddToCart: e,
1541
+ ...typeof t == "number" ? { defaultQuantity: t } : {},
1542
+ style: {
1543
+ display: "inline-flex",
1544
+ alignItems: "center",
1545
+ gap: 6,
1546
+ padding: "7px 16px",
1547
+ marginLeft: 4,
1548
+ borderRadius: 9999,
1549
+ border: "none",
1550
+ background: "var(--sc-color-foreground, #111)",
1551
+ color: "var(--sc-color-surface, #fff)",
1552
+ font: "inherit",
1553
+ fontSize: 12,
1554
+ fontWeight: 700,
1555
+ whiteSpace: "nowrap",
1556
+ cursor: r ? "pointer" : "not-allowed",
1557
+ opacity: r ? 1 : 0.5
1558
+ },
1559
+ children: [
1560
+ "Add to cart",
1561
+ typeof n == "number" ? ` · ${Zt(n)}` : ""
1562
+ ]
1563
+ }
1564
+ );
1565
+ }
1566
+ function on({
1567
+ title: e,
1568
+ height: t,
1569
+ mockupIds: n,
1570
+ onAddToCart: r,
1571
+ defaultQuantity: i,
1572
+ className: s,
1573
+ style: o
1574
+ }) {
1575
+ const [l, u] = T(!1), [a, h] = T(0), [p, b] = T(!1), k = O(null), d = vt(), v = n[Math.min(a, n.length - 1)], g = K(async () => {
1576
+ var V;
1577
+ const E = await ((V = k.current) == null ? void 0 : V.exportArtboards({ format: "dataUrl" }));
1578
+ if (!E) return;
1579
+ const [A, C] = Object.entries(E)[0] ?? [];
1580
+ if (typeof C != "string") return;
1581
+ const D = document.createElement("a");
1582
+ D.href = C, D.download = `${A ?? "design"}.png`, D.click();
1583
+ }, []), f = {
1584
+ display: "flex",
1585
+ flexDirection: "column",
1586
+ overflow: "hidden",
1587
+ background: "var(--sc-color-surface, #fff)",
1588
+ color: "var(--sc-color-foreground, #111)",
1589
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
1590
+ ...l ? {
1591
+ position: "fixed",
1592
+ inset: 0,
1593
+ zIndex: 9999,
1594
+ height: "100dvh",
1595
+ paddingBottom: "env(safe-area-inset-bottom)"
1596
+ } : {
1597
+ position: "relative",
1598
+ height: t,
1599
+ borderRadius: 16,
1600
+ border: "1px solid rgba(0,0,0,0.08)",
1601
+ boxShadow: "0 1px 3px rgba(0,0,0,0.08)"
1602
+ },
1603
+ ...o
1604
+ }, S = {
1605
+ position: "absolute",
1606
+ top: 8,
1607
+ right: 12,
1608
+ width: 96,
1609
+ aspectRatio: "2 / 3",
1610
+ borderRadius: 12,
1611
+ overflow: "hidden",
1612
+ border: "1px solid rgba(0,0,0,0.1)",
1613
+ boxShadow: "0 4px 12px rgba(0,0,0,0.12)",
1614
+ background: "var(--sc-color-surface, #fff)",
1615
+ padding: 0,
1616
+ cursor: "pointer",
1617
+ zIndex: 2
1618
+ };
1619
+ return /* @__PURE__ */ F(
1620
+ "div",
1621
+ {
1622
+ "data-sc-part": "snowcone-editor",
1623
+ "data-sc-fullscreen": l ? "true" : void 0,
1624
+ className: s,
1625
+ style: f,
1626
+ children: [
1627
+ /* @__PURE__ */ F(
1628
+ "div",
1629
+ {
1630
+ "data-sc-part": "snowcone-editor-header",
1631
+ style: {
1632
+ display: "flex",
1633
+ alignItems: "center",
1634
+ gap: 2,
1635
+ padding: "10px 12px 4px 16px",
1636
+ flexShrink: 0
1637
+ },
1638
+ children: [
1639
+ /* @__PURE__ */ c("span", { style: { fontSize: 14, fontWeight: 700, marginRight: "auto" }, children: e }),
1640
+ /* @__PURE__ */ c(W.History, {}),
1641
+ /* @__PURE__ */ c(
1642
+ nn,
1643
+ {
1644
+ fullscreen: l,
1645
+ onToggleFullscreen: () => u((E) => !E),
1646
+ viewCount: n.length,
1647
+ viewIndex: a,
1648
+ onSelectView: h,
1649
+ onExport: () => void g()
1650
+ }
1651
+ ),
1652
+ /* @__PURE__ */ c(sn, { onAddToCart: r, defaultQuantity: i })
1653
+ ]
1654
+ }
1655
+ ),
1656
+ /* @__PURE__ */ F(
1657
+ "div",
1658
+ {
1659
+ "data-sc-part": "snowcone-editor-stage",
1660
+ style: { position: "relative", flex: 1, minHeight: 0, padding: "4px 12px" },
1661
+ children: [
1662
+ /* @__PURE__ */ c(W.Canvas, { ref: k, style: { height: "100%" } }),
1663
+ p ? (
1664
+ // Swapped: the mockup IS the stage; the corner chip returns to the
1665
+ // artwork. The canvas stays mounted underneath (no document
1666
+ // teardown), just covered.
1667
+ /* @__PURE__ */ F(
1668
+ "div",
1669
+ {
1670
+ "data-sc-part": "snowcone-editor-swap-stage",
1671
+ style: {
1672
+ position: "absolute",
1673
+ inset: 0,
1674
+ zIndex: 1,
1675
+ display: "flex",
1676
+ alignItems: "center",
1677
+ justifyContent: "center",
1678
+ background: "var(--sc-color-surface, #fff)",
1679
+ padding: 8
1680
+ },
1681
+ children: [
1682
+ /* @__PURE__ */ c(
1683
+ W.Preview,
1684
+ {
1685
+ mockupId: v,
1686
+ alt: "Product mockup",
1687
+ loadingOverlay: !0,
1688
+ fallback: /* @__PURE__ */ c("div", { "aria-busy": "true", children: "Rendering…" }),
1689
+ style: { maxWidth: "100%", maxHeight: "100%", borderRadius: 12 }
1690
+ }
1691
+ ),
1692
+ /* @__PURE__ */ c(
1693
+ "button",
1694
+ {
1695
+ type: "button",
1696
+ "aria-label": "Back to artwork",
1697
+ "data-sc-part": "snowcone-editor-swap-back",
1698
+ onClick: () => b(!1),
1699
+ style: {
1700
+ ...S,
1701
+ display: "flex",
1702
+ alignItems: "center",
1703
+ justifyContent: "center",
1704
+ fontSize: 11,
1705
+ fontWeight: 600,
1706
+ color: "inherit",
1707
+ border: "1px dashed rgba(0,0,0,0.25)"
1708
+ },
1709
+ children: "artwork"
1710
+ }
1711
+ )
1712
+ ]
1713
+ }
1714
+ )
1715
+ ) : /* @__PURE__ */ c(
1716
+ "button",
1717
+ {
1718
+ type: "button",
1719
+ "aria-label": "Show product mockup",
1720
+ "data-sc-part": "snowcone-editor-pip",
1721
+ onClick: () => b(!0),
1722
+ style: S,
1723
+ children: /* @__PURE__ */ c(
1724
+ W.Preview,
1725
+ {
1726
+ mockupId: v,
1727
+ alt: "Product mockup preview",
1728
+ loadingOverlay: !0,
1729
+ fallback: /* @__PURE__ */ c("span", { style: { fontSize: 10, opacity: 0.5 }, "aria-busy": "true", children: "mockup…" }),
1730
+ style: { width: "100%", height: "100%", display: "block" }
1731
+ }
1732
+ )
1733
+ }
1734
+ )
1735
+ ]
1736
+ }
1737
+ ),
1738
+ /* @__PURE__ */ F(
1739
+ "div",
1740
+ {
1741
+ "data-sc-part": "snowcone-editor-zone",
1742
+ "data-sc-zone-state": d ? "selected" : "idle",
1743
+ style: { flexShrink: 0, padding: "4px 16px 14px" },
1744
+ children: [
1745
+ /* @__PURE__ */ c("div", { style: { display: d ? void 0 : "none" }, children: /* @__PURE__ */ c(W.Controls, { placement: "docked" }) }),
1746
+ d ? null : /* @__PURE__ */ c(rn, {})
1747
+ ]
1748
+ }
1749
+ )
1750
+ ]
1751
+ }
1752
+ );
1753
+ }
1754
+ function xn(e) {
1755
+ const {
1756
+ onAddToCart: t,
1757
+ defaultQuantity: n,
1758
+ title: r = "Editor",
1759
+ height: i = 480,
1760
+ className: s,
1761
+ style: o,
1762
+ ...l
1763
+ } = e;
1764
+ return /* @__PURE__ */ c(W.Root, { ...l, children: /* @__PURE__ */ c(
1765
+ on,
1766
+ {
1767
+ title: r,
1768
+ height: i,
1769
+ mockupIds: l.product.mockupIds,
1770
+ onAddToCart: t,
1771
+ ...typeof n == "number" ? { defaultQuantity: n } : {},
1772
+ className: s,
1773
+ style: o
1774
+ }
1775
+ ) });
1776
+ }
1777
+ tt();
1778
+ export {
1779
+ W as E,
1780
+ wn as R,
1781
+ xn as S,
1782
+ vn as a,
1783
+ En as b,
1784
+ bn as c,
1785
+ yn as d,
1786
+ wt as e,
1787
+ Ae as f,
1788
+ Zt as g,
1789
+ xt as h,
1790
+ _e as i,
1791
+ Bt as j,
1792
+ Yt as k,
1793
+ kn as l,
1794
+ Rn as p,
1795
+ ye as s,
1796
+ Q as u,
1797
+ St as v
1798
+ };
1799
+ //# sourceMappingURL=embed-CLz3CTcX.js.map