@snowcone-app/canvas 0.29.0 → 0.31.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 (92) hide show
  1. package/ERROR_CONTRACT.md +13 -0
  2. package/STYLING_CONTRACT.md +2 -2
  3. package/dist/{CanvasRenderer-_lFoIoz5.js → CanvasRenderer-hpJ05JGO.js} +4 -4
  4. package/dist/{CanvasRenderer-_lFoIoz5.js.map → CanvasRenderer-hpJ05JGO.js.map} +1 -1
  5. package/dist/{CanvasStateV1-BMxr6xb8.js → CanvasStateV1-jLOibojY.js} +876 -844
  6. package/dist/{CanvasStateV1-BMxr6xb8.js.map → CanvasStateV1-jLOibojY.js.map} +1 -1
  7. package/dist/ContextualToolbars-DRbuUE7W.js +6061 -0
  8. package/dist/ContextualToolbars-DRbuUE7W.js.map +1 -0
  9. package/dist/{EditorCanvasImpl-BNz8TPFs.js → EditorCanvasImpl-P2GHPNA3.js} +19 -20
  10. package/dist/{EditorCanvasImpl-BNz8TPFs.js.map → EditorCanvasImpl-P2GHPNA3.js.map} +1 -1
  11. package/dist/{ExportManager-B8Td7uR5.js → ExportManager-Bkzzyxgw.js} +973 -576
  12. package/dist/ExportManager-Bkzzyxgw.js.map +1 -0
  13. package/dist/{FontAnalyzer-D4Sg-P6_.js → FontAnalyzer-BYhwxDhe.js} +542 -477
  14. package/dist/FontAnalyzer-BYhwxDhe.js.map +1 -0
  15. package/dist/{ImagePanel-CN2Bu3hf.js → ImagePanel-CEr0_FYy.js} +38 -38
  16. package/dist/{ImagePanel-CN2Bu3hf.js.map → ImagePanel-CEr0_FYy.js.map} +1 -1
  17. package/dist/SnowconeCanvas-Fa0isFpO.js +1192 -0
  18. package/dist/SnowconeCanvas-Fa0isFpO.js.map +1 -0
  19. package/dist/advanced.mjs +14 -14
  20. package/dist/api/embed.d.ts +2 -0
  21. package/dist/api/stable.d.ts +2 -0
  22. package/dist/components/CanvasContextMenu.d.ts +15 -0
  23. package/dist/components/CanvasEditor/hooks/useCanvasInteraction.d.ts +27 -0
  24. package/dist/components/CanvasEditor.d.ts +8 -0
  25. package/dist/components/ImageToolbar.d.ts +4 -0
  26. package/dist/components/embed/SnowconeCanvas.d.ts +13 -0
  27. package/dist/components/embed/kernel.d.ts +8 -0
  28. package/dist/components/toolbars/EmbeddedToolbarLayout.d.ts +2 -0
  29. package/dist/components/toolbars/SecondaryPanels.d.ts +17 -1
  30. package/dist/components/toolbars/index.d.ts +2 -2
  31. package/dist/components/ui/SliderRow.d.ts +6 -0
  32. package/dist/components/ui/icons.d.ts +2 -0
  33. package/dist/components/ui/toolbar-button.d.ts +2 -2
  34. package/dist/contexts/CanvasServicesContext.d.ts +17 -0
  35. package/dist/contexts/EditorContext.d.ts +7 -0
  36. package/dist/core/image-adjustments.d.ts +32 -0
  37. package/dist/{custom-icons-B5TRC1Ed.js → custom-icons-Co4rcW0S.js} +7 -7
  38. package/dist/{custom-icons-B5TRC1Ed.js.map → custom-icons-Co4rcW0S.js.map} +1 -1
  39. package/dist/design-builder-CBG2hKc8.js +1672 -0
  40. package/dist/design-builder-CBG2hKc8.js.map +1 -0
  41. package/dist/{embed-BTRtoktw.js → embed-D4TIzO7g.js} +20 -20
  42. package/dist/{embed-BTRtoktw.js.map → embed-D4TIzO7g.js.map} +1 -1
  43. package/dist/embed.mjs +51 -46
  44. package/dist/embed.mjs.map +1 -1
  45. package/dist/errors/editorError.d.ts +8 -0
  46. package/dist/{guards-uQtEQJbs.js → guards-DgZuwVgu.js} +2 -2
  47. package/dist/{guards-uQtEQJbs.js.map → guards-DgZuwVgu.js.map} +1 -1
  48. package/dist/hooks/elementPatch.d.ts +7 -2
  49. package/dist/hooks/useKeyboardShortcuts.d.ts +15 -2
  50. package/dist/hooks/useLayers.d.ts +2 -0
  51. package/dist/index.mjs +57 -52
  52. package/dist/index.mjs.map +1 -1
  53. package/dist/internals.mjs +201 -132
  54. package/dist/internals.mjs.map +1 -1
  55. package/dist/rendering/element-painters.d.ts +0 -13
  56. package/dist/rendering/element-serializer.d.ts +3 -1
  57. package/dist/rendering/image-adjustments.d.ts +24 -0
  58. package/dist/rendering/renderer-types.d.ts +8 -1
  59. package/dist/state/CanvasStateV1.d.ts +36 -3
  60. package/dist/state/index.d.ts +2 -2
  61. package/dist/template-fields-CvFcr5zy.js +505 -0
  62. package/dist/template-fields-CvFcr5zy.js.map +1 -0
  63. package/dist/testing.mjs +46 -47
  64. package/dist/testing.mjs.map +1 -1
  65. package/dist/types/index.d.ts +21 -0
  66. package/dist/{UnifiedFontService-CgYnYTj7.js → useCommands-bPXY9rCh.js} +2429 -2145
  67. package/dist/useCommands-bPXY9rCh.js.map +1 -0
  68. package/dist/{usePieceGuideBundle-CTQszfKA.js → usePieceGuideBundle-Bj98nPk0.js} +2 -2
  69. package/dist/{usePieceGuideBundle-CTQszfKA.js.map → usePieceGuideBundle-Bj98nPk0.js.map} +1 -1
  70. package/dist/utils/custom-fonts.d.ts +99 -0
  71. package/dist/utils/featureApplied.d.ts +2 -0
  72. package/dist/validation-CsTlSvJJ.js +8686 -0
  73. package/dist/validation-CsTlSvJJ.js.map +1 -0
  74. package/dist/workers/export-protocol.d.ts +13 -0
  75. package/dist/workers/export-worker.bundle.string.d.ts +1 -1
  76. package/example/package.json +1 -1
  77. package/package.json +1 -1
  78. package/dist/ContextualToolbars-0jWvnQgp.js +0 -5833
  79. package/dist/ContextualToolbars-0jWvnQgp.js.map +0 -1
  80. package/dist/ExportManager-B8Td7uR5.js.map +0 -1
  81. package/dist/FontAnalyzer-D4Sg-P6_.js.map +0 -1
  82. package/dist/SnowconeCanvas-CR_LdRFP.js +0 -1173
  83. package/dist/SnowconeCanvas-CR_LdRFP.js.map +0 -1
  84. package/dist/UnifiedFontService-CgYnYTj7.js.map +0 -1
  85. package/dist/design-builder-COj0zkxu.js +0 -470
  86. package/dist/design-builder-COj0zkxu.js.map +0 -1
  87. package/dist/editorErrors-CCvoKj_P.js +0 -1219
  88. package/dist/editorErrors-CCvoKj_P.js.map +0 -1
  89. package/dist/template-fields-BLRp84Ny.js +0 -716
  90. package/dist/template-fields-BLRp84Ny.js.map +0 -1
  91. package/dist/validation-eCebhplp.js +0 -8402
  92. package/dist/validation-eCebhplp.js.map +0 -1
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { ArtboardManager } from '../core/ArtboardManager.js';
3
3
  import { ArtboardElement } from '../core/ArtboardElement.js';
