@snowcone-app/canvas 0.17.3 → 0.18.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 (65) hide show
  1. package/dist/{ImportManager-C_wBjWaV.js → CanvasRenderer-DUc-MOeR.js} +586 -806
  2. package/dist/CanvasRenderer-DUc-MOeR.js.map +1 -0
  3. package/dist/{CanvasStateV1-BG2hEc3-.js → CanvasStateV1-Bq48qhv5.js} +3 -3
  4. package/dist/{CanvasStateV1-BG2hEc3-.js.map → CanvasStateV1-Bq48qhv5.js.map} +1 -1
  5. package/dist/ContextualToolbars-CkLjHNPn.js +5833 -0
  6. package/dist/ContextualToolbars-CkLjHNPn.js.map +1 -0
  7. package/dist/EditorCanvasImpl-Bv4yagew.js +81 -0
  8. package/dist/EditorCanvasImpl-Bv4yagew.js.map +1 -0
  9. package/dist/EditorContext-5QpqAhjh.js +1184 -0
  10. package/dist/{EditorContext-CGkfmbgo.js.map → EditorContext-5QpqAhjh.js.map} +1 -1
  11. package/dist/{FontAnalyzer-CLv7_msL.js → FontAnalyzer-DIlqmyuf.js} +35 -35
  12. package/dist/FontAnalyzer-DIlqmyuf.js.map +1 -0
  13. package/dist/{ElementFactory-CuHSAPfg.js → ImportManager-D--7JnvW.js} +699 -476
  14. package/dist/ImportManager-D--7JnvW.js.map +1 -0
  15. package/dist/MenuButton-CeoEAtzP.js +7444 -0
  16. package/dist/MenuButton-CeoEAtzP.js.map +1 -0
  17. package/dist/SnowconeCanvas-C_O-aeaQ.js +1166 -0
  18. package/dist/SnowconeCanvas-C_O-aeaQ.js.map +1 -0
  19. package/dist/advanced.mjs +266 -264
  20. package/dist/advanced.mjs.map +1 -1
  21. package/dist/api/embed.d.ts +1 -1
  22. package/dist/components/ContextualToolbars.d.ts +8 -0
  23. package/dist/components/editor/EditorAdd.d.ts +11 -0
  24. package/dist/components/editor/EditorCanvasImpl.d.ts +6 -0
  25. package/dist/components/editor/EditorControls.d.ts +14 -0
  26. package/dist/components/editor/EditorHistory.d.ts +11 -0
  27. package/dist/components/editor/EditorLayers.d.ts +16 -0
  28. package/dist/components/editor/EditorMenu.d.ts +11 -0
  29. package/dist/components/editor/RootInternals.d.ts +7 -0
  30. package/dist/components/editor/index.d.ts +20 -0
  31. package/dist/components/embed/addElementTypes.d.ts +9 -0
  32. package/dist/contexts/EditorContext.d.ts +38 -0
  33. package/dist/core/preview-registry.d.ts +34 -0
  34. package/dist/{editorErrors-BY4Pdg7a.js → editorErrors-DXNfVXtD.js} +2 -2
  35. package/dist/{editorErrors-BY4Pdg7a.js.map → editorErrors-DXNfVXtD.js.map} +1 -1
  36. package/dist/embed-Cdbjay9i.js +961 -0
  37. package/dist/embed-Cdbjay9i.js.map +1 -0
  38. package/dist/embed.mjs +5 -5
  39. package/dist/index.mjs +7 -7
  40. package/dist/internals.mjs +12 -12
  41. package/dist/{template-fields-Z9x13sjM.js → template-fields-DgoBtajp.js} +266 -240
  42. package/dist/template-fields-DgoBtajp.js.map +1 -0
  43. package/dist/testing.mjs +3 -3
  44. package/dist/{usePieceGuideBundle-C1ipFnZd.js → usePieceGuideBundle-DVOISsB1.js} +2 -2
  45. package/dist/{usePieceGuideBundle-C1ipFnZd.js.map → usePieceGuideBundle-DVOISsB1.js.map} +1 -1
  46. package/dist/validation-0IscSfRb.js +9304 -0
  47. package/dist/validation-0IscSfRb.js.map +1 -0
  48. package/package.json +1 -1
  49. package/dist/EditorCanvasImpl-DQxqdgpq.js +0 -70
  50. package/dist/EditorCanvasImpl-DQxqdgpq.js.map +0 -1
  51. package/dist/EditorContext-CGkfmbgo.js +0 -1178
  52. package/dist/ElementFactory-CuHSAPfg.js.map +0 -1
  53. package/dist/FontAnalyzer-CLv7_msL.js.map +0 -1
  54. package/dist/ImportManager-C_wBjWaV.js.map +0 -1
  55. package/dist/SnowconeCanvas-DBbmx9qW.js +0 -1187
  56. package/dist/SnowconeCanvas-DBbmx9qW.js.map +0 -1
  57. package/dist/UnifiedFontService-Dsu5cwfT.js +0 -1478
  58. package/dist/UnifiedFontService-Dsu5cwfT.js.map +0 -1
  59. package/dist/embed--B8ot6Lj.js +0 -746
  60. package/dist/embed--B8ot6Lj.js.map +0 -1
  61. package/dist/iconify-D6eVIEy4.js +0 -1089
  62. package/dist/iconify-D6eVIEy4.js.map +0 -1
  63. package/dist/template-fields-Z9x13sjM.js.map +0 -1
  64. package/dist/validation-C795COGQ.js +0 -19959
  65. package/dist/validation-C795COGQ.js.map +0 -1
