@snowcone-app/canvas 0.18.0 → 0.19.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 (32) hide show
  1. package/dist/{EditorCanvasImpl-Bv4yagew.js → EditorCanvasImpl-Cthm-KSe.js} +4 -4
  2. package/dist/{EditorCanvasImpl-Bv4yagew.js.map → EditorCanvasImpl-Cthm-KSe.js.map} +1 -1
  3. package/dist/EditorContext-5QpqAhjh.js.map +1 -1
  4. package/dist/{SnowconeCanvas-C_O-aeaQ.js → SnowconeCanvas-3PA17na3.js} +2 -2
  5. package/dist/{SnowconeCanvas-C_O-aeaQ.js.map → SnowconeCanvas-3PA17na3.js.map} +1 -1
  6. package/dist/api/embed.d.ts +2 -2
  7. package/dist/api/testing.d.ts +3 -0
  8. package/dist/components/editor/EditorBuy.d.ts +16 -5
  9. package/dist/components/editor/RootInternals.d.ts +5 -0
  10. package/dist/components/editor/commerceStore.d.ts +48 -0
  11. package/dist/components/editor/index.d.ts +3 -0
  12. package/dist/components/editor/useProductSelection.d.ts +33 -0
  13. package/dist/design-builder-v4935TeS.js +343 -0
  14. package/dist/design-builder-v4935TeS.js.map +1 -0
  15. package/dist/embed-D0HHwCIO.js +1093 -0
  16. package/dist/embed-D0HHwCIO.js.map +1 -0
  17. package/dist/embed.mjs +27 -25
  18. package/dist/embed.mjs.map +1 -1
  19. package/dist/errors/editorError.d.ts +15 -1
  20. package/dist/index.mjs +40 -39
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/template-fields-Dr3fMItw.js +329 -0
  23. package/dist/template-fields-Dr3fMItw.js.map +1 -0
  24. package/dist/testing/assertRenders.d.ts +53 -0
  25. package/dist/testing/failureFixtures.d.ts +25 -0
  26. package/dist/testing.mjs +191 -80
  27. package/dist/testing.mjs.map +1 -1
  28. package/package.json +3 -3
  29. package/dist/embed-Cdbjay9i.js +0 -961
  30. package/dist/embed-Cdbjay9i.js.map +0 -1
  31. package/dist/template-fields-DgoBtajp.js +0 -668
  32. package/dist/template-fields-DgoBtajp.js.map +0 -1