4
4
  import { BaseElement } from '../core/BaseElement.js';
5
+ import { CustomFont } from '../utils/custom-fonts.js';
5
6
  declare const StrokeConfigSchema: z.ZodObject<{
6
7
  enabled: z.ZodBoolean;
7
8
  color: z.ZodString;
@@ -145,6 +146,11 @@ declare const AnyTransformDataSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
145
146
  flipHorizontal: z.ZodBoolean;
146
147
  flipVertical: z.ZodBoolean;
147
148
  borderRadius: z.ZodNumber;
149
+ adjustments: z.ZodOptional<z.ZodObject<{
150
+ brightness: z.ZodNumber;
151
+ contrast: z.ZodNumber;
152
+ saturation: z.ZodNumber;
153
+ }, z.core.$strip>>;
148
154
  }, z.core.$strip>, z.ZodObject<{
149
155
  type: z.ZodLiteral<"group">;
150
156
  }, z.core.$strip>, z.ZodObject<{
@@ -266,6 +272,15 @@ declare const SerializedArtboardSchema: z.ZodObject<{
266
272
  }>>;
267
273
  elementIds: z.ZodArray<z.ZodString>;
268
274
  }, z.core.$strip>;
275
+ /**
276
+ * A custom (brand) font dependency of the design — `{ family, url }` of a
277
+ * host-stored font binary plus optional weight/style descriptors. Saved
278
+ * designs carry these so every consumer (live editor, export worker, server
279
+ * render) can load the exact file. Families are stored as-is; a family that
280
+ * collides with a Google Fonts family deterministically SHADOWS it (see
281
+ * `utils/custom-fonts.ts` for the resolution contract).
282
+ */
283
+ declare const CustomFontSchema: z.ZodType<CustomFont>;
269
284
  /**
270
285
  * Top-level CanvasStateV1 schema.
271
286
  *
@@ -273,6 +288,7 @@ declare const SerializedArtboardSchema: z.ZodObject<{
273
288
  * - `artboards` contains serialized artboard configs with `elementIds`.
274
289
  * - `elements` is a flat array of all elements across all artboards.
275
290
  * - `activeArtboardId` identifies which artboard is currently active.
291
+ * - `customFonts` (optional) lists the design's custom-font dependencies.
276
292
  */