@@ -1,746 +0,0 @@
1
- import { C as Ee, e as Re } from "./UnifiedFontService-Dsu5cwfT.js";
2
- import { y as x } from "./FontAnalyzer-CLv7_msL.js";
3
- import { jsx as f, jsxs as Q, Fragment as we } from "react/jsx-runtime";
4
- import { useContext as Z, createContext as ee, useRef as N, useState as O, useMemo as I, useEffect as $, useSyncExternalStore as L, Component as xe, forwardRef as te, useId as re, useLayoutEffect as ne, useCallback as se } from "react";
5
- import { assertVariantSelected as ie, RenderSession as Se, createCartDetail as Ie } from "@snowcone-app/sdk";
6
- import { g as R, i as Ce, h as ae, B as Te, E as Ne } from "./EditorContext-CGkfmbgo.js";
7
- import { u as Oe, p as ke } from "./usePieceGuideBundle-C1ipFnZd.js";
8
- import { r as oe } from "./editorErrors-BY4Pdg7a.js";
9
- import { l as X, i as _e, s as le } from "./template-fields-Z9x13sjM.js";
10
- import "./CanvasStateV1-BG2hEc3-.js";
11
- const de = ee(void 0);
12
- function A() {
13
- const t = Z(de);
14
- if (!t)
15
- throw new Error(
16
- "useRealtimeMockups / <RealtimeCanvas> / <RealtimeMockup> must be rendered inside <RealtimeEmbed>. Wrap them: <RealtimeEmbed shop=… product=… design=… grantUrl=…>…</RealtimeEmbed>"
17
- );
18
- return t;
19
- }
20
- const $e = "[RealtimeEmbed]";
21
- function Be(t) {
22
- var n, s;
23
- if ((n = t == null ? void 0 : t.requiredPlacements) != null && n.length)
24
- return t.requiredPlacements.filter((a) => !a.autoFilledByVariant).map((a) => a.label);
25
- const e = new Set(
26
- (((s = t == null ? void 0 : t.options) == null ? void 0 : s.combinations) ?? []).map((a) => a.variantId).filter((a) => typeof a == "string" && a !== "")
27
- );
28
- return !!(t != null && t.variantId) || e.size > 1 ? [] : (t == null ? void 0 : t.placements) ?? [];
29
- }
30
- function ue({ shop: t, product: e, design: r, hasGrant: n }) {
31
- var c, d;
32
- const s = [];
33
- if (t || s.push({
34
- severity: "error",
35
- issue: "missing-shop",
36
- message: "`shop` is required (your publishable shop id)."
37
- }), n || s.push({
38
- severity: "error",
39
- issue: "missing-grant",
40
- message: "no grant source — pass `grantUrl` (a proxy to mintRealtimeGrant) or `grant`. Without it the session can never authorize and no mockup renders."
41
- }), (c = e == null ? void 0 : e.mockupIds) != null && c.length || s.push({
42
- severity: "error",
43
- issue: "empty-mockup-ids",
44
- message: 'product.mockupIds is empty — pass the catalog scene codes (product.mockups[].id) you want rendered, e.g. ["FV1qjO"].'
45
- }), e != null && e.options)
46
- try {
47
- ie(e);
48
- } catch (m) {
49
- s.push({
50
- severity: "error",
51
- issue: "variant-required",
52
- message: m instanceof Error ? m.message : String(m)
53
- });
54
- }
55
- const a = r.toJSON(), l = a.activeArtboard, i = Be(e);
56
- i.length > 1 && s.push({
57
- severity: "error",
58
- issue: "multi-placement",
59
- message: `product ${e.productId} requires placements [${i.join(", ")}] but this embed renders a single placement ("${l}"). The render will time out waiting for the others. This product needs the eject path — see https://developers.snowcone.app/embed-mode#eject.`
60
- }), (d = e == null ? void 0 : e.placements) != null && d.length && !e.placements.includes(l) && s.push({
61
- severity: "error",
62
- issue: "placement-mismatch",
63
- message: `the design's placement "${l}" is not in product.placements [${e.placements.join(", ")}]. The canvas artboard name and renderState placement must equal a real product placement label, or the server renders a blank mockup. Fix the \`name\` passed to design({ name }).`
64
- });
65
- const o = a.elements.map((m) => m.name).filter((m) => !!m), u = o.filter((m, y) => o.indexOf(m) !== y);
66
- return u.length && s.push({
67
- severity: "warn",
68
- issue: "duplicate-layer-names",
69
- message: `duplicate layer name(s) [${[...new Set(u)].join(", ")}] — useTextBinding(name) / useImageBinding(name) bind to the FIRST match, so a duplicate name makes a control edit the wrong layer. Keep names unique.`
70
- }), s;
71
- }
72
- function Fe(t) {
73
- for (const e of ue(t))
74
- (e.severity === "error" && e.issue !== "placement-mismatch" ? console.error : console.warn)(`${$e} ${e.message}`);
75
- }
76
- function j(t) {
77
- return t.text;
78
- }
79
- function _(t) {
80
- return t.name;
81
- }
82
- class De {
83
- constructor(e) {
84
- this.listeners = /* @__PURE__ */ new Set(), this.getState = () => this.state, this.subscribe = (n) => (this.listeners.add(n), () => this.listeners.delete(n)), this.state = e;
85
- const r = /* @__PURE__ */ new Map();
86
- for (const n of e.elements) {
87
- const s = _(n), a = j(n);
88
- s && typeof a == "string" && r.set(s, a);
89
- }
90
- this.seedTextByName = r;
91
- }
92
- /** Replace the whole state (the canvas `onChange` path). */
93
- setState(e) {
94
- this.state = e, this.emit();
95
- }
96
- /** The seed value of a named text layer (for completeness rule C3). */
97
- seedTextOf(e) {
98
- return this.seedTextByName.get(e);
99
- }
100
- /** Current text of a named text layer, or undefined when absent. */
101
- textOf(e) {
102
- const r = this.state.elements.find((n) => _(n) === e);
103
- return r ? j(r) : void 0;
104
- }
105
- /** Kind of the named layer in the CURRENT state ('text' | 'image' | null
106
- * for unbindable), or undefined when no layer has the name. */
107
- layerKindOf(e) {
108
- const r = this.state.elements.find((n) => _(n) === e);
109
- return r ? X(r) : void 0;
110
- }
111
- /** Names of bindable layers in the current state, for error messages. */
112
- bindableLayerNames() {
113
- return this.state.elements.filter((e) => X(e) !== null).map(_).filter((e) => !!e);
114
- }
115
- /**
116
- * Direct write path for `Editor.TextField` when NO canvas is mounted (with
117
- * one, writes route through the editor command path and arrive back here
118
- * via `setState`). Immutable update; last-write-wins.
119
- */
120
- updateTextLayer(e, r) {
121
- let n = !1;
122
- const s = this.state.elements.map((a) => _(a) !== e ? a : (n = !0, { ...a, text: r }));
123
- if (!n)
124
- throw new R(
125
- "E_LAYER_NOT_FOUND",
126
- `no layer "${e}" in the current design state. Bindable layers: ${JSON.stringify(this.bindableLayerNames())}.`,
127
- { layerName: e, validNames: this.bindableLayerNames(), docSlug: "embed-mode" }
128
- );
129
- this.setState({ ...this.state, elements: s });
130
- }
131
- /** ≥1 element with visible content (completeness rule C4): non-empty text,
132
- * an image with a URL, or a shape. Hidden elements don't count. */
133
- hasRenderableElement() {
134
- return this.state.elements.some((e) => {
135
- if (e.visible === !1) return !1;
136
- const r = e.transformType;
137
- if (r === "image") return !!e.imageUrl;
138
- if (r === "shape" || r === "group" || r === "path") return !0;
139
- const n = j(e);
140
- return typeof n == "string" && n.trim().length > 0;
141
- });
142
- }
143
- emit() {
144
- for (const e of [...this.listeners]) e();
145
- }
146
- }
147
- class Ue {
148
- constructor() {
149
- this.canvasIds = /* @__PURE__ */ new Set(), this.requirements = /* @__PURE__ */ new Map(), this.textFields = /* @__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));
150
- }
151
- registerCanvas(e) {
152
- return this.canvasIds.add(e), this.bump(), () => {
153
- this.canvasIds.delete(e), this.bump();
154
- };
155
- }
156
- hasCanvas() {
157
- return this.canvasIds.size > 0;
158
- }
159
- /** Declare that a part is meaningless without a mounted Canvas (ADR-0085 §4
160
- * table). Validated by Root once per commit. */
161
- requireCanvas(e, r) {
162
- return this.requirements.set(e, r), this.bump(), () => {
163
- this.requirements.delete(e), this.bump();
164
- };
165
- }
166
- /** Run by Root during RENDER, re-triggered by every registry change — by
167
- * then the whole commit's registrations are in, so there is no race
168
- * against the lazy chunk (ADR-0085 §4). Throws land in Root's boundary. */
169
- validate() {
170
- if (this.canvasIds.size > 1)
171
- throw new R(
172
- "E_DUPLICATE_CANVAS",
173
- "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.)",
174
- { docSlug: "embed-mode" }
175
- );
176
- if (this.requirements.size > 0 && !this.hasCanvas()) {
177
- const e = [...new Set(this.requirements.values())];
178
- throw new R(
179
- "E_REQUIRES_CANVAS",
180
- `${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.`,
181
- { parts: e, docSlug: "embed-mode" }
182
- );
183
- }
184
- }
185
- registerTextField(e, r) {
186
- return this.textFields.set(e, r), this.bump(), () => {
187
- this.textFields.delete(e), this.bump();
188
- };
189
- }
190
- /** Mounted TextField registrations — Buy's required-layer list (C2/C3).
191
- * Unmounted (incl. conditionally hidden) fields are absent by construction. */
192
- getTextFields() {
193
- return [...this.textFields.values()];
194
- }
195
- /** Bound-layer lookup for the mutation guard (ADR-0085 §3 /
196
- * `E_BOUND_LAYER_MUTATION`): a layer is bound while ANY mounted
197
- * `Editor.TextField` carries its name. Satisfies `BoundLayerGuard`
198
- * structurally (subscribe/getVersion above). */
199
- getBoundLayer(e) {
200
- for (const r of this.textFields.values())
201
- if (r.name === e) return { part: "Editor.TextField" };
202
- return null;
203
- }
204
- registerCanvasBridge(e) {
205
- return this.bridge = e, this.bump(), () => {
206
- this.bridge === e && (this.bridge = null), this.bump();
207
- };
208
- }
209
- getCanvasBridge() {
210
- return this.bridge;
211
- }
212
- bump() {
213
- this.version++;
214
- for (const e of [...this.listeners]) e();
215
- }
216
- }
217
- const ce = ee(null);
218
- function K(t) {
219
- const e = Z(ce);
220
- if (!e)
221
- throw new R(
222
- "E_OUTSIDE_ROOT",
223
- `<${t}> must be rendered inside <Editor.Root>. Wrap it: <Editor.Root shop={…} product={…} design={…} grantUrl={…}><${t}/></Editor.Root>`,
224
- { part: t, docSlug: "embed-mode" }
225
- );
226
- return e;
227
- }
228
- function Le(t) {
229
- return {
230
- reportInvariant(e) {
231
- if (Ce()) throw e;
232
- t(e);
233
- },
234
- reportRuntimeError(e) {
235
- oe(e), t(e);
236
- }
237
- };
238
- }
239
- const me = 8e3;
240
- function Ae(t) {
241
- return `[RealtimeEmbed] No mockup after ${Math.round(me / 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.`;
242
- }
243
- function qe(t) {
244
- if ("grantUrl" in t && t.grantUrl)
245
- return { grantUrl: t.grantUrl };
246
- const e = t.grant;
247
- return typeof e == "function" ? { getToken: e } : { getToken: () => Promise.resolve(e) };
248
- }
249
- function fe({ error: t, onRetry: e }) {
250
- const r = ae(t) ? t.code : "EDITOR_ERROR";
251
- return /* @__PURE__ */ Q(
252
- "div",
253
- {
254
- "data-sc-part": "error",
255
- role: "alert",
256
- style: {
257
- padding: "16px",
258
- borderRadius: "12px",
259
- border: "1px solid var(--sc-color-destructive, #b91c1c)",
260
- background: "var(--sc-color-surface, #fef2f2)",
261
- color: "var(--sc-color-foreground, #7f1d1d)",
262
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
263
- fontSize: "13px",
264
- lineHeight: 1.5
265
- },
266
- children: [
267
- /* @__PURE__ */ Q("p", { style: { margin: 0, fontWeight: 700 }, children: [
268
- "Editor failed: ",
269
- r
270
- ] }),
271
- /* @__PURE__ */ f("p", { style: { margin: "4px 0 0" }, children: t.message }),
272
- e ? /* @__PURE__ */ f(
273
- "button",
274
- {
275
- type: "button",
276
- "data-sc-part": "error-retry",
277
- onClick: e,
278
- style: {
279
- marginTop: "10px",
280
- padding: "6px 14px",
281
- borderRadius: "9999px",
282
- border: "1px solid currentColor",
283
- background: "transparent",
284
- color: "inherit",
285
- font: "inherit",
286
- fontWeight: 600,
287
- cursor: "pointer"
288
- },
289
- children: "Retry"
290
- }
291
- ) : null
292
- ]
293
- }
294
- );
295
- }
296
- class Me extends xe {
297
- constructor() {
298
- super(...arguments), this.state = { caught: null };
299
- }
300
- static getDerivedStateFromError(e) {
301
- return { caught: e };
302
- }
303
- componentDidCatch(e) {
304
- var r;
305
- oe(e), typeof x < "u" && ((r = x.env) == null ? void 0 : r.NODE_ENV) !== "production" && console.error("[Editor.Root] contained a composition error:", e);
306
- }
307
- render() {
308
- return this.state.caught ? /* @__PURE__ */ f(fe, { error: this.state.caught }) : this.props.children;
309
- }
310
- }
311
- function Ve({ reportedError: t, onRetry: e, children: r }) {
312
- if (t) {
313
- const n = ae(t) && t.code === "E_CHUNK_LOAD_FAILED";
314
- return /* @__PURE__ */ f(fe, { error: t, onRetry: n ? e : void 0 });
315
- }
316
- return /* @__PURE__ */ f(Me, { reportedError: null, onRetry: e, children: r });
317
- }
318
- function Pe(t) {
319
- const {
320
- shop: e,
321
- product: r,
322
- design: n,
323
- throttleMs: s = 200,
324
- onMockups: a,
325
- onRenderError: l,
326
- wsUrl: i,
327
- services: o,
328
- children: u,
329
- internals: c,
330
- __validation: d = "throw"
331
- } = t, m = I(() => n.toJSON().activeArtboard, [n]), y = Oe(r.variantId ?? r.defaultGvid), g = I(() => {
332
- if (!y) return null;
333
- const h = n.toJSON().artboards[0];
334
- return ke(y, h) ?? null;
335
- }, [y, n]), b = N(a), p = N(l);
336
- b.current = a, p.current = l;
337
- const [S, q] = O([]), [C, T] = O(!0), [H, B] = O(null), M = !!("grantUrl" in t && t.grantUrl || "grant" in t && t.grant), V = qe(t), ye = "grantUrl" in V ? V.grantUrl : "getToken", W = `${r.productId}|${r.mockupIds.join(",")}|${r.variantId ?? ""}`, F = `${e}|${W}|${m}|${M}`;
338
- I(() => {
339
- var E;
340
- if (d !== "throw") return;
341
- const h = ue({ shop: e, product: r, design: n, hasGrant: M }), v = h.find((k) => k.severity === "error");
342
- if (v)
343
- throw new R("E_EMBED_CONFIG", v.message, {
344
- issue: v.issue,
345
- docSlug: "embed-mode"
346
- });
347
- for (const k of h)
348
- typeof x < "u" && ((E = x.env) == null ? void 0 : E.NODE_ENV) !== "production" && console.warn(`[Editor.Root] ${k.message}`);
349
- }, [F, d]);
350
- const z = N(null);
351
- $(() => {
352
- var h;
353
- if (d === "warn" && typeof x < "u" && ((h = x.env) == null ? void 0 : h.NODE_ENV) !== "production") {
354
- if (z.current === F) return;
355
- z.current = F, Fe({ shop: e, product: r, design: n, hasGrant: M });
356
- }
357
- }, [F, d]);
358
- const D = I(
359
- () => {
360
- try {
361
- return { session: new Se({ shop: e, product: r, wsUrl: i, ...V }) };
362
- } catch (h) {
363
- return { error: h instanceof Error ? h.message : String(h) };
364
- }
365
- },
366
- // eslint-disable-next-line react-hooks/exhaustive-deps
367
- [e, W, m, i, ye]
368
- ), w = "session" in D ? D.session : null, U = "error" in D ? D.error : null, P = I(() => (h, v = s) => {
369
- w && (B(null), T(!0), w.renderState(m, h, v).catch((E) => {
370
- var Y;
371
- const k = E instanceof Error ? E.message : String(E);
372
- B(k), T(!1), (Y = p.current) == null || Y.call(p, k);
373
- }));
374
- }, [w, m, s]);
375
- $(() => {
376
- var h;
377
- if (U) {
378
- B(U), T(!1), (h = p.current) == null || h.call(p, U);
379
- return;
380
- }
381
- if (w)
382
- return w.onMockups((v) => {
383
- var E;
384
- q(v), T(!1), (E = b.current) == null || E.call(b, v);
385
- }), w.onError((v) => {
386
- var E;
387
- B(v), T(!1), (E = p.current) == null || E.call(p, v);
388
- }), P(n.toServerRequest()), () => w.close();
389
- }, [w, U]), $(() => {
390
- var v;
391
- if (!C || typeof x > "u" || ((v = x.env) == null ? void 0 : v.NODE_ENV) === "production") return;
392
- const h = setTimeout(() => console.warn(Ae(m)), me);
393
- return () => clearTimeout(h);
394
- }, [C, m]);
395
- const ve = I(
396
- () => ({
397
- session: w,
398
- design: n,
399
- placement: m,
400
- product: r,
401
- mockups: S,
402
- isRendering: C,
403
- error: H,
404
- throttleMs: s,
405
- pieceGuides: g,
406
- pushRender: P,
407
- stateStore: c.store
408
- }),
409
- [w, n, m, r, S, C, H, s, g, P, c.store]
410
- ), be = L(
411
- c.registry.subscribe,
412
- c.registry.getVersion,
413
- c.registry.getVersion
414
- );
415
- I(() => c.registry.validate(), [c.registry, be]);
416
- const J = /* @__PURE__ */ f(ce.Provider, { value: c, children: /* @__PURE__ */ f(de.Provider, { value: ve, children: /* @__PURE__ */ f(Te.Provider, { value: c.registry, children: /* @__PURE__ */ f(Ne, { children: u }) }) }) });
417
- return o ? /* @__PURE__ */ f(Ee, { services: o, children: J }) : J;
418
- }
419
- function he(t) {
420
- const { design: e } = t, r = N(null), [n, s] = O(null);
421
- (r.current === null || r.current.design !== e) && (r.current = {
422
- design: e,
423
- internals: {
424
- store: new De(e.toJSON()),
425
- registry: new Ue()
426
- }
427
- });
428
- const a = I(
429
- () => ({
430
- ...r.current.internals,
431
- ...Le(s)
432
- }),
433
- // eslint-disable-next-line react-hooks/exhaustive-deps
434
- [r.current.internals]
435
- );
436
- return (
437
- // `display: contents` — the .sc-root scope without a layout box, so the
438
- // host's grid/flex sees the parts directly (and the legacy RealtimeEmbed
439
- // wrapper, which had no DOM at all, stays layout-identical).
440
- /* @__PURE__ */ f("div", { className: "sc-root", style: { display: "contents" }, "data-sc-part": "root", children: /* @__PURE__ */ f(Ve, { reportedError: n, onRetry: () => s(null), children: /* @__PURE__ */ f(Pe, { ...t, internals: a }) }) })
441
- );
442
- }
443
- let je = () => import("./EditorCanvasImpl-DQxqdgpq.js").then((t) => t.EditorCanvasImpl);
444
- function ge() {
445
- return je();
446
- }
447
- const Ge = te(function({ children: e, maxHeight: r, className: n, style: s }, a) {
448
- const l = K("Editor.Canvas"), i = re();
449
- ne(() => l.registry.registerCanvas(i), [l.registry, i]);
450
- const [o, u] = O(null);
451
- return $(() => {
452
- let c = !1;
453
- return ge().then(
454
- (d) => {
455
- c || u(() => d);
456
- },
457
- (d) => {
458
- c || l.reportRuntimeError(
459
- new R(
460
- "E_CHUNK_LOAD_FAILED",
461
- `the editor's rendering chunk failed to load (${d instanceof Error ? d.message : String(d)}). This is a network/CDN failure, not a composition mistake — retry, or check the deployment serves the @snowcone-app/canvas lazy chunks.`,
462
- { cause: d instanceof Error ? d.message : String(d), docSlug: "embed-mode" }
463
- )
464
- );
465
- }
466
- ), () => {
467
- c = !0;
468
- };
469
- }, [l]), o ? /* @__PURE__ */ f(o, { ref: a, kit: "embed-only", partAttr: "canvas", maxHeight: r, className: n, style: s, children: e }) : /* @__PURE__ */ f(
470
- "div",
471
- {
472
- "data-sc-part": "canvas",
473
- "data-sc-state": "loading",
474
- className: n,
475
- style: { minHeight: r ?? 200, ...s },
476
- "aria-busy": "true"
477
- }
478
- );
479
- }), Ke = 1.2, G = [1200, 1400, 1600, 1800, 2e3, 2500, 3e3, 4e3];
480
- function He(t) {
481
- for (const e of G)
482
- if (e >= t) return e;
483
- return G[G.length - 1];
484
- }
485
- function We(t, e) {
486
- if (!(t > 0) || !(e > 0) || e <= t * Ke) return null;
487
- const r = He(e);
488
- return `[RealtimeMockup] mockup rendered at ${t}px but displayed at ~${e} device px — the preview will look soft. Pass \`product.width\` (e.g. ${r}) on the embed for a crisp preview.`;
489
- }
490
- function pe({
491
- mockupId: t,
492
- fallback: e = null,
493
- alt: r = "Live product mockup",
494
- className: n,
495
- style: s
496
- }) {
497
- const { mockups: a } = A(), l = N(/* @__PURE__ */ new Set()), i = t ? a.find((u) => u.mockupId === t && u.imageUrl) : a.find((u) => u.imageUrl);
498
- if (!(i != null && i.imageUrl))
499
- return /* @__PURE__ */ f(we, { children: e });
500
- const o = (u) => {
501
- var g;
502
- if (typeof x > "u" || ((g = x.env) == null ? void 0 : g.NODE_ENV) === "production" || typeof window > "u" || l.current.has(i.mockupId)) return;
503
- const c = u.currentTarget, d = c.getBoundingClientRect().width;
504
- if (d <= 0) return;
505
- const m = Math.round(d * (window.devicePixelRatio || 1)), y = We(c.naturalWidth, m);
506
- y && (l.current.add(i.mockupId), console.warn(y));
507
- };
508
- return (
509
- // eslint-disable-next-line @next/next/no-img-element
510
- /* @__PURE__ */ f(
511
- "img",
512
- {
513
- "data-sc-part": "preview",
514
- src: i.imageUrl,
515
- alt: r,
516
- className: n,
517
- style: s,
518
- onLoad: o
519
- }
520
- )
521
- );
522
- }
523
- function ze(t) {
524
- const { layer: e, optional: r = !1, placeholder: n, className: s, style: a } = t, l = K("Editor.TextField"), { pushRender: i, throttleMs: o, stateStore: u } = A(), c = re(), d = N(null);
525
- if (!_e(e))
526
- throw new R(
527
- "E_LAYER_TOKEN_REQUIRED",
528
- `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.`,
529
- { received: typeof e == "string" ? e : typeof e, docSlug: "embed-mode" }
530
- );
531
- if (d.current !== e.name) {
532
- const g = u.layerKindOf(e.name);
533
- if (g === void 0)
534
- throw new R(
535
- "E_LAYER_NOT_FOUND",
536
- `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(u.bindableLayerNames())}.`,
537
- { layerName: e.name, validNames: u.bindableLayerNames(), docSlug: "embed-mode" }
538
- );
539
- if (g !== "text")
540
- throw new R(
541
- "E_LAYER_WRONG_KIND",
542
- `layer "${e.name}" is ${g === null ? "not a bindable kind" : `an ${g} layer`} — <Editor.TextField> binds text layers only.`,
543
- { layerName: e.name, actualKind: g, docSlug: "embed-mode" }
544
- );
545
- d.current = e.name;
546
- }
547
- ne(() => l.registry.registerTextField(c, { name: e.name, optional: r }), [l.registry, c, e.name, r]);
548
- const m = L(
549
- u.subscribe,
550
- () => u.textOf(e.name) ?? "",
551
- () => u.textOf(e.name) ?? ""
552
- ), y = se(
553
- (g) => {
554
- const b = g.target.value, p = l.registry.getCanvasBridge();
555
- if (p) {
556
- p.setTextByName(e.name, b) || l.reportInvariant(
557
- new R(
558
- "E_LAYER_NOT_FOUND",
559
- `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).`,
560
- { layerName: e.name, docSlug: "embed-mode" }
561
- )
562
- );
563
- return;
564
- }
565
- u.updateTextLayer(e.name, b), i(le(u.getState()), o);
566
- },
567
- [l, e.name, u, i, o]
568
- );
569
- return /* @__PURE__ */ f(
570
- "input",
571
- {
572
- "data-sc-part": "textfield",
573
- "data-sc-layer": e.name,
574
- type: "text",
575
- value: m,
576
- onChange: y,
577
- placeholder: n,
578
- "aria-label": t["aria-label"] ?? e.name,
579
- className: s,
580
- style: a
581
- }
582
- );
583
- }
584
- function Je(t) {
585
- const e = [];
586
- if (t.product.options)
587
- try {
588
- ie(t.product);
589
- } catch {
590
- e.push("variant");
591
- }
592
- for (const r of t.textFields) {
593
- if (r.optional) continue;
594
- const n = (t.textOf(r.name) ?? "").trim();
595
- if (n.length === 0) {
596
- e.push(`layer:${r.name}`);
597
- continue;
598
- }
599
- const s = (t.seedTextOf(r.name) ?? "").trim();
600
- s.length > 0 && n === s && e.push(`layer-seed:${r.name}`);
601
- }
602
- return t.hasRenderableElement || e.push("empty-design"), (!Number.isInteger(t.quantity) || t.quantity < 1) && e.push("quantity"), e;
603
- }
604
- function Ye(t, e) {
605
- const r = t.options, n = r == null ? void 0 : r.combinations;
606
- if (!r || !(n != null && n.length)) return null;
607
- const s = n.find((i) => i.variantId && i.variantId === t.variantId) ?? n[0];
608
- if (!s) return null;
609
- const a = {};
610
- for (const [i, o] of Object.entries(s))
611
- i === "variantId" || i === "price" || (typeof o == "string" ? a[i] = o : typeof o == "number" && (a[i] = String(o)));
612
- const l = Object.fromEntries(
613
- Object.keys(a).map((i) => [
614
- i,
615
- { key: i, affectsCombinations: !0, choices: [...new Set(n.map((o) => String(o[i] ?? "")))] }
616
- ])
617
- );
618
- return Ie({
619
- productId: t.productId,
620
- quantity: e,
621
- selection: a,
622
- combinations: n,
623
- attributes: l
624
- });
625
- }
626
- function Qe(t) {
627
- const { onAddToCart: e, quantity: r = 1, children: n, className: s, style: a } = t, l = K("Editor.Buy"), { product: i, stateStore: o } = A();
628
- if (!(i != null && i.productId))
629
- throw new R(
630
- "E_BUY_NO_PRODUCT",
631
- "<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.",
632
- { docSlug: "embed-mode" }
633
- );
634
- const u = L(o.subscribe, o.getState, o.getState), c = L(
635
- l.registry.subscribe,
636
- l.registry.getVersion,
637
- l.registry.getVersion
638
- ), d = I(
639
- () => Je({
640
- product: i,
641
- quantity: r,
642
- textFields: l.registry.getTextFields(),
643
- textOf: (S) => o.textOf(S),
644
- seedTextOf: (S) => o.seedTextOf(S),
645
- hasRenderableElement: o.hasRenderableElement()
646
- }),
647
- // eslint-disable-next-line react-hooks/exhaustive-deps
648
- [i, r, u, c]
649
- ), m = d.length === 0, [y, g] = O(!1), b = N(!1), p = se(() => {
650
- if (b.current || !m) return;
651
- b.current = !0, g(!0);
652
- const S = o.getState(), q = {
653
- productId: i.productId,
654
- ...i.variantId ? { variantId: i.variantId } : {},
655
- quantity: r,
656
- cartDetail: Ye(i, r),
657
- designState: S,
658
- serverRequest: le(S)
659
- };
660
- let C;
661
- try {
662
- C = e(q);
663
- } catch (T) {
664
- throw b.current = !1, g(!1), T;
665
- }
666
- Promise.resolve(C).finally(() => {
667
- b.current = !1, g(!1);
668
- });
669
- }, [m, i, r, o, e]);
670
- return /* @__PURE__ */ f(
671
- "button",
672
- {
673
- type: "button",
674
- "data-sc-part": "buy",
675
- "data-sc-buy-state": y ? "busy" : m ? "ready" : "incomplete",
676
- "data-sc-buy-missing": d.join(",") || void 0,
677
- disabled: !m || y,
678
- "aria-busy": y || void 0,
679
- onClick: p,
680
- className: s,
681
- style: a,
682
- children: n ?? "Add to cart"
683
- }
684
- );
685
- }
686
- const lt = {
687
- /** The one provider boundary — session, design, live state, error boundary. */
688
- Root: he,
689
- /** The editable artboard (the ONE lazy renderer boundary — D9). */
690
- Canvas: Ge,
691
- /** Live product mockup. ×N under one Root is valid. */
692
- Preview: pe,
693
- /** Form input bound to a design text layer by typed token (T1). */
694
- TextField: ze,
695
- /** Smart commerce part: completeness + payload assembly; submit = host callback. */
696
- Buy: Qe
697
- };
698
- function dt(t) {
699
- return /* @__PURE__ */ f(he, { ...t, __validation: "warn" });
700
- }
701
- const ut = te(
702
- function(e, r) {
703
- const { children: n, className: s, style: a, maxHeight: l, ...i } = e, [o, u] = O(null);
704
- return $(() => {
705
- let c = !1;
706
- return ge().then(
707
- (d) => {
708
- c || u(() => d);
709
- },
710
- (d) => {
711
- console.error("[RealtimeCanvas] failed to load the rendering chunk:", d);
712
- }
713
- ), () => {
714
- c = !0;
715
- };
716
- }, []), o ? /* @__PURE__ */ f(o, { ref: r, selfScope: !0, maxHeight: l, className: s, style: a, ...i, children: n }) : /* @__PURE__ */ f(
717
- "div",
718
- {
719
- className: `sc-root${s ? ` ${s}` : ""}`,
720
- style: { minHeight: l ?? 200, ...a },
721
- "aria-busy": "true"
722
- }
723
- );
724
- }
725
- );
726
- function ct(t) {
727
- return /* @__PURE__ */ f(pe, { ...t });
728
- }
729
- function mt() {
730
- const { mockups: t, isRendering: e, error: r } = A();
731
- return { mockups: t, isRendering: e, error: r };
732
- }
733
- Re();
734
- export {
735
- De as D,
736
- ce as E,
737
- ut as R,
738
- lt as a,
739
- dt as b,
740
- ct as c,
741
- ue as d,
742
- mt as e,
743
- A as u,
744
- Fe as v
745
- };
746
- //# sourceMappingURL=embed--B8ot6Lj.js.map