@@ -0,0 +1,1093 @@
1
+ import { C as Re, L as Oe, I as le, o as G, M as ke, D as Te, k as Ne, A as $e, l as _e, T as Ae, h as De, B as Ve, i as Le, S as Be, e as Ue } from "./MenuButton-CeoEAtzP.js";
2
+ import { y as O } from "./FontAnalyzer-DIlqmyuf.js";
3
+ import { jsx as c, jsxs as M, Fragment as Fe } from "react/jsx-runtime";
4
+ import { useContext as de, createContext as ue, useId as W, useLayoutEffect as P, useRef as x, useState as k, useMemo as R, useSyncExternalStore as T, useEffect as N, Component as qe, forwardRef as me, useCallback as Q } from "react";
5
+ import { assertVariantSelected as Me, toOptionAttributes as Pe, toCombinations as je, getMissingSelections as Ke, validateProductSelection as He, findBestCombination as ae, computeDisabledChoices as ze, RenderSession as Ge, createCartDetail as Qe } from "@snowcone-app/sdk";
6
+ import { g as E, i as We, h as he, B as Je, E as Ye, j as Xe } from "./EditorContext-5QpqAhjh.js";
7
+ import { u as Ze, p as et } from "./usePieceGuideBundle-DVOISsB1.js";
8
+ import { r as fe } from "./editorErrors-DXNfVXtD.js";
9
+ import { l as ce, s as ne, i as tt } from "./design-builder-v4935TeS.js";
10
+ import { U as nt } from "./template-fields-Dr3fMItw.js";
11
+ import "./CanvasStateV1-Bq48qhv5.js";
12
+ const pe = ue(void 0);
13
+ function J() {
14
+ const t = de(pe);
15
+ if (!t)
16
+ throw new Error(
17
+ "useRealtimeMockups / <RealtimeCanvas> / <RealtimeMockup> must be rendered inside <RealtimeEmbed>. Wrap them: <RealtimeEmbed shop=… product=… design=… grantUrl=…>…</RealtimeEmbed>"
18
+ );
19
+ return t;
20
+ }
21
+ const rt = "[RealtimeEmbed]";
22
+ function st(t) {
23
+ var s, r;
24
+ if ((s = t == null ? void 0 : t.requiredPlacements) != null && s.length)
25
+ return t.requiredPlacements.filter((i) => !i.autoFilledByVariant).map((i) => i.label);
26
+ const e = new Set(
27
+ (((r = t == null ? void 0 : t.options) == null ? void 0 : r.combinations) ?? []).map((i) => i.variantId).filter((i) => typeof i == "string" && i !== "")
28
+ );
29
+ return !!(t != null && t.variantId) || e.size > 1 ? [] : (t == null ? void 0 : t.placements) ?? [];
30
+ }
31
+ function ge({ shop: t, product: e, design: n, hasGrant: s }) {
32
+ var d, m;
33
+ const r = [];
34
+ if (t || r.push({
35
+ severity: "error",
36
+ issue: "missing-shop",
37
+ message: "`shop` is required (your publishable shop id)."
38
+ }), s || r.push({
39
+ severity: "error",
40
+ issue: "missing-grant",
41
+ message: "no grant source — pass `grantUrl` (a proxy to mintRealtimeGrant) or `grant`. Without it the session can never authorize and no mockup renders."
42
+ }), (d = e == null ? void 0 : e.mockupIds) != null && d.length || r.push({
43
+ severity: "error",
44
+ issue: "empty-mockup-ids",
45
+ message: 'product.mockupIds is empty — pass the catalog scene codes (product.mockups[].id) you want rendered, e.g. ["FV1qjO"].'
46
+ }), e != null && e.options)
47
+ try {
48
+ Me(e);
49
+ } catch (h) {
50
+ r.push({
51
+ severity: "error",
52
+ issue: "variant-required",
53
+ message: h instanceof Error ? h.message : String(h)
54
+ });
55
+ }
56
+ const i = n.toJSON(), a = i.activeArtboard, u = st(e);
57
+ u.length > 1 && r.push({
58
+ severity: "error",
59
+ issue: "multi-placement",
60
+ message: `product ${e.productId} requires placements [${u.join(", ")}] but this embed renders a single placement ("${a}"). The render will time out waiting for the others. This product needs the eject path — see https://developers.snowcone.app/embed-mode#eject.`
61
+ }), (m = e == null ? void 0 : e.placements) != null && m.length && !e.placements.includes(a) && r.push({
62
+ severity: "error",
63
+ issue: "placement-mismatch",
64
+ message: `the design's placement "${a}" 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 }).`
65
+ });
66
+ const l = i.elements.map((h) => h.name).filter((h) => !!h), o = l.filter((h, v) => l.indexOf(h) !== v);
67
+ return o.length && r.push({
68
+ severity: "warn",
69
+ issue: "duplicate-layer-names",
70
+ message: `duplicate layer name(s) [${[...new Set(o)].join(", ")}] — useTextBinding(name) / useImageBinding(name) bind to the FIRST match, so a duplicate name makes a control edit the wrong layer. Keep names unique.`
71
+ }), r;
72
+ }
73
+ function it(t) {
74
+ for (const e of ge(t))
75
+ (e.severity === "error" && e.issue !== "placement-mismatch" ? console.error : console.warn)(`${rt} ${e.message}`);
76
+ }
77
+ function Z(t) {
78
+ return t.text;
79
+ }
80
+ function q(t) {
81
+ return t.name;
82
+ }
83
+ class ot {
84
+ constructor(e) {
85
+ this.listeners = /* @__PURE__ */ new Set(), this.getState = () => this.state, this.subscribe = (s) => (this.listeners.add(s), () => this.listeners.delete(s)), this.state = e;
86
+ const n = /* @__PURE__ */ new Map();
87
+ for (const s of e.elements) {
88
+ const r = q(s), i = Z(s);
89
+ r && typeof i == "string" && n.set(r, i);
90
+ }
91
+ this.seedTextByName = n;
92
+ }
93
+ /** Replace the whole state (the canvas `onChange` path). */
94
+ setState(e) {
95
+ this.state = e, this.emit();
96
+ }
97
+ /** The seed value of a named text layer (for completeness rule C3). */
98
+ seedTextOf(e) {
99
+ return this.seedTextByName.get(e);
100
+ }
101
+ /** Current text of a named text layer, or undefined when absent. */
102
+ textOf(e) {
103
+ const n = this.state.elements.find((s) => q(s) === e);
104
+ return n ? Z(n) : void 0;
105
+ }
106
+ /** Kind of the named layer in the CURRENT state ('text' | 'image' | null
107
+ * for unbindable), or undefined when no layer has the name. */
108
+ layerKindOf(e) {
109
+ const n = this.state.elements.find((s) => q(s) === e);
110
+ return n ? ce(n) : void 0;
111
+ }
112
+ /** Names of bindable layers in the current state, for error messages. */
113
+ bindableLayerNames() {
114
+ return this.state.elements.filter((e) => ce(e) !== null).map(q).filter((e) => !!e);
115
+ }
116
+ /**
117
+ * Direct write path for `Editor.TextField` when NO canvas is mounted (with
118
+ * one, writes route through the editor command path and arrive back here
119
+ * via `setState`). Immutable update; last-write-wins.
120
+ */
121
+ updateTextLayer(e, n) {
122
+ let s = !1;
123
+ const r = this.state.elements.map((i) => q(i) !== e ? i : (s = !0, { ...i, text: n }));
124
+ if (!s)
125
+ throw new E(
126
+ "E_LAYER_NOT_FOUND",
127
+ `no layer "${e}" in the current design state. Bindable layers: ${JSON.stringify(this.bindableLayerNames())}.`,
128
+ { layerName: e, validNames: this.bindableLayerNames(), docSlug: "embed-mode" }
129
+ );
130
+ this.setState({ ...this.state, elements: r });
131
+ }
132
+ /** ≥1 element with visible content (completeness rule C4): non-empty text,
133
+ * an image with a URL, or a shape. Hidden elements don't count. */
134
+ hasRenderableElement() {
135
+ return this.state.elements.some((e) => {
136
+ if (e.visible === !1) return !1;
137
+ const n = e.transformType;
138
+ if (n === "image") return !!e.imageUrl;
139
+ if (n === "shape" || n === "group" || n === "path") return !0;
140
+ const s = Z(e);
141
+ return typeof s == "string" && s.trim().length > 0;
142
+ });
143
+ }
144
+ emit() {
145
+ for (const e of [...this.listeners]) e();
146
+ }
147
+ }
148
+ function te(t) {
149
+ return (t.choices ?? []).map((e) => typeof e == "string" ? e : e.label ?? "").filter((e) => e !== "");
150
+ }
151
+ function at(t, e) {
152
+ if (!t.variantId) return {};
153
+ const n = e.find((r) => r.variantId === t.variantId);
154
+ if (!n) return {};
155
+ const s = {};
156
+ for (const [r, i] of Object.entries(n))
157
+ r === "variantId" || r === "price" || (typeof i == "string" ? s[r] = i : typeof i == "number" && (s[r] = String(i)));
158
+ return s;
159
+ }
160
+ class ct {
161
+ constructor(e) {
162
+ this.quantity = 1, this.version = 0, this.listeners = /* @__PURE__ */ new Set(), this.getVersion = () => this.version, this.subscribe = (n) => (this.listeners.add(n), () => this.listeners.delete(n)), this.product = e, this.attrs = Pe({ options: e.options }), this.combos = je({ options: e.options }), this.selection = at(e, this.combos);
163
+ }
164
+ getAttributes() {
165
+ return this.attrs;
166
+ }
167
+ getSelection() {
168
+ return this.selection;
169
+ }
170
+ getQuantity() {
171
+ return this.quantity;
172
+ }
173
+ /**
174
+ * Select a choice for a variant attribute. Strict + loud (D4/E3): an
175
+ * attribute key or choice that doesn't exist on the product is a wiring
176
+ * mistake, not user state — throws `E_UNKNOWN_OPTION` listing what IS valid.
177
+ */
178
+ select(e, n) {
179
+ const s = this.attrs[e];
180
+ if (!s)
181
+ throw new E(
182
+ "E_UNKNOWN_OPTION",
183
+ `product ${this.product.productId} has no option "${e}". Valid option keys: ${JSON.stringify(Object.keys(this.attrs))}.`,
184
+ { key: e, validKeys: Object.keys(this.attrs), docSlug: "embed-mode" }
185
+ );
186
+ const r = te(s);
187
+ if (r.length > 0 && !r.includes(n))
188
+ throw new E(
189
+ "E_UNKNOWN_OPTION",
190
+ `"${n}" is not a choice of option "${e}" on product ${this.product.productId}. Valid choices: ${JSON.stringify(r)}.`,
191
+ { key: e, choice: n, validChoices: r, docSlug: "embed-mode" }
192
+ );
193
+ this.selection[e] !== n && (this.selection = { ...this.selection, [e]: n }, this.bump());
194
+ }
195
+ /** Quantity is USER state (completeness rule C5 gates on it) — never throws. */
196
+ setQuantity(e) {
197
+ this.quantity !== e && (this.quantity = e, this.bump());
198
+ }
199
+ /** Attribute keys still required (non-optional) but unselected — Buy's
200
+ * `option:<key>` reason codes. */
201
+ missingOptionKeys() {
202
+ return Object.keys(this.attrs).length === 0 ? [] : Ke(this.attrs, this.selection);
203
+ }
204
+ /** Full selection resolution (the C1 amendment): true when every required
205
+ * attribute is selected AND the selection matches a valid combination. */
206
+ selectionValid() {
207
+ return Object.keys(this.attrs).length === 0 ? !0 : He(this.attrs, this.selection, this.combos);
208
+ }
209
+ /** The variant (gvid) the current selection resolves to. Falls back to the
210
+ * product's initial `variantId` while nothing has been selected. */
211
+ resolvedVariantId() {
212
+ if (Object.keys(this.attrs).length === 0 || this.combos.length === 0)
213
+ return this.product.variantId;
214
+ if (!this.selectionValid()) return;
215
+ const e = ae(this.selection, this.combos, this.attrs);
216
+ return (typeof (e == null ? void 0 : e.variantId) == "string" && e.variantId !== "" ? e.variantId : void 0) ?? this.product.variantId;
217
+ }
218
+ /** Price of the resolved combination, when the catalog carries one. */
219
+ resolvedPrice() {
220
+ if (!this.selectionValid()) return;
221
+ const e = ae(this.selection, this.combos, this.attrs);
222
+ return typeof (e == null ? void 0 : e.price) == "number" ? e.price : void 0;
223
+ }
224
+ /** Choices invalid given the rest of the current selection — for hosts to
225
+ * disable/grey out in their variant UI. */
226
+ disabledChoices() {
227
+ const e = {};
228
+ for (const [n, s] of Object.entries(this.attrs))
229
+ e[n] = { ...s, choices: te(s) };
230
+ return ze(this.selection, e, this.combos);
231
+ }
232
+ /** The combinations (for payload assembly via `createCartDetail`). */
233
+ getCombinations() {
234
+ return this.combos;
235
+ }
236
+ bump() {
237
+ this.version++;
238
+ for (const e of [...this.listeners]) e();
239
+ }
240
+ }
241
+ class lt {
242
+ constructor() {
243
+ this.canvasIds = /* @__PURE__ */ new Set(), this.requirements = /* @__PURE__ */ new Map(), this.textFields = /* @__PURE__ */ new Map(), this.controls = /* @__PURE__ */ new Map(), this.bridge = null, this.listeners = /* @__PURE__ */ new Set(), this.version = 0, this.getVersion = () => this.version, this.subscribe = (e) => (this.listeners.add(e), () => this.listeners.delete(e));
244
+ }
245
+ registerCanvas(e) {
246
+ return this.canvasIds.add(e), this.bump(), () => {
247
+ this.canvasIds.delete(e), this.bump();
248
+ };
249
+ }
250
+ hasCanvas() {
251
+ return this.canvasIds.size > 0;
252
+ }
253
+ /** Declare that a part is meaningless without a mounted Canvas (ADR-0085 §4
254
+ * table). Validated by Root once per commit. */
255
+ requireCanvas(e, n) {
256
+ return this.requirements.set(e, n), this.bump(), () => {
257
+ this.requirements.delete(e), this.bump();
258
+ };
259
+ }
260
+ /** Run by Root during RENDER, re-triggered by every registry change — by
261
+ * then the whole commit's registrations are in, so there is no race
262
+ * against the lazy chunk (ADR-0085 §4). Throws land in Root's boundary. */
263
+ validate() {
264
+ if (this.canvasIds.size > 1)
265
+ throw new E(
266
+ "E_DUPLICATE_CANVAS",
267
+ "two <Editor.Canvas> parts are mounted under one <Editor.Root>. A Root owns exactly one editable artboard — remove one Canvas, or mount a second <Editor.Root> for a second editor. (Multiple <Editor.Preview> parts ARE valid.)",
268
+ { docSlug: "embed-mode" }
269
+ );
270
+ if (this.requirements.size > 0 && !this.hasCanvas()) {
271
+ const e = [...new Set(this.requirements.values())];
272
+ throw new E(
273
+ "E_REQUIRES_CANVAS",
274
+ `${e.join(", ")} require${e.length === 1 ? "s" : ""} an <Editor.Canvas> under the same <Editor.Root> — they operate on the editable artboard (selection, editor commands). Mount <Editor.Canvas/>, or remove the part. Canvas-free parts: Editor.Preview, Editor.Buy, Editor.TextField, Editor.Layers.`,
275
+ { parts: e, docSlug: "embed-mode" }
276
+ );
277
+ }
278
+ }
279
+ registerTextField(e, n) {
280
+ return this.textFields.set(e, n), this.bump(), () => {
281
+ this.textFields.delete(e), this.bump();
282
+ };
283
+ }
284
+ /** Mounted TextField registrations — Buy's required-layer list (C2/C3).
285
+ * Unmounted (incl. conditionally hidden) fields are absent by construction. */
286
+ getTextFields() {
287
+ return [...this.textFields.values()];
288
+ }
289
+ /** Bound-layer lookup for the mutation guard (ADR-0085 §3 /
290
+ * `E_BOUND_LAYER_MUTATION`): a layer is bound while ANY mounted
291
+ * `Editor.TextField` carries its name. Satisfies `BoundLayerGuard`
292
+ * structurally (subscribe/getVersion above). */
293
+ getBoundLayer(e) {
294
+ for (const n of this.textFields.values())
295
+ if (n.name === e) return { part: "Editor.TextField" };
296
+ return null;
297
+ }
298
+ /** `Editor.Controls` registration (ADR-0085 §5): a mounted DOCKED Controls
299
+ * suppresses the canvas's built-in floating contextual toolbar — one
300
+ * contextual zone at a time. `placement="floating"` registers without
301
+ * suppressing (it declares today's default explicitly). */
302
+ registerControls(e, n) {
303
+ return this.controls.set(e, n), this.bump(), () => {
304
+ this.controls.delete(e), this.bump();
305
+ };
306
+ }
307
+ hasDockedControls() {
308
+ for (const e of this.controls.values())
309
+ if (e === "docked") return !0;
310
+ return !1;
311
+ }
312
+ registerCanvasBridge(e) {
313
+ return this.bridge = e, this.bump(), () => {
314
+ this.bridge === e && (this.bridge = null), this.bump();
315
+ };
316
+ }
317
+ getCanvasBridge() {
318
+ return this.bridge;
319
+ }
320
+ bump() {
321
+ this.version++;
322
+ for (const e of [...this.listeners]) e();
323
+ }
324
+ }
325
+ const ye = ue(null);
326
+ function $(t) {
327
+ const e = de(ye);
328
+ if (!e)
329
+ throw new E(
330
+ "E_OUTSIDE_ROOT",
331
+ `<${t}> must be rendered inside <Editor.Root>. Wrap it: <Editor.Root shop={…} product={…} design={…} grantUrl={…}><${t}/></Editor.Root>`,
332
+ { part: t, docSlug: "embed-mode" }
333
+ );
334
+ return e;
335
+ }
336
+ function Y(t) {
337
+ const e = $(t), n = W();
338
+ P(
339
+ () => e.registry.requireCanvas(n, t),
340
+ [e.registry, n, t]
341
+ );
342
+ }
343
+ function dt(t) {
344
+ return {
345
+ reportInvariant(e) {
346
+ if (We()) throw e;
347
+ t(e);
348
+ },
349
+ reportRuntimeError(e) {
350
+ fe(e), t(e);
351
+ }
352
+ };
353
+ }
354
+ const ve = 8e3;
355
+ function ut(t) {
356
+ return `[RealtimeEmbed] No mockup after ${Math.round(ve / 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.`;
357
+ }
358
+ function mt(t) {
359
+ if ("grantUrl" in t && t.grantUrl)
360
+ return { grantUrl: t.grantUrl };
361
+ const e = t.grant;
362
+ return typeof e == "function" ? { getToken: e } : { getToken: () => Promise.resolve(e) };
363
+ }
364
+ function be({ error: t, onRetry: e }) {
365
+ const n = he(t) ? t.code : "EDITOR_ERROR";
366
+ return /* @__PURE__ */ M(
367
+ "div",
368
+ {
369
+ "data-sc-part": "error",
370
+ role: "alert",
371
+ style: {
372
+ padding: "16px",
373
+ borderRadius: "12px",
374
+ border: "1px solid var(--sc-color-destructive, #b91c1c)",
375
+ background: "var(--sc-color-surface, #fef2f2)",
376
+ color: "var(--sc-color-foreground, #7f1d1d)",
377
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
378
+ fontSize: "13px",
379
+ lineHeight: 1.5
380
+ },
381
+ children: [
382
+ /* @__PURE__ */ M("p", { style: { margin: 0, fontWeight: 700 }, children: [
383
+ "Editor failed: ",
384
+ n
385
+ ] }),
386
+ /* @__PURE__ */ c("p", { style: { margin: "4px 0 0" }, children: t.message }),
387
+ e ? /* @__PURE__ */ c(
388
+ "button",
389
+ {
390
+ type: "button",
391
+ "data-sc-part": "error-retry",
392
+ onClick: e,
393
+ style: {
394
+ marginTop: "10px",
395
+ padding: "6px 14px",
396
+ borderRadius: "9999px",
397
+ border: "1px solid currentColor",
398
+ background: "transparent",
399
+ color: "inherit",
400
+ font: "inherit",
401
+ fontWeight: 600,
402
+ cursor: "pointer"
403
+ },
404
+ children: "Retry"
405
+ }
406
+ ) : null
407
+ ]
408
+ }
409
+ );
410
+ }
411
+ class ht extends qe {
412
+ constructor() {
413
+ super(...arguments), this.state = { caught: null };
414
+ }
415
+ static getDerivedStateFromError(e) {
416
+ return { caught: e };
417
+ }
418
+ componentDidCatch(e) {
419
+ var n;
420
+ fe(e), typeof O < "u" && ((n = O.env) == null ? void 0 : n.NODE_ENV) !== "production" && console.error("[Editor.Root] contained a composition error:", e);
421
+ }
422
+ render() {
423
+ return this.state.caught ? /* @__PURE__ */ c(be, { error: this.state.caught }) : this.props.children;
424
+ }
425
+ }
426
+ function ft({ reportedError: t, onRetry: e, children: n }) {
427
+ if (t) {
428
+ const s = he(t) && t.code === "E_CHUNK_LOAD_FAILED";
429
+ return /* @__PURE__ */ c(be, { error: t, onRetry: s ? e : void 0 });
430
+ }
431
+ return /* @__PURE__ */ c(ht, { reportedError: null, onRetry: e, children: n });
432
+ }
433
+ function pt(t) {
434
+ const {
435
+ shop: e,
436
+ product: n,
437
+ design: s,
438
+ throttleMs: r = 200,
439
+ onMockups: i,
440
+ onRenderError: a,
441
+ wsUrl: u,
442
+ services: l,
443
+ children: o,
444
+ internals: d,
445
+ __validation: m = "throw"
446
+ } = t, h = R(() => s.toJSON().activeArtboard, [s]);
447
+ T(
448
+ d.commerce.subscribe,
449
+ d.commerce.getVersion,
450
+ d.commerce.getVersion
451
+ );
452
+ const v = d.commerce.resolvedVariantId(), p = Ze(v ?? n.variantId ?? n.defaultGvid), C = R(() => {
453
+ if (!p) return null;
454
+ const f = s.toJSON().artboards[0];
455
+ return et(p, f) ?? null;
456
+ }, [p, s]), S = x(i), b = x(a);
457
+ S.current = i, b.current = a;
458
+ const [_, A] = k([]), [D, w] = k(!0), [U, V] = k(null), L = !!("grantUrl" in t && t.grantUrl || "grant" in t && t.grant), F = mt(t), Ie = "grantUrl" in F ? F.grantUrl : "getToken", re = `${n.productId}|${n.mockupIds.join(",")}|${n.variantId ?? ""}`, j = `${e}|${re}|${h}|${L}`;
459
+ R(() => {
460
+ var I;
461
+ if (m !== "throw") return;
462
+ const f = ge({ shop: e, product: n, design: s, hasGrant: L }), g = f.find((B) => B.severity === "error");
463
+ if (g)
464
+ throw new E("E_EMBED_CONFIG", g.message, {
465
+ issue: g.issue,
466
+ docSlug: "embed-mode"
467
+ });
468
+ for (const B of f)
469
+ typeof O < "u" && ((I = O.env) == null ? void 0 : I.NODE_ENV) !== "production" && console.warn(`[Editor.Root] ${B.message}`);
470
+ }, [j, m]);
471
+ const se = x(null);
472
+ N(() => {
473
+ var f;
474
+ if (m === "warn" && typeof O < "u" && ((f = O.env) == null ? void 0 : f.NODE_ENV) !== "production") {
475
+ if (se.current === j) return;
476
+ se.current = j, it({ shop: e, product: n, design: s, hasGrant: L });
477
+ }
478
+ }, [j, m]);
479
+ const K = R(
480
+ () => {
481
+ try {
482
+ return { session: new Ge({ shop: e, product: n, wsUrl: u, ...F }) };
483
+ } catch (f) {
484
+ return { error: f instanceof Error ? f.message : String(f) };
485
+ }
486
+ },
487
+ // eslint-disable-next-line react-hooks/exhaustive-deps
488
+ [e, re, h, u, Ie]
489
+ ), y = "session" in K ? K.session : null, H = "error" in K ? K.error : null, z = R(() => (f, g = r) => {
490
+ y && (V(null), w(!0), y.renderState(h, f, g).catch((I) => {
491
+ var oe;
492
+ const B = I instanceof Error ? I.message : String(I);
493
+ V(B), w(!1), (oe = b.current) == null || oe.call(b, B);
494
+ }));
495
+ }, [y, h, r]);
496
+ N(() => {
497
+ var f;
498
+ if (H) {
499
+ V(H), w(!1), (f = b.current) == null || f.call(b, H);
500
+ return;
501
+ }
502
+ if (y)
503
+ return y.onMockups((g) => {
504
+ var I;
505
+ A(g), w(!1), (I = S.current) == null || I.call(S, g);
506
+ }), y.onError((g) => {
507
+ var I;
508
+ V(g), w(!1), (I = b.current) == null || I.call(b, g);
509
+ }), z(s.toServerRequest()), () => y.close();
510
+ }, [y, H]);
511
+ const X = x({
512
+ session: null,
513
+ variantId: void 0
514
+ });
515
+ N(() => {
516
+ if (!y || !v) return;
517
+ const f = X.current, g = f.session === y ? f.variantId : n.variantId;
518
+ if (v === g) {
519
+ X.current = { session: y, variantId: g };
520
+ return;
521
+ }
522
+ X.current = { session: y, variantId: v }, y.setProduct({ ...n, variantId: v }), z(ne(d.store.getState()));
523
+ }, [y, v]), N(() => {
524
+ var g;
525
+ if (!D || typeof O > "u" || ((g = O.env) == null ? void 0 : g.NODE_ENV) === "production") return;
526
+ const f = setTimeout(() => console.warn(ut(h)), ve);
527
+ return () => clearTimeout(f);
528
+ }, [D, h]);
529
+ const Ce = R(
530
+ () => ({
531
+ session: y,
532
+ design: s,
533
+ placement: h,
534
+ product: n,
535
+ mockups: _,
536
+ isRendering: D,
537
+ error: U,
538
+ throttleMs: r,
539
+ pieceGuides: C,
540
+ pushRender: z,
541
+ stateStore: d.store
542
+ }),
543
+ [y, s, h, n, _, D, U, r, C, z, d.store]
544
+ ), xe = T(
545
+ d.registry.subscribe,
546
+ d.registry.getVersion,
547
+ d.registry.getVersion
548
+ );
549
+ R(() => d.registry.validate(), [d.registry, xe]);
550
+ const ie = /* @__PURE__ */ c(ye.Provider, { value: d, children: /* @__PURE__ */ c(pe.Provider, { value: Ce, children: /* @__PURE__ */ c(Je.Provider, { value: d.registry, children: /* @__PURE__ */ c(Ye, { children: o }) }) }) });
551
+ return l ? /* @__PURE__ */ c(Re, { services: l, children: ie }) : ie;
552
+ }
553
+ function Ee(t) {
554
+ const { design: e, product: n } = t, s = `${n.productId}|${n.variantId ?? ""}`, r = x(null), i = x(null), [a, u] = k(null);
555
+ (r.current === null || r.current.design !== e) && (r.current = {
556
+ design: e,
557
+ internals: {
558
+ store: new ot(e.toJSON()),
559
+ registry: new lt()
560
+ }
561
+ }), (i.current === null || i.current.productIdentity !== s) && (i.current = { productIdentity: s, commerce: new ct(n) });
562
+ const l = R(
563
+ () => ({
564
+ ...r.current.internals,
565
+ commerce: i.current.commerce,
566
+ ...dt(u)
567
+ }),
568
+ // eslint-disable-next-line react-hooks/exhaustive-deps
569
+ [r.current.internals, i.current.commerce]
570
+ );
571
+ return (
572
+ // `display: contents` — the .sc-root scope without a layout box, so the
573
+ // host's grid/flex sees the parts directly (and the legacy RealtimeEmbed
574
+ // wrapper, which had no DOM at all, stays layout-identical).
575
+ /* @__PURE__ */ c("div", { className: "sc-root", style: { display: "contents" }, "data-sc-part": "root", children: /* @__PURE__ */ c(ft, { reportedError: a, onRetry: () => u(null), children: /* @__PURE__ */ c(pt, { ...t, internals: l }) }) })
576
+ );
577
+ }
578
+ let gt = () => import("./EditorCanvasImpl-Cthm-KSe.js").then((t) => t.EditorCanvasImpl);
579
+ function Se() {
580
+ return gt();
581
+ }
582
+ const yt = me(function({ children: e, maxHeight: n, className: s, style: r }, i) {
583
+ const a = $("Editor.Canvas"), u = W();
584
+ P(() => a.registry.registerCanvas(u), [a.registry, u]);
585
+ const [l, o] = k(null);
586
+ return N(() => {
587
+ let d = !1;
588
+ return Se().then(
589
+ (m) => {
590
+ d || o(() => m);
591
+ },
592
+ (m) => {
593
+ d || a.reportRuntimeError(
594
+ new E(
595
+ "E_CHUNK_LOAD_FAILED",
596
+ `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.`,
597
+ { cause: m instanceof Error ? m.message : String(m), docSlug: "embed-mode" }
598
+ )
599
+ );
600
+ }
601
+ ), () => {
602
+ d = !0;
603
+ };
604
+ }, [a]), l ? (
605
+ // `builtinControls`: the canvas owns the floating contextual toolbar
606
+ // unless a docked <Editor.Controls> is mounted (ADR-0085 §5). Only here —
607
+ // the legacy <RealtimeCanvas> wrapper stays chrome-less.
608
+ /* @__PURE__ */ c(l, { ref: i, kit: "embed-only", partAttr: "canvas", builtinControls: !0, maxHeight: n, className: s, style: r, children: e })
609
+ ) : /* @__PURE__ */ c(
610
+ "div",
611
+ {
612
+ "data-sc-part": "canvas",
613
+ "data-sc-state": "loading",
614
+ className: s,
615
+ style: { minHeight: n ?? 200, ...r },
616
+ "aria-busy": "true"
617
+ }
618
+ );
619
+ }), vt = 1.2, ee = [1200, 1400, 1600, 1800, 2e3, 2500, 3e3, 4e3];
620
+ function bt(t) {
621
+ for (const e of ee)
622
+ if (e >= t) return e;
623
+ return ee[ee.length - 1];
624
+ }
625
+ function Et(t, e) {
626
+ if (!(t > 0) || !(e > 0) || e <= t * vt) return null;
627
+ const n = bt(e);
628
+ 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.`;
629
+ }
630
+ function we({
631
+ mockupId: t,
632
+ fallback: e = null,
633
+ alt: n = "Live product mockup",
634
+ className: s,
635
+ style: r
636
+ }) {
637
+ const { mockups: i, error: a } = J(), u = x(/* @__PURE__ */ new Set()), l = t ? i.find((d) => d.mockupId === t && d.imageUrl) : i.find((d) => d.imageUrl);
638
+ if (!(l != null && l.imageUrl))
639
+ return a ? /* @__PURE__ */ c("div", { "data-sc-part": "preview-error", role: "alert", "data-sc-error": a, className: s, style: r, children: e ?? `Render failed: ${a}` }) : /* @__PURE__ */ c(Fe, { children: e });
640
+ const o = (d) => {
641
+ var C;
642
+ if (typeof O > "u" || ((C = O.env) == null ? void 0 : C.NODE_ENV) === "production" || typeof window > "u" || u.current.has(l.mockupId)) return;
643
+ const m = d.currentTarget, h = m.getBoundingClientRect().width;
644
+ if (h <= 0) return;
645
+ const v = Math.round(h * (window.devicePixelRatio || 1)), p = Et(m.naturalWidth, v);
646
+ p && (u.current.add(l.mockupId), console.warn(p));
647
+ };
648
+ return (
649
+ // eslint-disable-next-line @next/next/no-img-element
650
+ /* @__PURE__ */ c(
651
+ "img",
652
+ {
653
+ "data-sc-part": "preview",
654
+ src: l.imageUrl,
655
+ alt: n,
656
+ className: s,
657
+ style: r,
658
+ onLoad: o
659
+ }
660
+ )
661
+ );
662
+ }
663
+ function St(t) {
664
+ const { layer: e, optional: n = !1, placeholder: s, className: r, style: i } = t, a = $("Editor.TextField"), { pushRender: u, throttleMs: l, stateStore: o } = J(), d = W(), m = x(null);
665
+ if (!tt(e))
666
+ throw new E(
667
+ "E_LAYER_TOKEN_REQUIRED",
668
+ `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.`,
669
+ { received: typeof e == "string" ? e : typeof e, docSlug: "embed-mode" }
670
+ );
671
+ if (m.current !== e.name) {
672
+ const p = o.layerKindOf(e.name);
673
+ if (p === void 0)
674
+ throw new E(
675
+ "E_LAYER_NOT_FOUND",
676
+ `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(o.bindableLayerNames())}.`,
677
+ { layerName: e.name, validNames: o.bindableLayerNames(), docSlug: "embed-mode" }
678
+ );
679
+ if (p !== "text")
680
+ throw new E(
681
+ "E_LAYER_WRONG_KIND",
682
+ `layer "${e.name}" is ${p === null ? "not a bindable kind" : `an ${p} layer`} — <Editor.TextField> binds text layers only.`,
683
+ { layerName: e.name, actualKind: p, docSlug: "embed-mode" }
684
+ );
685
+ m.current = e.name;
686
+ }
687
+ P(() => a.registry.registerTextField(d, { name: e.name, optional: n }), [a.registry, d, e.name, n]);
688
+ const h = T(
689
+ o.subscribe,
690
+ () => o.textOf(e.name) ?? "",
691
+ () => o.textOf(e.name) ?? ""
692
+ ), v = Q(
693
+ (p) => {
694
+ const C = p.target.value, S = a.registry.getCanvasBridge();
695
+ if (S) {
696
+ S.setTextByName(e.name, C) || a.reportInvariant(
697
+ new E(
698
+ "E_LAYER_NOT_FOUND",
699
+ `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).`,
700
+ { layerName: e.name, docSlug: "embed-mode" }
701
+ )
702
+ );
703
+ return;
704
+ }
705
+ o.updateTextLayer(e.name, C), u(ne(o.getState()), l);
706
+ },
707
+ [a, e.name, o, u, l]
708
+ );
709
+ return /* @__PURE__ */ c(
710
+ "input",
711
+ {
712
+ "data-sc-part": "textfield",
713
+ "data-sc-layer": e.name,
714
+ type: "text",
715
+ value: h,
716
+ onChange: v,
717
+ placeholder: s,
718
+ "aria-label": t["aria-label"] ?? e.name,
719
+ className: r,
720
+ style: i
721
+ }
722
+ );
723
+ }
724
+ function wt(t) {
725
+ const e = [];
726
+ for (const n of t.missingOptionKeys)
727
+ e.push(`option:${n}`);
728
+ t.missingOptionKeys.length === 0 && !t.selectionValid && e.push("variant");
729
+ for (const n of t.textFields) {
730
+ if (n.optional) continue;
731
+ const s = (t.textOf(n.name) ?? "").trim();
732
+ if (s.length === 0) {
733
+ e.push(`layer:${n.name}`);
734
+ continue;
735
+ }
736
+ const r = (t.seedTextOf(n.name) ?? "").trim();
737
+ r.length > 0 && s === r && e.push(`layer-seed:${n.name}`);
738
+ }
739
+ return t.hasRenderableElement || e.push("empty-design"), (!Number.isInteger(t.quantity) || t.quantity < 1) && e.push("quantity"), e;
740
+ }
741
+ function It(t, e) {
742
+ const n = e.getCombinations(), s = e.getAttributes();
743
+ if (Object.keys(s).length === 0 || n.length === 0) return null;
744
+ const r = Object.fromEntries(
745
+ Object.entries(s).map(([i, a]) => [
746
+ i,
747
+ { ...a, choices: te(a) }
748
+ ])
749
+ );
750
+ return Qe({
751
+ productId: t.productId,
752
+ quantity: e.getQuantity(),
753
+ selection: e.getSelection(),
754
+ combinations: n,
755
+ attributes: r
756
+ });
757
+ }
758
+ function Ct(t) {
759
+ const { onAddToCart: e, defaultQuantity: n, children: s, className: r, style: i } = t, a = $("Editor.Buy"), { product: u, stateStore: l } = J(), o = a.commerce;
760
+ if (!(u != null && u.productId))
761
+ throw new E(
762
+ "E_BUY_NO_PRODUCT",
763
+ "<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.",
764
+ { docSlug: "embed-mode" }
765
+ );
766
+ const d = x(!1);
767
+ P(() => {
768
+ d.current || (d.current = !0, typeof n == "number" && o.setQuantity(n));
769
+ }, [o]);
770
+ const m = T(l.subscribe, l.getState, l.getState), h = T(o.subscribe, o.getVersion, o.getVersion), v = T(
771
+ a.registry.subscribe,
772
+ a.registry.getVersion,
773
+ a.registry.getVersion
774
+ ), p = o.getQuantity(), C = R(
775
+ () => wt({
776
+ quantity: p,
777
+ missingOptionKeys: o.missingOptionKeys(),
778
+ selectionValid: o.selectionValid(),
779
+ textFields: a.registry.getTextFields(),
780
+ textOf: (w) => l.textOf(w),
781
+ seedTextOf: (w) => l.seedTextOf(w),
782
+ hasRenderableElement: l.hasRenderableElement()
783
+ }),
784
+ // eslint-disable-next-line react-hooks/exhaustive-deps
785
+ [o, p, m, h, v]
786
+ ), S = C.length === 0, [b, _] = k(!1), A = x(!1), D = Q(() => {
787
+ if (A.current || !S) return;
788
+ A.current = !0, _(!0);
789
+ const w = l.getState(), U = o.resolvedVariantId(), V = {
790
+ productId: u.productId,
791
+ ...U ? { variantId: U } : {},
792
+ quantity: o.getQuantity(),
793
+ selection: o.getSelection(),
794
+ cartDetail: It(u, o),
795
+ designState: w,
796
+ serverRequest: ne(w)
797
+ };
798
+ let L;
799
+ try {
800
+ L = e(V);
801
+ } catch (F) {
802
+ throw A.current = !1, _(!1), F;
803
+ }
804
+ Promise.resolve(L).finally(() => {
805
+ A.current = !1, _(!1);
806
+ });
807
+ }, [S, u, o, l, e]);
808
+ return /* @__PURE__ */ c(
809
+ "button",
810
+ {
811
+ type: "button",
812
+ "data-sc-part": "buy",
813
+ "data-sc-buy-state": b ? "busy" : S ? "ready" : "incomplete",
814
+ "data-sc-buy-missing": C.join(",") || void 0,
815
+ disabled: !S || b,
816
+ "aria-busy": b || void 0,
817
+ onClick: D,
818
+ className: r,
819
+ style: i,
820
+ children: s ?? "Add to cart"
821
+ }
822
+ );
823
+ }
824
+ function xt(t) {
825
+ const { elements: e } = t.getState();
826
+ return e.map((n, s) => {
827
+ const r = n, i = r.transformType ?? "custom", a = r.name ?? i.charAt(0).toUpperCase() + i.slice(1);
828
+ return {
829
+ key: `${s}:${r.name ?? ""}`,
830
+ name: a,
831
+ kind: i,
832
+ visible: r.visible !== !1
833
+ };
834
+ });
835
+ }
836
+ function Rt(t) {
837
+ const e = x(null), n = () => {
838
+ var i;
839
+ const s = xt(t), r = s.map((a) => `${a.key}|${a.kind}|${a.visible}`).join(`
840
+ `);
841
+ return ((i = e.current) == null ? void 0 : i.key) !== r && (e.current = { key: r, rows: s }), e.current.rows;
842
+ };
843
+ return T(t.subscribe, n, n);
844
+ }
845
+ function Ot(t) {
846
+ return t === "image" ? G.image : t === "shape" ? G.shapes : t === "group" ? "lucide:folder-open" : G.text;
847
+ }
848
+ function kt({
849
+ store: t,
850
+ direction: e
851
+ }) {
852
+ const s = [...Rt(t)].reverse();
853
+ return /* @__PURE__ */ c(
854
+ "div",
855
+ {
856
+ style: {
857
+ display: "flex",
858
+ flexDirection: e === "horizontal" ? "row" : "column",
859
+ gap: 4
860
+ },
861
+ children: s.length === 0 ? /* @__PURE__ */ c("div", { style: { padding: 12, fontSize: 12, color: "var(--text-secondary, #999)" }, children: "No layers" }) : s.map((r) => /* @__PURE__ */ M(
862
+ "div",
863
+ {
864
+ "data-sc-part": "layers-item",
865
+ "data-sc-layer-kind": r.kind,
866
+ "data-sc-hidden": r.visible ? void 0 : "true",
867
+ style: {
868
+ display: "flex",
869
+ alignItems: "center",
870
+ gap: 8,
871
+ padding: "6px 8px",
872
+ minHeight: 36,
873
+ opacity: r.visible ? 1 : 0.4
874
+ },
875
+ children: [
876
+ /* @__PURE__ */ c(le, { icon: Ot(r.kind), style: { fontSize: 18, color: "var(--text-secondary, #888)", flexShrink: 0 } }),
877
+ /* @__PURE__ */ c(
878
+ "span",
879
+ {
880
+ style: {
881
+ fontSize: 13,
882
+ color: "var(--foreground, #333)",
883
+ overflow: "hidden",
884
+ textOverflow: "ellipsis",
885
+ whiteSpace: "nowrap"
886
+ },
887
+ children: r.name
888
+ }
889
+ )
890
+ ]
891
+ },
892
+ r.key
893
+ ))
894
+ }
895
+ );
896
+ }
897
+ function Tt({
898
+ direction: t = "vertical",
899
+ className: e,
900
+ style: n
901
+ }) {
902
+ const s = $("Editor.Layers"), { registry: r } = s, i = T(
903
+ r.subscribe,
904
+ () => r.hasCanvas(),
905
+ () => r.hasCanvas()
906
+ );
907
+ return /* @__PURE__ */ c("div", { "data-sc-part": "layers", "data-sc-direction": t, className: e, style: n, children: i ? /* @__PURE__ */ c(Oe, { style: { width: "100%" } }) : /* @__PURE__ */ c(kt, { store: s.store, direction: t }) });
908
+ }
909
+ function Nt({
910
+ placement: t = "docked",
911
+ className: e,
912
+ style: n
913
+ }) {
914
+ const s = $("Editor.Controls");
915
+ Y("Editor.Controls");
916
+ const r = W();
917
+ P(
918
+ () => s.registry.registerControls(r, t),
919
+ [s.registry, r, t]
920
+ );
921
+ const i = t === "docked", [a, u] = k(null);
922
+ return N(() => {
923
+ if (!i) return;
924
+ let l = !1;
925
+ return import("./ContextualToolbars-CkLjHNPn.js").then((o) => o.x).then(
926
+ (o) => {
927
+ l || u(() => o.ContextualToolbars);
928
+ },
929
+ (o) => {
930
+ l || s.reportRuntimeError(
931
+ new E(
932
+ "E_CHUNK_LOAD_FAILED",
933
+ `the editor's controls chunk failed to load (${o instanceof Error ? o.message : String(o)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
934
+ { cause: o instanceof Error ? o.message : String(o), docSlug: "embed-mode" }
935
+ )
936
+ );
937
+ }
938
+ ), () => {
939
+ l = !0;
940
+ };
941
+ }, [i, s]), i ? /* @__PURE__ */ c(
942
+ "div",
943
+ {
944
+ "data-sc-part": "controls",
945
+ "data-sc-placement": "docked",
946
+ className: e,
947
+ style: n,
948
+ "aria-busy": a ? void 0 : "true",
949
+ children: a ? /* @__PURE__ */ c(a, { mode: "embedded" }) : null
950
+ }
951
+ ) : null;
952
+ }
953
+ function $t({ className: t, style: e }) {
954
+ return Y("Editor.History"), /* @__PURE__ */ c("div", { "data-sc-part": "history", className: t, style: e, children: /* @__PURE__ */ c(nt, {}) });
955
+ }
956
+ function _t({ className: t, style: e }) {
957
+ return Y("Editor.Menu"), /* @__PURE__ */ c("div", { "data-sc-part": "menu", className: t, style: e, children: /* @__PURE__ */ c(ke, {}) });
958
+ }
959
+ function At({ className: t, style: e }) {
960
+ Y("Editor.Add");
961
+ const { get: n } = Xe(), [s, r] = k(!1), [i, a] = k(!1), u = async (o) => {
962
+ if (r(!1), o === "shape") {
963
+ a(!0);
964
+ return;
965
+ }
966
+ await n().handleAddElement(o);
967
+ }, l = async (o) => {
968
+ a(!1), await n().handleAddElement("shape", { shapeType: o });
969
+ };
970
+ return /* @__PURE__ */ M("div", { "data-sc-part": "add", className: t, style: e, children: [
971
+ /* @__PURE__ */ c(
972
+ Te,
973
+ {
974
+ trigger: /* @__PURE__ */ M(Ae, { delayDuration: 0, children: [
975
+ /* @__PURE__ */ c(De, { asChild: !0, children: /* @__PURE__ */ c(Ve, { variant: "ghost", size: "icon", "aria-label": "Add element", children: /* @__PURE__ */ c(le, { icon: G.plus, className: "sc:size-6" }) }) }),
976
+ /* @__PURE__ */ c(Le, { children: /* @__PURE__ */ c("p", { children: "Add element" }) })
977
+ ] }),
978
+ isOpen: s,
979
+ onToggle: r,
980
+ position: "bottom",
981
+ align: "start",
982
+ children: /* @__PURE__ */ c(Ne, { minWidth: "180px", children: $e.map((o) => /* @__PURE__ */ c(
983
+ _e,
984
+ {
985
+ icon: o.icon,
986
+ label: o.label,
987
+ onClick: () => u(o.type)
988
+ },
989
+ o.type
990
+ )) })
991
+ }
992
+ ),
993
+ /* @__PURE__ */ c(
994
+ Be,
995
+ {
996
+ isOpen: i,
997
+ onClose: () => a(!1),
998
+ onSelectType: l
999
+ }
1000
+ )
1001
+ ] });
1002
+ }
1003
+ function Ht() {
1004
+ const e = $("useProductSelection").commerce, n = T(e.subscribe, e.getVersion, e.getVersion), s = Q((i, a) => e.select(i, a), [e]), r = Q((i) => e.setQuantity(i), [e]);
1005
+ return R(
1006
+ () => ({
1007
+ attributes: e.getAttributes(),
1008
+ selection: e.getSelection(),
1009
+ select: s,
1010
+ disabledChoices: e.disabledChoices(),
1011
+ variantId: e.resolvedVariantId(),
1012
+ price: e.resolvedPrice(),
1013
+ quantity: e.getQuantity(),
1014
+ setQuantity: r,
1015
+ complete: e.selectionValid(),
1016
+ missing: e.missingOptionKeys()
1017
+ }),
1018
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1019
+ [e, s, r, n]
1020
+ );
1021
+ }
1022
+ const zt = {
1023
+ /** The one provider boundary — session, design, live state, error boundary. */
1024
+ Root: Ee,
1025
+ /** The editable artboard (the ONE lazy renderer boundary — D9). */
1026
+ Canvas: yt,
1027
+ /** Live product mockup. ×N under one Root is valid. */
1028
+ Preview: we,
1029
+ /** Form input bound to a design text layer by typed token (T1). */
1030
+ TextField: St,
1031
+ /** Smart commerce part: completeness + payload assembly; submit = host callback. */
1032
+ Buy: Ct,
1033
+ /** Layer list. The one editor part that works canvas-free (ADR-0085 §4). */
1034
+ Layers: Tt,
1035
+ /** Morphing contextual zone — ONE coarse part; placement docked|floating (§5). */
1036
+ Controls: Nt,
1037
+ /** Undo/redo controls. Requires Editor.Canvas. */
1038
+ History: $t,
1039
+ /** The one ⋯ overflow menu. Requires Editor.Canvas. */
1040
+ Menu: _t,
1041
+ /** The ⊕ add-element menu. Requires Editor.Canvas. */
1042
+ Add: At
1043
+ };
1044
+ function Gt(t) {
1045
+ return /* @__PURE__ */ c(Ee, { ...t, __validation: "warn" });
1046
+ }
1047
+ const Qt = me(
1048
+ function(e, n) {
1049
+ const { children: s, className: r, style: i, maxHeight: a, ...u } = e, [l, o] = k(null);
1050
+ return N(() => {
1051
+ let d = !1;
1052
+ return Se().then(
1053
+ (m) => {
1054
+ d || o(() => m);
1055
+ },
1056
+ (m) => {
1057
+ console.error("[RealtimeCanvas] failed to load the rendering chunk:", m);
1058
+ }
1059
+ ), () => {
1060
+ d = !0;
1061
+ };
1062
+ }, []), l ? /* @__PURE__ */ c(l, { ref: n, selfScope: !0, maxHeight: a, className: r, style: i, ...u, children: s }) : /* @__PURE__ */ c(
1063
+ "div",
1064
+ {
1065
+ className: `sc-root${r ? ` ${r}` : ""}`,
1066
+ style: { minHeight: a ?? 200, ...i },
1067
+ "aria-busy": "true"
1068
+ }
1069
+ );
1070
+ }
1071
+ );
1072
+ function Wt(t) {
1073
+ return /* @__PURE__ */ c(we, { ...t });
1074
+ }
1075
+ function Jt() {
1076
+ const { mockups: t, isRendering: e, error: n } = J();
1077
+ return { mockups: t, isRendering: e, error: n };
1078
+ }
1079
+ Ue();
1080
+ export {
1081
+ ot as D,
1082
+ ye as E,
1083
+ Qt as R,
1084
+ zt as a,
1085
+ Gt as b,
1086
+ Wt as c,
1087
+ ge as d,
1088
+ Ht as e,
1089
+ Jt as f,
1090
+ J as u,
1091
+ it as v
1092
+ };
1093
+ //# sourceMappingURL=embed-D0HHwCIO.js.map