277
293
  declare const CanvasStateV1Schema: z.ZodObject<{
278
294
  version: z.ZodLiteral<1>;
@@ -324,6 +340,7 @@ declare const CanvasStateV1Schema: z.ZodObject<{
324
340
  }, z.core.$strip>>;
325
341
  elements: z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
326
342
  activeArtboardId: z.ZodString;
343
+ customFonts: z.ZodOptional<z.ZodArray<z.ZodType<CustomFont, unknown, z.core.$ZodTypeInternals<CustomFont, unknown>>>>;
327
344
  }, z.core.$strip>;
328
345
  /** A serialized artboard (artboard config + elementIds, no nested elements). */
329
346
  type SerializedArtboard = z.infer<typeof SerializedArtboardSchema>;
@@ -331,19 +348,35 @@ type SerializedArtboard = z.infer<typeof SerializedArtboardSchema>;
331
348
  type SerializedElement = z.infer<typeof AnyElementConfigSchema>;
332
349
  /** The full versioned state document. */
333
350
  type CanvasStateV1 = z.infer<typeof CanvasStateV1Schema>;
351
+ /** Options for {@link serializeState}. */
352
+ interface SerializeStateOptions {
353
+ /**
354
+ * Custom-font dependencies to embed in the document. When omitted, the
355
+ * default is CORRECT, not empty: the registered custom fonts whose family
356
+ * is actually used by the serialized elements are included automatically,
357
+ * so a host that uploaded a brand font cannot forget to persist it.
358
+ * Pass `[]` explicitly to serialize without custom fonts.
359
+ */
360
+ customFonts?: readonly CustomFont[];
361
+ }
334
362
  /**
335
363
  * Serialize the current editor state into a `CanvasStateV1` document.
336
364
  *
337
365
  * @param artboardManager - The ArtboardManager instance (provides artboard list + active ID).
338
366
  * @param elements - All elements currently in the editor (across all artboards).
367
+ * @param options - See {@link SerializeStateOptions} (`customFonts` defaults to
368
+ * the registered custom fonts the elements actually use).
339
369
  * @returns A validated CanvasStateV1 object ready for JSON.stringify().
340
370
  */
341
- declare function serializeState(artboardManager: ArtboardManager, elements: BaseElement[]): CanvasStateV1;
371
+ declare function serializeState(artboardManager: ArtboardManager, elements: BaseElement[], options?: SerializeStateOptions): CanvasStateV1;
342
372
  /** Result of deserializing a CanvasStateV1 document. */
343
373
  interface DeserializeResult {
344
374
  artboards: ArtboardElement[];
345
375
  elements: BaseElement[];
346
376
  activeArtboardId: string;
377
+ /** The design's custom-font dependencies (already registered with
378
+ * `customFontRegistry` as a side effect of deserializing). */
379
+ customFonts: CustomFont[];
347
380
  }
348
381
  /**
349
382
  * Deserialize a validated `CanvasStateV1` document back into runtime objects.
@@ -384,5 +417,5 @@ declare function validateState(data: unknown): ValidationResult;
384
417
  * @throws Error if the data cannot be migrated.
385
418
  */
386
419
  declare function migrateState(data: unknown): CanvasStateV1;
387
- export { CanvasStateV1Schema, SerializedArtboardSchema, AnyElementConfigSchema, AnyTransformDataSchema, StrokeConfigSchema, KnockoutConfigSchema, DistressEffectSchema, MaskDefinitionSchema, CharacterStyleSchema, TextSpanSchema, RichTextSchema, ClipShapeSchema, serializeState, deserializeState, validateState, migrateState, };
388
- export type { CanvasStateV1, SerializedArtboard, SerializedElement, DeserializeResult, ValidationResult, };
420
+ export { CanvasStateV1Schema, SerializedArtboardSchema, AnyElementConfigSchema, AnyTransformDataSchema, StrokeConfigSchema, KnockoutConfigSchema, DistressEffectSchema, MaskDefinitionSchema, CharacterStyleSchema, TextSpanSchema, RichTextSchema, ClipShapeSchema, CustomFontSchema, serializeState, deserializeState, validateState, migrateState, };
421
+ export type { CanvasStateV1, SerializedArtboard, SerializedElement, DeserializeResult, ValidationResult, SerializeStateOptions, CustomFont, };
@@ -6,5 +6,5 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- export { CanvasStateV1Schema, SerializedArtboardSchema, AnyElementConfigSchema, AnyTransformDataSchema, StrokeConfigSchema, KnockoutConfigSchema, DistressEffectSchema, MaskDefinitionSchema, CharacterStyleSchema, TextSpanSchema, RichTextSchema, ClipShapeSchema, serializeState, deserializeState, validateState, migrateState, } from './CanvasStateV1.js';
10
- export type { CanvasStateV1, SerializedArtboard, SerializedElement, DeserializeResult, ValidationResult, } from './CanvasStateV1.js';
9
+ export { CanvasStateV1Schema, SerializedArtboardSchema, AnyElementConfigSchema, AnyTransformDataSchema, StrokeConfigSchema, KnockoutConfigSchema, DistressEffectSchema, MaskDefinitionSchema, CharacterStyleSchema, TextSpanSchema, RichTextSchema, ClipShapeSchema, CustomFontSchema, serializeState, deserializeState, validateState, migrateState, } from './CanvasStateV1.js';
10
+ export type { CanvasStateV1, SerializedArtboard, SerializedElement, DeserializeResult, ValidationResult, SerializeStateOptions, CustomFont, } from './CanvasStateV1.js';
@@ -0,0 +1,505 @@
1
+ import { E as x, z as V, T as B, I as M, C as st } from "./FontAnalyzer-BYhwxDhe.js";
2
+ import { l as q, u as ot, i as it, e as O, c as S, j as rt } from "./design-builder-CBG2hKc8.js";
3
+ import { toOptionAttributes as at, toCombinations as ct, getMissingSelections as lt, validateProductSelection as ut, findBestCombination as F, computeDisabledChoices as ft } from "@snowcone-app/sdk";
4
+ import { useEffect as mt, useContext as Z, useId as dt, useLayoutEffect as ht, useRef as C, useMemo as w, useCallback as k, useState as G } from "react";
5
+ import { q as tt, U as gt } from "./useCommands-bPXY9rCh.js";
6
+ import { a as pt, o as N, b as f, n as yt, l as p, u as z, c as bt } from "./CanvasStateV1-jLOibojY.js";
7
+ function R(s) {
8
+ return s.text;
9
+ }
10
+ function $(s) {
11
+ return s.name;
12
+ }
13
+ class jt {
14
+ constructor(t) {
15
+ this.listeners = /* @__PURE__ */ new Set(), this.getState = () => this.state, this.subscribe = (o) => (this.listeners.add(o), () => this.listeners.delete(o)), this.state = t;
16
+ const n = /* @__PURE__ */ new Map();
17
+ for (const o of t.elements) {
18
+ const e = $(o), i = R(o);
19
+ e && typeof i == "string" && n.set(e, i);
20
+ }
21
+ this.seedTextByName = n;
22
+ }
23
+ /** Replace the whole state (the canvas `onChange` path). */
24
+ setState(t) {
25
+ this.state = t, this.emit();
26
+ }
27
+ /** The seed value of a named text layer (for completeness rule C3). */
28
+ seedTextOf(t) {
29
+ return this.seedTextByName.get(t);
30
+ }
31
+ /** Current text of a named text layer, or undefined when absent. */
32
+ textOf(t) {
33
+ const n = this.state.elements.find((o) => $(o) === t);
34
+ return n ? R(n) : void 0;
35
+ }
36
+ /** Kind of the named layer in the CURRENT state ('text' | 'image' | null
37
+ * for unbindable), or undefined when no layer has the name. */
38
+ layerKindOf(t) {
39
+ const n = this.state.elements.find((o) => $(o) === t);
40
+ return n ? q(n) : void 0;
41
+ }
42
+ /** Names of bindable layers in the current state, for error messages. */
43
+ bindableLayerNames() {
44
+ return this.state.elements.filter((t) => q(t) !== null).map($).filter((t) => !!t);
45
+ }
46
+ /**
47
+ * Direct write path for `Editor.TextField` when NO canvas is mounted (with
48
+ * one, writes route through the editor command path and arrive back here
49
+ * via `setState`). Immutable update; last-write-wins.
50
+ */
51
+ updateTextLayer(t, n) {
52
+ let o = !1;
53
+ const e = this.state.elements.map((i) => $(i) !== t ? i : (o = !0, { ...i, text: n }));
54
+ if (!o)
55
+ throw new x(
56
+ "E_LAYER_NOT_FOUND",
57
+ `no layer "${t}" in the current design state. Bindable layers: ${JSON.stringify(this.bindableLayerNames())}.`,
58
+ { layerName: t, validNames: this.bindableLayerNames(), docSlug: "embed-mode" }
59
+ );
60
+ this.setState({ ...this.state, elements: e });
61
+ }
62
+ /** ≥1 element with visible content (completeness rule C4): non-empty text,
63
+ * an image with a URL, or a shape. Hidden elements don't count. */
64
+ hasRenderableElement() {
65
+ return this.state.elements.some((t) => {
66
+ if (t.visible === !1) return !1;
67
+ const n = t.transformType;
68
+ if (n === "image") return !!t.imageUrl;
69
+ if (n === "shape" || n === "group" || n === "path") return !0;
70
+ const o = R(t);
71
+ return typeof o == "string" && o.trim().length > 0;
72
+ });
73
+ }
74
+ emit() {
75
+ for (const t of [...this.listeners]) t();
76
+ }
77
+ }
78
+ function X(s) {
79
+ return (s.choices ?? []).map((t) => typeof t == "string" ? t : t.label ?? "").filter((t) => t !== "");
80
+ }
81
+ function vt(s, t) {
82
+ if (!s.variantId) return {};
83
+ const n = t.find((e) => e.variantId === s.variantId);
84
+ if (!n) return {};
85
+ const o = {};
86
+ for (const [e, i] of Object.entries(n))
87
+ e === "variantId" || e === "price" || (typeof i == "string" ? o[e] = i : typeof i == "number" && (o[e] = String(i)));
88
+ return o;
89
+ }
90
+ class Pt {
91
+ constructor(t) {
92
+ 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 = t, this.attrs = at({ options: t.options }), this.combos = ct({ options: t.options }), this.selection = vt(t, this.combos);
93
+ }
94
+ getAttributes() {
95
+ return this.attrs;
96
+ }
97
+ getSelection() {
98
+ return this.selection;
99
+ }
100
+ getQuantity() {
101
+ return this.quantity;
102
+ }
103
+ /**
104
+ * Select a choice for a variant attribute. Strict + loud (D4/E3): an
105
+ * attribute key or choice that doesn't exist on the product is a wiring
106
+ * mistake, not user state — throws `E_UNKNOWN_OPTION` listing what IS valid.
107
+ */
108
+ select(t, n) {
109
+ const o = this.attrs[t];
110
+ if (!o)
111
+ throw new x(
112
+ "E_UNKNOWN_OPTION",
113
+ `product ${this.product.productId} has no option "${t}". Valid option keys: ${JSON.stringify(Object.keys(this.attrs))}.`,
114
+ { key: t, validKeys: Object.keys(this.attrs), docSlug: "embed-mode" }
115
+ );
116
+ const e = X(o);
117
+ if (e.length > 0 && !e.includes(n))
118
+ throw new x(
119
+ "E_UNKNOWN_OPTION",
120
+ `"${n}" is not a choice of option "${t}" on product ${this.product.productId}. Valid choices: ${JSON.stringify(e)}.`,
121
+ { key: t, choice: n, validChoices: e, docSlug: "embed-mode" }
122
+ );
123
+ this.selection[t] !== n && (this.selection = { ...this.selection, [t]: n }, this.bump());
124
+ }
125
+ /** Quantity is USER state (completeness rule C5 gates on it) — never throws. */
126
+ setQuantity(t) {
127
+ this.quantity !== t && (this.quantity = t, this.bump());
128
+ }
129
+ /** Attribute keys still required (non-optional) but unselected — Buy's
130
+ * `option:<key>` reason codes. */
131
+ missingOptionKeys() {
132
+ return Object.keys(this.attrs).length === 0 ? [] : lt(this.attrs, this.selection);
133
+ }
134
+ /** Full selection resolution (the C1 amendment): true when every required
135
+ * attribute is selected AND the selection matches a valid combination. */
136
+ selectionValid() {
137
+ return Object.keys(this.attrs).length === 0 ? !0 : ut(this.attrs, this.selection, this.combos);
138
+ }
139
+ /** The variant (gvid) the current selection resolves to. Falls back to the
140
+ * product's initial `variantId` while nothing has been selected. */
141
+ resolvedVariantId() {
142
+ if (Object.keys(this.attrs).length === 0 || this.combos.length === 0)
143
+ return this.product.variantId;
144
+ if (!this.selectionValid()) return;
145
+ const t = F(this.selection, this.combos, this.attrs);
146
+ return (typeof (t == null ? void 0 : t.variantId) == "string" && t.variantId !== "" ? t.variantId : void 0) ?? this.product.variantId;
147
+ }
148
+ /**
149
+ * The variant (gvid) to RENDER with — deliberately looser than
150
+ * {@link resolvedVariantId}. That one is Buy's currency: it returns
151
+ * `undefined` while the selection is incomplete so completeness can gate.
152
+ * But the PRODUCT PHOTO must follow the shopper's picks immediately —
153
+ * "choose the Oak frame" should render Oak even while an unrelated axis
154
+ * (size, pocket) is still unselected. This resolves the best combination
155
+ * matching the CURRENT (possibly partial) selection, falling back to the
156
+ * product's initial variant. (Before this split, switching a color swatch
157
+ * with any axis unselected rendered nothing — the mockup silently stayed
158
+ * on the old color.)
159
+ */
160
+ renderVariantId() {
161
+ if (Object.keys(this.attrs).length === 0 || this.combos.length === 0)
162
+ return this.product.variantId;
163
+ const t = F(this.selection, this.combos, this.attrs);
164
+ return (typeof (t == null ? void 0 : t.variantId) == "string" && t.variantId !== "" ? t.variantId : void 0) ?? this.product.variantId;
165
+ }
166
+ /** Price of the resolved combination, when the catalog carries one. */
167
+ resolvedPrice() {
168
+ if (!this.selectionValid()) return;
169
+ const t = F(this.selection, this.combos, this.attrs);
170
+ return typeof (t == null ? void 0 : t.price) == "number" ? t.price : void 0;
171
+ }
172
+ /** Choices invalid given the rest of the current selection — for hosts to
173
+ * disable/grey out in their variant UI. */
174
+ disabledChoices() {
175
+ const t = {};
176
+ for (const [n, o] of Object.entries(this.attrs))
177
+ t[n] = { ...o, choices: X(o) };
178
+ return ft(this.selection, t, this.combos);
179
+ }
180
+ /** The combinations (for payload assembly via `createCartDetail`). */
181
+ getCombinations() {
182
+ return this.combos;
183
+ }
184
+ bump() {
185
+ this.version++;
186
+ for (const t of [...this.listeners]) t();
187
+ }
188
+ }
189
+ function Wt() {
190
+ const { selectedElement: s } = ot();
191
+ return s ?? null;
192
+ }
193
+ const xt = 3e3, J = {
194
+ text: "useTextBinding",
195
+ image: "useImageBinding",
196
+ font: "useFontBinding"
197
+ }, Nt = {
198
+ text: "setText",
199
+ image: "setImageUrl",
200
+ font: "setFontFamily"
201
+ }, $t = {
202
+ text: "text",
203
+ image: "image",
204
+ font: "text"
205
+ }, U = /* @__PURE__ */ new Set();
206
+ function Q(s) {
207
+ return s instanceof B ? "text" : s instanceof M ? "image" : "other";
208
+ }
209
+ function Ct(s, t, n) {
210
+ const o = `[${J[s]}]`, e = `the binding is dead (isConnected: false, ${Nt[s]} is a no-op)`, i = $t[s], a = n.getAllByName(t);
211
+ if (a.length > 0) {
212
+ const c = [...new Set(a.map(Q))], u = i === "text" ? "image" : "text", v = c.length === 1 && c[0] === u ? ` Bind it with ${J[u]}("${t}") instead.` : "";
213
+ return `${o} the layer named "${t}" is ${c.join("/")}, not ${i} — ${e}.${v}`;
214
+ }
215
+ const r = n.getAll().filter((c) => c.name).map((c) => `"${c.name}" (${Q(c)})`), l = r.length ? `Named layers on this canvas: ${r.join(", ")}. Check the name for a typo.` : `No element on this canvas has a name — name one via design().${i}(…, { name: '${t}' }) or the element's \`name\` property.`;
216
+ return `${o} no ${i} layer named "${t}" exists — ${e}. ${l}`;
217
+ }
218
+ function Y(s, t, n, o) {
219
+ mt(() => {
220
+ var a;
221
+ if (o || typeof V > "u" || ((a = V.env) == null ? void 0 : a.NODE_ENV) === "production") return;
222
+ const e = `${s}:${t}`;
223
+ if (U.has(e)) return;
224
+ const i = setTimeout(() => {
225
+ U.has(e) || (U.add(e), console.warn(Ct(s, t, n)));
226
+ }, xt);
227
+ return () => clearTimeout(i);
228
+ }, [s, t, n, o]);
229
+ }
230
+ function A(s) {
231
+ const t = Z(tt), n = dt();
232
+ ht(() => {
233
+ if (t)
234
+ return t.registry.requireCanvas(n, s);
235
+ }, [t, n, s]);
236
+ }
237
+ function L(s) {
238
+ return s instanceof B ? "text" : s instanceof M ? "image" : null;
239
+ }
240
+ const Ot = {
241
+ text: "useTextBinding / useFontBinding",
242
+ image: "useImageBinding"
243
+ };
244
+ function j(s, t, n) {
245
+ const o = it(s), e = o ? s.name : s, i = Z(tt), a = C(null);
246
+ if (o && s.kind !== t)
247
+ throw new x(
248
+ "E_LAYER_WRONG_KIND",
249
+ `layer "${e}" is a ${s.kind} token — this hook binds ${t} layers only (use ${Ot[s.kind]}).`,
250
+ { layerName: e, actualKind: s.kind, expectedKind: t, docSlug: "embed-mode" }
251
+ );
252
+ if (o && i && a.current !== e) {
253
+ const r = n.getAll();
254
+ if (r.length > 0) {
255
+ const l = n.getAllByName(e);
256
+ if (l.length === 0) {
257
+ const c = r.map((u) => ({ name: u.name, kind: L(u) })).filter((u) => !!u.name && u.kind !== null);
258
+ throw new x(
259
+ "E_LAYER_NOT_FOUND",
260
+ `no layer "${e}" in this Root's document — the token was minted from a different design than the one passed to <Editor.Root design={…}>, or the layer was deleted. Bindable layers here: ${JSON.stringify(c.map((u) => u.name))}.`,
261
+ { layerName: e, validNames: c.map((u) => u.name), docSlug: "embed-mode" }
262
+ );
263
+ }
264
+ if (!l.some((c) => L(c) === t)) {
265
+ const c = L(l[0]) ?? "unbindable";
266
+ throw new x(
267
+ "E_LAYER_WRONG_KIND",
268
+ `layer "${e}" is ${c === "unbindable" ? "not a bindable kind" : `an ${c} layer`} — this hook binds ${t} layers only.`,
269
+ { layerName: e, actualKind: c, expectedKind: t, docSlug: "embed-mode" }
270
+ );
271
+ }
272
+ a.current = e;
273
+ }
274
+ }
275
+ return e;
276
+ }
277
+ const Dt = () => {
278
+ }, It = [];
279
+ function Kt(s) {
280
+ const { elementStore: t } = O();
281
+ A("useTextBinding()");
282
+ const n = j(s, "text", t), { executeElementUpdate: o } = S(), e = w(() => t.getAllByName(n).filter((r) => r instanceof B), [t, n]), i = k(
283
+ (a) => {
284
+ for (const r of e) {
285
+ const l = r.clone();
286
+ l.setText(a), o(r, l);
287
+ }
288
+ },
289
+ [e, o]
290
+ );
291
+ return Y("text", n, t, e.length > 0), e.length === 0 ? { text: "", setText: Dt, element: null, elements: It, isConnected: !1 } : {
292
+ text: e[0].getText(),
293
+ setText: i,
294
+ element: e[0],
295
+ elements: e,
296
+ isConnected: !0
297
+ };
298
+ }
299
+ const Tt = () => {
300
+ }, Et = [];
301
+ function Bt(s, t, n) {
302
+ const o = s / t;
303
+ if (n >= o) {
304
+ const e = t, i = e * n, a = s / i;
305
+ return {
306
+ width: i,
307
+ height: e,
308
+ cropX: (1 - a) / 2,
309
+ cropY: 0,
310
+ cropWidth: a,
311
+ cropHeight: 1
312
+ };
313
+ } else {
314
+ const e = s, i = e / n, a = t / i;
315
+ return {
316
+ width: e,
317
+ height: i,
318
+ cropX: 0,
319
+ cropY: (1 - a) / 2,
320
+ cropWidth: 1,
321
+ cropHeight: a
322
+ };
323
+ }
324
+ }
325
+ function Ht(s, t) {
326
+ A("useImageBinding()");
327
+ const n = (t == null ? void 0 : t.fit) ?? "cover", { elementStore: o, setElements: e } = O(), i = j(s, "image", o), { executeElementUpdate: a } = S(), r = w(() => o.getAllByName(i).filter((y) => y instanceof M), [o, i]), l = C(/* @__PURE__ */ new Map());
328
+ for (const d of r)
329
+ if (!l.current.has(d.id)) {
330
+ const y = d.transformData;
331
+ l.current.set(d.id, {
332
+ width: y.width * y.cropWidth,
333
+ height: y.height * y.cropHeight
334
+ });
335
+ }
336
+ const c = C(r);
337
+ c.current = r;
338
+ const u = C(a);
339
+ u.current = a;
340
+ const v = C(e);
341
+ v.current = e;
342
+ const D = k(
343
+ (d) => {
344
+ const y = c.current, P = u.current;
345
+ for (const I of y) {
346
+ const W = l.current.get(I.id);
347
+ if (!W) continue;
348
+ const { width: K, height: H } = W, h = I.clone();
349
+ h.imageLoaded = !1, h.imageElement = null, h.isCropping = !1, h.imageUrl = d, n === "cover" ? (h.preserveDimensions = !0, h.onLoadCallback = (m) => {
350
+ const T = m.imageAspectRatio || 1, g = Bt(K, H, T);
351
+ m.transformData.width = g.width, m.transformData.height = g.height, m.transformData.cropX = g.cropX, m.transformData.cropY = g.cropY, m.transformData.cropWidth = g.cropWidth, m.transformData.cropHeight = g.cropHeight, P(I, m);
352
+ }) : (h.preserveDimensions = !1, h.onLoadCallback = (m) => {
353
+ const T = m.transformData.width, g = m.transformData.height, _ = Math.min(K / T, H / g, 1);
354
+ _ < 1 && (m.transformData.width = T * _, m.transformData.height = g * _), m.transformData.cropX = 0, m.transformData.cropY = 0, m.transformData.cropWidth = 1, m.transformData.cropHeight = 1, P(I, m);
355
+ }), h.loadImage(d);
356
+ }
357
+ },
358
+ [n]
359
+ // Stable deps only — imageElements/executeElementUpdate/setElements accessed via refs
360
+ );
361
+ return Y("image", i, o, r.length > 0), r.length === 0 ? { imageUrl: "", setImageUrl: Tt, element: null, elements: Et, isConnected: !1 } : {
362
+ imageUrl: r[0].imageUrl,
363
+ setImageUrl: D,
364
+ element: r[0],
365
+ elements: r,
366
+ isConnected: !0
367
+ };
368
+ }
369
+ const St = [];
370
+ function E(s) {
371
+ let t = s.trim().toUpperCase();
372
+ return t.length === 4 && t.startsWith("#") && (t = "#" + t[1] + t[1] + t[2] + t[2] + t[3] + t[3]), t;
373
+ }
374
+ function b(s, t) {
375
+ return typeof s == "string" && E(s) === t;
376
+ }
377
+ function et(s, t) {
378
+ var o, e, i;
379
+ const n = s;
380
+ return b(n.color, t) || b((o = n.transformData) == null ? void 0 : o.fillColor, t) || b((e = n.transformData) == null ? void 0 : e.strokeColor, t) || ((i = n.stroke) == null ? void 0 : i.enabled) === !0 && b(n.stroke.color, t);
381
+ }
382
+ function wt(s, t, n) {
383
+ var a, r, l;
384
+ if (!et(s, t)) return null;
385
+ const o = s.clone(), e = s, i = o;
386
+ return b(e.color, t) && (typeof i.setColor == "function" ? i.setColor(n) : i.color = n), b((a = e.transformData) == null ? void 0 : a.fillColor, t) && (i.transformData = { ...i.transformData, fillColor: n }), b((r = e.transformData) == null ? void 0 : r.strokeColor, t) && (i.transformData = { ...i.transformData, strokeColor: n }), ((l = e.stroke) == null ? void 0 : l.enabled) === !0 && b(e.stroke.color, t) && (i.stroke = { ...i.stroke, color: n }), o;
387
+ }
388
+ function Vt(s) {
389
+ const t = rt(s) ? s.value : s, { elementStore: n } = O(), { executeElementUpdate: o } = S();
390
+ A("useColorBinding()");
391
+ const [e, i] = G(() => E(t)), [a, r] = G(t);
392
+ t !== a && (r(t), i(E(t)));
393
+ const l = w(
394
+ () => n.getAll().filter((u) => et(u, e)),
395
+ [n, e]
396
+ ), c = k(
397
+ (u) => {
398
+ const v = E(u);
399
+ if (v !== e) {
400
+ for (const D of n.getAll()) {
401
+ const d = wt(D, e, u);
402
+ d && o(D, d);
403
+ }
404
+ i(v);
405
+ }
406
+ },
407
+ [n, e, o]
408
+ );
409
+ return l.length === 0 ? { color: e, setColor: c, elements: St, isConnected: !1 } : { color: e, setColor: c, elements: l, isConnected: !0 };
410
+ }
411
+ const kt = () => {
412
+ }, At = [];
413
+ function qt(s) {
414
+ const { elementStore: t } = O();
415
+ A("useFontBinding()");
416
+ const n = j(s, "text", t), { executeElementUpdate: o } = S(), e = w(() => t.getAllByName(n).filter((r) => r instanceof B), [t, n]), i = k(
417
+ (a) => {
418
+ gt.loadGoogleFontByName(a);
419
+ for (const r of e) {
420
+ if (r.fontFamily === a) continue;
421
+ st.clearCache(r.fontFamily);
422
+ const l = r.clone();
423
+ l.fontFamily = a, o(r, l);
424
+ }
425
+ },
426
+ [e, o]
427
+ );
428
+ return Y("font", n, t, e.length > 0), e.length === 0 ? { fontFamily: "", setFontFamily: kt, element: null, elements: At, isConnected: !1 } : {
429
+ fontFamily: e[0].fontFamily,
430
+ setFontFamily: i,
431
+ element: e[0],
432
+ elements: e,
433
+ isConnected: !0
434
+ };
435
+ }
436
+ function Gt(s) {
437
+ const { elementStore: t } = O();
438
+ return s ? t.getByName(s) ?? null : null;
439
+ }
440
+ const nt = "personalization", _t = pt("type", [
441
+ N({
442
+ type: p("text"),
443
+ name: f().min(1),
444
+ label: f(),
445
+ placeholder: f().optional(),
446
+ maxLength: yt().int().positive().optional(),
447
+ artboard: f().min(1).optional(),
448
+ group: f().optional()
449
+ }),
450
+ N({
451
+ type: p("image"),
452
+ name: f().min(1),
453
+ label: f(),
454
+ placeholder: f().optional(),
455
+ fit: z([p("cover"), p("contain")]).optional(),
456
+ artboard: f().min(1).optional(),
457
+ group: f().optional()
458
+ }),
459
+ N({
460
+ type: p("color"),
461
+ color: f().min(1),
462
+ label: f(),
463
+ artboard: f().min(1).optional(),
464
+ group: f().optional()
465
+ }),
466
+ N({
467
+ type: p("select"),
468
+ name: f().min(1),
469
+ label: f(),
470
+ property: z([p("text"), p("imageUrl"), p("fontFamily")]),
471
+ options: bt(N({ label: f(), value: f() })).min(1),
472
+ artboard: f().min(1).optional(),
473
+ group: f().optional()
474
+ })
475
+ ]);
476
+ function zt(s) {
477
+ if (!s || typeof s != "object") return [];
478
+ const t = s[nt];
479
+ if (!Array.isArray(t)) return [];
480
+ const n = [];
481
+ for (const o of t) {
482
+ if (!o || typeof o != "object") continue;
483
+ const e = "type" in o ? o : { ...o, type: "text" }, i = _t.safeParse(e);
484
+ i.success && n.push(i.data);
485
+ }
486
+ return n;
487
+ }
488
+ function Xt(s, t) {
489
+ return { ...s, [nt]: t };
490
+ }
491
+ export {
492
+ Pt as C,
493
+ jt as D,
494
+ nt as T,
495
+ Gt as a,
496
+ qt as b,
497
+ Ht as c,
498
+ Wt as d,
499
+ Kt as e,
500
+ X as f,
501
+ zt as r,
502
+ Vt as u,
503
+ Xt as w
504
+ };
505
+ //# sourceMappingURL=template-fields-CvFcr5zy.js.map