@snowcone-app/canvas 0.21.2 → 0.22.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 (47) hide show
  1. package/STYLING_CONTRACT.md +31 -7
  2. package/dist/{CanvasRenderer-B0szG0Us.js → CanvasRenderer-6_HhR4-q.js} +170 -189
  3. package/dist/CanvasRenderer-6_HhR4-q.js.map +1 -0
  4. package/dist/{ContextualToolbars-BYnovWnN.js → ContextualToolbars-BDEt9ND0.js} +1619 -1846
  5. package/dist/ContextualToolbars-BDEt9ND0.js.map +1 -0
  6. package/dist/{EditorCanvasImpl-BmLHjXe7.js → EditorCanvasImpl-BH_bKy-F.js} +5 -5
  7. package/dist/{EditorCanvasImpl-BmLHjXe7.js.map → EditorCanvasImpl-BH_bKy-F.js.map} +1 -1
  8. package/dist/ImagePanel-BwqFDKvW.js +951 -0
  9. package/dist/ImagePanel-BwqFDKvW.js.map +1 -0
  10. package/dist/{SnowconeCanvas-Bj2773ym.js → SnowconeCanvas-Cx7nXlBI.js} +19 -18
  11. package/dist/{SnowconeCanvas-Bj2773ym.js.map → SnowconeCanvas-Cx7nXlBI.js.map} +1 -1
  12. package/dist/{UnifiedFontService-C8gaspRD.js → UnifiedFontService-CC3CSmH5.js} +1042 -984
  13. package/dist/UnifiedFontService-CC3CSmH5.js.map +1 -0
  14. package/dist/advanced.mjs +97 -96
  15. package/dist/advanced.mjs.map +1 -1
  16. package/dist/api/embed.d.ts +3 -1
  17. package/dist/components/editor/EditorCanvas.d.ts +9 -0
  18. package/dist/components/editor/EditorRoot.d.ts +11 -0
  19. package/dist/components/editor/LazyImageBrowser.d.ts +7 -0
  20. package/dist/components/editor/commerceStore.d.ts +13 -0
  21. package/dist/components/embed-realtime/validateEmbed.d.ts +10 -4
  22. package/dist/custom-icons-BW6hqmkL.js +236 -0
  23. package/dist/custom-icons-BW6hqmkL.js.map +1 -0
  24. package/dist/design/product-config.d.ts +48 -0
  25. package/dist/editorErrors-B2mb1sVN.js.map +1 -1
  26. package/dist/{embed-pM7ZkFuz.js → embed-8ecUMMH-.js} +550 -441
  27. package/dist/embed-8ecUMMH-.js.map +1 -0
  28. package/dist/embed.mjs +30 -29
  29. package/dist/errors/editorError.d.ts +3 -0
  30. package/dist/guards-8rkFAMBs.js +24 -0
  31. package/dist/guards-8rkFAMBs.js.map +1 -0
  32. package/dist/index.mjs +5 -5
  33. package/dist/internals.mjs +1 -1
  34. package/dist/style.css +1 -1
  35. package/dist/{template-fields-BsnHMf1i.js → template-fields-C2pSfMBh.js} +57 -39
  36. package/dist/template-fields-C2pSfMBh.js.map +1 -0
  37. package/dist/validation-C9Fe9ahK.js +8367 -0
  38. package/dist/validation-C9Fe9ahK.js.map +1 -0
  39. package/example/package.json +1 -1
  40. package/package.json +1 -1
  41. package/dist/CanvasRenderer-B0szG0Us.js.map +0 -1
  42. package/dist/ContextualToolbars-BYnovWnN.js.map +0 -1
  43. package/dist/UnifiedFontService-C8gaspRD.js.map +0 -1
  44. package/dist/embed-pM7ZkFuz.js.map +0 -1
  45. package/dist/template-fields-BsnHMf1i.js.map +0 -1
  46. package/dist/validation-DdVeWnlo.js +0 -9308
  47. package/dist/validation-DdVeWnlo.js.map +0 -1
@@ -49,6 +49,8 @@ export { useRealtimeMockups } from '../components/embed-realtime/useRealtimeMock
49
49
  export type { UseRealtimeMockupsResult } from '../components/embed-realtime/useRealtimeMockups.js';
50
50
  export { validateEmbed, collectEmbedIssues } from '../components/embed-realtime/validateEmbed.js';
51
51
  export type { ValidateEmbedInput, EmbedIssue } from '../components/embed-realtime/validateEmbed.js';
52
+ export { productToRenderConfig } from '../design/product-config.js';
53
+ export type { ProductRenderConfig, ProductRenderConfigOptions } from '../design/product-config.js';
52
54
  export { design, DesignBuilder, isLayerToken } from '../design/design-builder.js';
53
55
  export type { Anchor, ShapeKind, DesignArtboard, StateArtboard, FromStateOptions, AnyDesign, LayerKind, LayerToken, } from '../design/design-builder.js';
54
56
  export { useEditor } from '../contexts/EditorContext.js';
@@ -77,6 +79,6 @@ export { CanvasServicesProvider, useCanvasServices } from '../contexts/CanvasSer
77
79
  export type { CanvasServices, ResolvedCanvasServices } from '../contexts/CanvasServicesContext.js';
78
80
  export { createProxyImageSearch, createProxyGenerate } from '../services/imageSearch.js';
79
81
  export type { ImageSearchProvider, ImageSearchResult, ImageSearchResponse, ImageSearchParams, GenerateProvider, GenerateParams, GenerateResponse, } from '../services/imageSearch.js';
80
- export type { RenderProduct, RenderProductOptions, RenderProductCombination, RenderProductRequiredPlacement, MockupResult, RealtimeGrant, } from '@snowcone-app/sdk';
82
+ export type { CatalogProduct, RenderProduct, RenderProductOptions, RenderProductCombination, RenderProductRequiredPlacement, MockupResult, RealtimeGrant, } from '@snowcone-app/sdk';
81
83
  export { SnowconeEditor, formatBuyPrice } from '../default-editor/SnowconeEditor.js';
82
84
  export type { SnowconeEditorProps } from '../default-editor/SnowconeEditor.js';
@@ -16,6 +16,15 @@ export interface EditorCanvasProps {
16
16
  children?: ReactNode;
17
17
  /** Cap the canvas height in px. */
18
18
  maxHeight?: number;
19
+ /**
20
+ * The canvas's built-in selection-anchored floating toolbar (default
21
+ * `true`). Pass `false` when your composition owns the contextual zone —
22
+ * e.g. a conditionally-rendered `<Editor.Controls placement="docked"/>` —
23
+ * so suppression is declared HERE, in the type surface, rather than implied
24
+ * by whether a docked Controls happens to be mounted this frame (a mounted
25
+ * docked Controls still suppresses it either way, ADR-0085 §5).
26
+ */
27
+ floatingToolbar?: boolean;
19
28
  className?: string;
20
29
  style?: CSSProperties;
21
30
  }
@@ -2,6 +2,7 @@ import { default as React, ReactNode } from 'react';
2
2
  import { RenderProduct, MockupResult, RealtimeGrant } from '@snowcone-app/sdk';
3
3
  import { AnyDesign } from '../../design/design-builder.js';
4
4
  import { CanvasServices } from '../../contexts/CanvasServicesContext.js';
5
+ import { EmbedIssue } from '../embed-realtime/validateEmbed.js';
5
6
  /**
6
7
  * How the session authorizes. Provide exactly one — the discriminated union makes
7
8
  * "I forgot the grant" a compile error rather than a silent runtime hang.
@@ -41,6 +42,16 @@ export type EditorRootBaseProps = {
41
42
  onMockups?: (results: MockupResult[]) => void;
42
43
  /** Called on a render/timeout error. */
43
44
  onRenderError?: (message: string) => void;
45
+ /**
46
+ * The machine-readable advisory channel (F3's other half): every non-fatal
47
+ * issue — warn-severity wiring problems at mount, the runtime `slow-render`
48
+ * stall — is delivered here, in dev AND prod. Console output is as
49
+ * invisible to a headless agent as it is to production telemetry; assert on
50
+ * this callback instead of scraping logs. (Error-severity wiring issues
51
+ * don't arrive here — they throw `EditorError E_EMBED_CONFIG` into Root's
52
+ * boundary.)
53
+ */
54
+ onIssue?: (issue: EmbedIssue) => void;
44
55
  /** Override the realtime WS endpoint (defaults to the SDK's prod URL). */
45
56
  wsUrl?: string;
46
57
  /**
@@ -0,0 +1,7 @@
1
+ import { default as React } from 'react';
2
+ export declare function LazyImageBrowser({ part, isOpen, onOpenChange, }: {
3
+ /** The part that owns this browser instance — names the error source. */
4
+ part: 'Editor.Add' | 'Editor.Menu';
5
+ isOpen: boolean;
6
+ onOpenChange: (open: boolean) => void;
7
+ }): React.ReactElement | null;
@@ -57,6 +57,19 @@ export declare class CommerceStateStore {
57
57
  /** The variant (gvid) the current selection resolves to. Falls back to the
58
58
  * product's initial `variantId` while nothing has been selected. */
59
59
  resolvedVariantId(): string | undefined;
60
+ /**
61
+ * The variant (gvid) to RENDER with — deliberately looser than
62
+ * {@link resolvedVariantId}. That one is Buy's currency: it returns
63
+ * `undefined` while the selection is incomplete so completeness can gate.
64
+ * But the PRODUCT PHOTO must follow the shopper's picks immediately —
65
+ * "choose the Oak frame" should render Oak even while an unrelated axis
66
+ * (size, pocket) is still unselected. This resolves the best combination
67
+ * matching the CURRENT (possibly partial) selection, falling back to the
68
+ * product's initial variant. (Before this split, switching a color swatch
69
+ * with any axis unselected rendered nothing — the mockup silently stayed
70
+ * on the old color.)
71
+ */
72
+ renderVariantId(): string | undefined;
60
73
  /** Price of the resolved combination, when the catalog carries one. */
61
74
  resolvedPrice(): number | undefined;
62
75
  /** Choices invalid given the rest of the current selection — for hosts to
@@ -6,13 +6,19 @@ export interface ValidateEmbedInput {
6
6
  design: AnyDesign;
7
7
  hasGrant: boolean;
8
8
  }
9
- /** One wiring problem found at mount. `error`-severity issues break the embed
10
- * (no render will ever arrive / it targets the wrong thing); `warn`-severity
11
- * issues are advisory. */
9
+ /** One wiring problem found at mount, or a runtime stall. `error`-severity
10
+ * issues break the embed (no render will ever arrive / it targets the wrong
11
+ * thing); `warn`-severity issues are advisory. Every issue also reaches the
12
+ * host through `Editor.Root`'s `onIssue` — the machine-readable channel
13
+ * (console.warn is as invisible to a headless agent as console.error). */
12
14
  export interface EmbedIssue {
13
15
  severity: 'error' | 'warn';
14
16
  /** Stable issue key, carried into `EditorError.data.issue` in throw mode. */
15
- issue: 'missing-shop' | 'missing-grant' | 'empty-mockup-ids' | 'variant-required' | 'multi-placement' | 'placement-mismatch' | 'duplicate-layer-names';
17
+ issue: 'missing-shop' | 'missing-grant' | 'empty-mockup-ids' | 'variant-required' | 'multi-placement' | 'placement-mismatch' | 'duplicate-layer-names'
18
+ /** Runtime (not mount-time): a render has been in flight past
19
+ * `SLOW_RENDER_HINT_MS` with no mockup and no error — the silent-hang
20
+ * class. The render may still arrive; this is a heads-up. */
21
+ | 'slow-render';
16
22
  message: string;
17
23
  }
18
24
  /** Find every wiring problem. Pure — no logging, no throwing. */
@@ -0,0 +1,236 @@
1
+ import { jsx as i, jsxs as h } from "react/jsx-runtime";
2
+ import * as c from "react";
3
+ import { n as T, y, N as w } from "./UnifiedFontService-CC3CSmH5.js";
4
+ import * as R from "@radix-ui/react-label";
5
+ import { cva as S } from "class-variance-authority";
6
+ import * as g from "@radix-ui/react-tabs";
7
+ const E = c.forwardRef(
8
+ ({
9
+ className: s,
10
+ type: e,
11
+ isInvalid: a,
12
+ startContent: t,
13
+ endContent: o,
14
+ disabled: f,
15
+ readOnly: b,
16
+ required: r,
17
+ variant: n,
18
+ ...d
19
+ }, u) => {
20
+ const m = T(), l = m.variant === "default" || m.variant === "secondary", p = n ?? (l ? "filled" : "default"), v = /* @__PURE__ */ i(
21
+ "input",
22
+ {
23
+ type: e,
24
+ className: y(
25
+ "sc:flex sc:h-10 sc:w-full sc:rounded-input sc:px-2.5 sc:py-1.5 sc:text-sm sc:appearance-none",
26
+ "sc:text-foreground sc:border-none",
27
+ "sc:file:border-0 sc:file:bg-transparent sc:file:text-sm sc:file:font-medium sc:file:text-foreground",
28
+ "sc:placeholder:text-foreground/50",
29
+ "sc:transition-shadow sc:duration-200 sc:ease-out sc:focus-visible:outline-none sc:focus:ring-2 sc:focus:ring-focus sc:focus:ring-offset-1 sc:focus:ring-offset-background",
30
+ "sc:disabled:cursor-not-allowed sc:disabled:opacity-50",
31
+ // Variant styles - semantic: field on page vs field on surface vs toolbar
32
+ p === "default" && "sc:bg-field sc:shadow-soft sc:dark:shadow-none",
33
+ p === "filled" && "sc:bg-[var(--color-field-on-surface)]",
34
+ p === "subtle" && "sc:bg-default sc:dark:bg-[var(--color-default-on-surface)]",
35
+ a && "sc:ring-2 sc:ring-danger sc:focus-visible:ring-danger",
36
+ t && "sc:pl-10",
37
+ o && "sc:pr-10",
38
+ s
39
+ ),
40
+ ref: u,
41
+ disabled: f,
42
+ readOnly: b,
43
+ required: r,
44
+ "aria-invalid": a,
45
+ ...d
46
+ }
47
+ );
48
+ return t || o ? /* @__PURE__ */ h("div", { className: "sc:relative", children: [
49
+ t && /* @__PURE__ */ i("div", { className: "sc:absolute sc:left-3 sc:top-1/2 sc:-translate-y-1/2 sc:text-foreground sc:opacity-40", children: t }),
50
+ v,
51
+ o && /* @__PURE__ */ i("div", { className: "sc:absolute sc:right-3 sc:top-1/2 sc:-translate-y-1/2 sc:text-foreground sc:opacity-40", children: o })
52
+ ] }) : v;
53
+ }
54
+ );
55
+ E.displayName = "Input";
56
+ const C = S(
57
+ "sc:block sc:text-sm sc:leading-none sc:text-foreground sc:peer-disabled:cursor-not-allowed sc:peer-disabled:opacity-70 sc:font-label"
58
+ ), j = c.forwardRef(({ className: s, ...e }, a) => /* @__PURE__ */ i(R.Root, { ref: a, className: y(C(), s), ...e }));
59
+ j.displayName = R.Root.displayName;
60
+ const L = c.forwardRef(
61
+ ({ value: s, onValueChange: e, ...a }, t) => /* @__PURE__ */ i(g.Root, { ref: t, value: s, onValueChange: e, ...a })
62
+ );
63
+ L.displayName = "Tabs";
64
+ const k = c.forwardRef(({ className: s, children: e, ...a }, t) => {
65
+ const o = c.useRef(null), [f, b] = c.useState({
66
+ opacity: 0
67
+ });
68
+ return c.useEffect(() => {
69
+ const r = o.current;
70
+ if (!r) return;
71
+ const n = () => {
72
+ const l = r.querySelector('[data-state="active"]');
73
+ if (l) {
74
+ const p = r.getBoundingClientRect(), v = l.getBoundingClientRect();
75
+ b({
76
+ width: v.width,
77
+ height: v.height,
78
+ left: v.left - p.left,
79
+ opacity: 1
80
+ });
81
+ }
82
+ };
83
+ n();
84
+ const d = new MutationObserver((l) => {
85
+ for (const p of l)
86
+ if (p.type === "attributes" && p.attributeName === "data-state") {
87
+ n();
88
+ break;
89
+ }
90
+ });
91
+ r.querySelectorAll('[role="tab"]').forEach((l) => {
92
+ d.observe(l, { attributes: !0, attributeFilter: ["data-state"] });
93
+ });
94
+ const m = new ResizeObserver(n);
95
+ return m.observe(r), () => {
96
+ d.disconnect(), m.disconnect();
97
+ };
98
+ }, [e]), /* @__PURE__ */ h(
99
+ g.List,
100
+ {
101
+ ref: (r) => {
102
+ o.current = r, typeof t == "function" ? t(r) : t && (t.current = r);
103
+ },
104
+ className: y(
105
+ "sc:relative sc:inline-flex sc:h-10 sc:items-center sc:justify-center sc:rounded-full sc:px-1 sc:text-muted-foreground",
106
+ // Consistent bg-default matches Button secondary variant for toolbar harmony
107
+ "sc:bg-default sc:dark:bg-[var(--color-default-on-surface)]",
108
+ s
109
+ ),
110
+ ...a,
111
+ children: [
112
+ /* @__PURE__ */ i(
113
+ "span",
114
+ {
115
+ className: "sc:absolute sc:top-1/2 sc:-translate-y-1/2 sc:rounded-full sc:bg-surface sc:dark:bg-popover sc:shadow-md sc:transition-[left,width,opacity]",
116
+ style: {
117
+ ...f,
118
+ transitionDuration: "350ms",
119
+ transitionTimingFunction: "cubic-bezier(0.22, 1, 0.36, 1)"
120
+ },
121
+ "aria-hidden": "true"
122
+ }
123
+ ),
124
+ e
125
+ ]
126
+ }
127
+ );
128
+ });
129
+ k.displayName = g.List.displayName;
130
+ const x = c.forwardRef(
131
+ ({ className: s, id: e, value: a, ...t }, o) => {
132
+ const { variant: f } = T(), b = f === "default" || f === "secondary";
133
+ return /* @__PURE__ */ i(
134
+ g.Trigger,
135
+ {
136
+ ref: o,
137
+ value: e ?? a ?? "",
138
+ className: y(
139
+ "sc:relative sc:z-10 sc:inline-flex sc:h-8 sc:items-center sc:justify-center sc:gap-2 sc:whitespace-nowrap sc:rounded-full sc:px-4 sc:text-sm sc:font-label",
140
+ "sc:transition-[color,opacity]",
141
+ "sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus sc:focus-visible:ring-offset-2",
142
+ "sc:disabled:pointer-events-none sc:disabled:opacity-50",
143
+ "sc:data-[state=active]:text-foreground sc:data-[state=active]:opacity-100",
144
+ // Inactive tabs use element opacity (not color opacity) to avoid SVG path overlap issues
145
+ b ? "sc:data-[state=inactive]:text-foreground sc:data-[state=inactive]:opacity-30 sc:data-[state=inactive]:hover:opacity-50" : "sc:data-[state=inactive]:text-foreground sc:data-[state=inactive]:opacity-50 sc:data-[state=inactive]:hover:opacity-70",
146
+ s
147
+ ),
148
+ ...t
149
+ }
150
+ );
151
+ }
152
+ );
153
+ x.displayName = g.Trigger.displayName;
154
+ const N = c.forwardRef(
155
+ ({ className: s, id: e, value: a, ...t }, o) => /* @__PURE__ */ i(
156
+ g.Content,
157
+ {
158
+ ref: o,
159
+ value: e ?? a ?? "",
160
+ className: y(
161
+ "sc:mt-2 sc:ring-offset-background sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus sc:focus-visible:ring-offset-2",
162
+ s
163
+ ),
164
+ ...t
165
+ }
166
+ )
167
+ );
168
+ N.displayName = g.Content.displayName;
169
+ const I = c.forwardRef(
170
+ ({ className: s, ...e }, a) => /* @__PURE__ */ i("div", { ref: a, className: s, ...e })
171
+ );
172
+ I.displayName = "TabsListContainer";
173
+ const O = x, B = N, P = () => null, q = L, z = Object.assign(q, {
174
+ List: k,
175
+ ListContainer: I,
176
+ Tab: O,
177
+ Trigger: x,
178
+ Panel: B,
179
+ Content: N,
180
+ Indicator: P
181
+ }), A = c.forwardRef(
182
+ ({ className: s, children: e, isInvalid: a, disabled: t, required: o, ...f }, b) => {
183
+ const r = c.Children.map(e, (n) => {
184
+ if (!c.isValidElement(n)) return n;
185
+ const d = n.type, u = typeof d == "function" ? d.displayName || d.name : typeof d == "string" ? d : "";
186
+ if (u === "Input" || u === "Textarea" || u === "TextArea" || u === "input" || u === "textarea") {
187
+ const l = n.props;
188
+ return c.cloneElement(n, {
189
+ isInvalid: l.isInvalid ?? a,
190
+ disabled: l.disabled ?? t,
191
+ required: l.required ?? o
192
+ });
193
+ }
194
+ return n;
195
+ });
196
+ return /* @__PURE__ */ i("div", { ref: b, className: y("sc:flex sc:flex-col sc:gap-1.5", s), ...f, children: r });
197
+ }
198
+ );
199
+ A.displayName = "TextField";
200
+ const D = ({ className: s, size: e = 24 }) => /* @__PURE__ */ h(
201
+ "svg",
202
+ {
203
+ viewBox: "0 0 24 24",
204
+ fill: "none",
205
+ stroke: "currentColor",
206
+ strokeWidth: "2",
207
+ strokeLinecap: "round",
208
+ strokeLinejoin: "round",
209
+ width: e,
210
+ height: e,
211
+ className: w("sc:inline-block iconify", s),
212
+ children: [
213
+ /* @__PURE__ */ i("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
214
+ /* @__PURE__ */ i("path", { d: "M3 3v5h5" })
215
+ ]
216
+ }
217
+ ), W = ({ className: s, size: e = 24 }) => /* @__PURE__ */ i(
218
+ "svg",
219
+ {
220
+ viewBox: "0 0 28 28",
221
+ fill: "currentColor",
222
+ width: e,
223
+ height: e,
224
+ className: w("sc:inline-block", s),
225
+ children: /* @__PURE__ */ i("path", { d: "M10.06 18.701a1.628 1.628 0 0 0 2.43-.676l.77-2.34a3.82 3.82 0 0 1 2.416-2.418l2.238-.727a1.6 1.6 0 0 0 .786-.595a1.62 1.62 0 0 0-.849-2.489l-2.215-.72a3.82 3.82 0 0 1-2.42-2.414l-.727-2.237a1.62 1.62 0 0 0-.594-.785a1.655 1.655 0 0 0-1.879 0a1.63 1.63 0 0 0-.6.8L8.68 6.365a3.82 3.82 0 0 1-2.359 2.37l-2.24.726a1.626 1.626 0 0 0 .02 3.073l2.216.72a3.86 3.86 0 0 1 1.816 1.286c.266.343.471.728.606 1.14l.728 2.234c.112.318.32.593.595.787m-3.273-6.873l-2.22-.72s-.085-.038-.085-.108a.14.14 0 0 1 .084-.115L6.8 10.16a5.34 5.34 0 0 0 3.32-3.375l.721-2.219s.043-.084.114-.084s.113.084.113.084l.723 2.223a5.32 5.32 0 0 0 3.377 3.372l2.27.735a.13.13 0 0 1 .078.11a.15.15 0 0 1-.083.113l-2.225.722a5.32 5.32 0 0 0-3.374 3.374l-.715 2.2a.11.11 0 0 1-.115.093c-.104 0-.118-.075-.118-.075l-.723-2.22a5.32 5.32 0 0 0-3.376-3.385m13.017 12.954A1.2 1.2 0 0 0 20.5 25a1.2 1.2 0 0 0 .692-.216a1.2 1.2 0 0 0 .446-.6l.372-1.143a1.61 1.61 0 0 1 1.017-1.02l1.166-.378A1.21 1.21 0 0 0 25 20.505a1.2 1.2 0 0 0-.844-1.146l-1.144-.37a1.61 1.61 0 0 1-1.02-1.018l-.38-1.163a1.2 1.2 0 0 0-2.274.016l-.374 1.146a1.61 1.61 0 0 1-.993 1.017l-1.166.378a1.21 1.21 0 0 0-.807 1.139a1.2 1.2 0 0 0 .823 1.138l1.144.372a1.6 1.6 0 0 1 1.02 1.023l.379 1.163a1.2 1.2 0 0 0 .44.582m-1.371-4.194l-.27-.088l.283-.096a3.12 3.12 0 0 0 1.943-1.974l.088-.27l.089.272a3.11 3.11 0 0 0 1.974 1.975l.293.1l-.271.087a3.12 3.12 0 0 0-1.974 1.977l-.088.27l-.088-.272a3.12 3.12 0 0 0-1.98-1.98" })
226
+ }
227
+ );
228
+ export {
229
+ E as I,
230
+ j as L,
231
+ D as R,
232
+ W as S,
233
+ z as T,
234
+ A as a
235
+ };
236
+ //# sourceMappingURL=custom-icons-BW6hqmkL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-icons-BW6hqmkL.js","sources":["../src/vendor/ui/primitives/input.tsx","../src/vendor/ui/primitives/label.tsx","../src/vendor/ui/primitives/tabs.tsx","../src/vendor/ui/primitives/text-field.tsx","../src/components/ui/custom-icons.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '../lib/utils';\nimport { useSurface } from './surface';\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n /** Whether the input is in an invalid state */\n isInvalid?: boolean;\n /** Content to display at the start of the input */\n startContent?: React.ReactNode;\n /** Content to display at the end of the input */\n endContent?: React.ReactNode;\n /**\n * Visual variant - auto-detected from Surface context, or can be set explicitly\n * - `default`: Standard field styling for forms (bg-field)\n * - `filled`: Field on elevated surface (bg-field-on-surface)\n * - `subtle`: Blends with buttons/tabs in toolbars (bg-default)\n */\n variant?: 'default' | 'filled' | 'subtle';\n}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n (\n {\n className,\n type,\n isInvalid,\n startContent,\n endContent,\n disabled,\n readOnly,\n required,\n variant,\n ...props\n },\n ref\n ) => {\n // Auto-detect variant from Surface context\n const surface = useSurface();\n const isOnSurface = surface.variant === 'default' || surface.variant === 'secondary';\n const effectiveVariant = variant ?? (isOnSurface ? 'filled' : 'default');\n\n const inputElement = (\n <input\n type={type}\n className={cn(\n 'sc:flex sc:h-10 sc:w-full sc:rounded-input sc:px-2.5 sc:py-1.5 sc:text-sm sc:appearance-none',\n 'sc:text-foreground sc:border-none',\n 'sc:file:border-0 sc:file:bg-transparent sc:file:text-sm sc:file:font-medium sc:file:text-foreground',\n 'sc:placeholder:text-foreground/50',\n 'sc:transition-shadow sc:duration-200 sc:ease-out sc:focus-visible:outline-none sc:focus:ring-2 sc:focus:ring-focus sc:focus:ring-offset-1 sc:focus:ring-offset-background',\n 'sc:disabled:cursor-not-allowed sc:disabled:opacity-50',\n // Variant styles - semantic: field on page vs field on surface vs toolbar\n effectiveVariant === 'default' && 'sc:bg-field sc:shadow-soft sc:dark:shadow-none',\n effectiveVariant === 'filled' && 'sc:bg-[var(--color-field-on-surface)]',\n effectiveVariant === 'subtle' && 'sc:bg-default sc:dark:bg-[var(--color-default-on-surface)]',\n isInvalid && 'sc:ring-2 sc:ring-danger sc:focus-visible:ring-danger',\n startContent && 'sc:pl-10',\n endContent && 'sc:pr-10',\n className\n )}\n ref={ref}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n aria-invalid={isInvalid}\n {...props}\n />\n );\n\n if (startContent || endContent) {\n return (\n <div className=\"sc:relative\">\n {startContent && (\n <div className=\"sc:absolute sc:left-3 sc:top-1/2 sc:-translate-y-1/2 sc:text-foreground sc:opacity-40\">{startContent}</div>\n )}\n {inputElement}\n {endContent && (\n <div className=\"sc:absolute sc:right-3 sc:top-1/2 sc:-translate-y-1/2 sc:text-foreground sc:opacity-40\">{endContent}</div>\n )}\n </div>\n );\n }\n\n return inputElement;\n }\n);\nInput.displayName = 'Input';\n\nexport { Input };\n","'use client';\n\nimport * as React from 'react';\nimport * as LabelPrimitive from '@radix-ui/react-label';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/utils';\n\nconst labelVariants = cva(\n 'sc:block sc:text-sm sc:leading-none sc:text-foreground sc:peer-disabled:cursor-not-allowed sc:peer-disabled:opacity-70 sc:font-label'\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n","\"use client\";\n\n'use client';\n\nimport * as React from 'react';\nimport * as TabsPrimitive from '@radix-ui/react-tabs';\nimport { cn } from '../lib/utils';\nimport { useSurface } from './surface';\n\ninterface TabsProps\n extends Omit<React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>, 'value' | 'onValueChange'> {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nconst TabsRoot = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Root>, TabsProps>(\n ({ value, onValueChange, ...props }, ref) => (\n <TabsPrimitive.Root ref={ref} value={value} onValueChange={onValueChange} {...props} />\n )\n);\nTabsRoot.displayName = 'Tabs';\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, children, ...props }, ref) => {\n const listRef = React.useRef<HTMLDivElement>(null);\n const [indicatorStyle, setIndicatorStyle] = React.useState<React.CSSProperties>({\n opacity: 0,\n });\n\n // Update indicator position when active tab changes\n React.useEffect(() => {\n const listElement = listRef.current;\n if (!listElement) return;\n\n const updateIndicator = () => {\n const activeTab = listElement.querySelector('[data-state=\"active\"]') as HTMLElement;\n if (activeTab) {\n const listRect = listElement.getBoundingClientRect();\n const tabRect = activeTab.getBoundingClientRect();\n\n setIndicatorStyle({\n width: tabRect.width,\n height: tabRect.height,\n left: tabRect.left - listRect.left,\n opacity: 1,\n });\n }\n };\n\n // Initial position\n updateIndicator();\n\n // Use MutationObserver to detect when active tab changes\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'attributes' && mutation.attributeName === 'data-state') {\n updateIndicator();\n break;\n }\n }\n });\n\n // Observe all trigger elements for data-state changes\n const triggers = listElement.querySelectorAll('[role=\"tab\"]');\n triggers.forEach((trigger) => {\n observer.observe(trigger, { attributes: true, attributeFilter: ['data-state'] });\n });\n\n // Also update on resize\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(listElement);\n\n return () => {\n observer.disconnect();\n resizeObserver.disconnect();\n };\n }, [children]);\n\n return (\n <TabsPrimitive.List\n ref={(node) => {\n // Handle both refs\n (listRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n }}\n className={cn(\n 'sc:relative sc:inline-flex sc:h-10 sc:items-center sc:justify-center sc:rounded-full sc:px-1 sc:text-muted-foreground',\n // Consistent bg-default matches Button secondary variant for toolbar harmony\n 'sc:bg-default sc:dark:bg-[var(--color-default-on-surface)]',\n className\n )}\n {...props}\n >\n {/* Animated sliding indicator. In dark mode the previous `bg-muted`\n value resolved to the same color as `bg-default` on the container,\n so the active tab was invisible without relying purely on\n shadow-md. `bg-popover` lands ~0.08 lightness above `bg-default`\n in dark mode for a clearly visible elevation. */}\n <span\n className=\"sc:absolute sc:top-1/2 sc:-translate-y-1/2 sc:rounded-full sc:bg-surface sc:dark:bg-popover sc:shadow-md sc:transition-[left,width,opacity]\"\n style={{\n ...indicatorStyle,\n transitionDuration: '350ms',\n transitionTimingFunction: 'cubic-bezier(0.22, 1, 0.36, 1)',\n }}\n aria-hidden=\"true\"\n />\n {children}\n </TabsPrimitive.List>\n );\n});\nTabsList.displayName = TabsPrimitive.List.displayName;\n\ninterface TabsTriggerProps extends Omit<React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>, 'value'> {\n // HeroUI uses 'id' for tab identification, Radix uses 'value'\n // At least one must be provided\n id?: string;\n value?: string;\n}\n\nconst TabsTrigger = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Trigger>, TabsTriggerProps>(\n ({ className, id, value, ...props }, ref) => {\n const { variant: surfaceVariant } = useSurface();\n const isOnSurface = surfaceVariant === 'default' || surfaceVariant === 'secondary';\n\n return (\n <TabsPrimitive.Trigger\n ref={ref}\n value={id ?? value ?? ''}\n className={cn(\n 'sc:relative sc:z-10 sc:inline-flex sc:h-8 sc:items-center sc:justify-center sc:gap-2 sc:whitespace-nowrap sc:rounded-full sc:px-4 sc:text-sm sc:font-label',\n 'sc:transition-[color,opacity]',\n 'sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus sc:focus-visible:ring-offset-2',\n 'sc:disabled:pointer-events-none sc:disabled:opacity-50',\n 'sc:data-[state=active]:text-foreground sc:data-[state=active]:opacity-100',\n // Inactive tabs use element opacity (not color opacity) to avoid SVG path overlap issues\n isOnSurface\n ? 'sc:data-[state=inactive]:text-foreground sc:data-[state=inactive]:opacity-30 sc:data-[state=inactive]:hover:opacity-50'\n : 'sc:data-[state=inactive]:text-foreground sc:data-[state=inactive]:opacity-50 sc:data-[state=inactive]:hover:opacity-70',\n className\n )}\n {...props}\n />\n );\n }\n);\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\ninterface TabsContentProps extends Omit<React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>, 'value'> {\n // HeroUI uses 'id' for panel identification, Radix uses 'value'\n // At least one must be provided\n id?: string;\n value?: string;\n}\n\nconst TabsContent = React.forwardRef<React.ElementRef<typeof TabsPrimitive.Content>, TabsContentProps>(\n ({ className, id, value, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n value={id ?? value ?? ''}\n className={cn(\n 'sc:mt-2 sc:ring-offset-background sc:focus-visible:outline-none sc:focus-visible:ring-2 sc:focus-visible:ring-focus sc:focus-visible:ring-offset-2',\n className\n )}\n {...props}\n />\n )\n);\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\n// HeroUI ListContainer - wrapper for the tabs list (passthrough div)\nconst TabsListContainer = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => <div ref={ref} className={className} {...props} />\n);\nTabsListContainer.displayName = 'TabsListContainer';\n\n// HeroUI Tab - alias for TabsTrigger\nconst TabsTab = TabsTrigger;\n\n// HeroUI Panel - alias for TabsContent\nconst TabsPanel = TabsContent;\n\n// HeroUI Indicator - now rendered automatically by TabsList, this is a no-op\nconst TabsIndicator = () => null;\n\n// Create compound component with all aliases\nconst TabsBase = TabsRoot;\n\n// HeroUI-compatible compound component\nconst Tabs = Object.assign(TabsBase, {\n List: TabsList,\n ListContainer: TabsListContainer,\n Tab: TabsTab,\n Trigger: TabsTrigger,\n Panel: TabsPanel,\n Content: TabsContent,\n Indicator: TabsIndicator,\n});\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, TabsRoot };\n","'use client';\n\nimport * as React from 'react';\nimport { cn } from '../lib/utils';\n\nexport interface TextFieldProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Accessible label for the input.\n * If not provided via children, use aria-label on the div.\n */\n 'aria-label'?: string;\n /**\n * Error state - applies error styling to children inputs\n */\n isInvalid?: boolean;\n /**\n * Disabled state - disables all children inputs\n */\n disabled?: boolean;\n /**\n * Required state - marks all children inputs as required\n */\n required?: boolean;\n}\n\n/**\n * TextField - A wrapper component that groups Label and Input together.\n *\n * Groups Label and Input with a convenient wrapper that can propagate\n * disabled, required, and invalid states to child inputs.\n *\n * @example\n * ```tsx\n * <TextField>\n * <Label>Email</Label>\n * <Input type=\"email\" placeholder=\"Enter email...\" />\n * </TextField>\n * ```\n */\nconst TextField = React.forwardRef<HTMLDivElement, TextFieldProps>(\n ({ className, children, isInvalid, disabled, required, ...props }, ref) => {\n // Clone children to pass through state props to Input/Textarea children\n const enhancedChildren = React.Children.map(children, (child) => {\n if (!React.isValidElement(child)) return child;\n\n // Get the display name or type name to check if it's an input component\n const childType = child.type;\n const displayName =\n typeof childType === 'function'\n ? (childType as React.ComponentType).displayName || childType.name\n : typeof childType === 'string'\n ? childType\n : '';\n\n // Check if this is an input-like element that should receive state props\n const isInputElement =\n displayName === 'Input' ||\n displayName === 'Textarea' ||\n displayName === 'TextArea' ||\n displayName === 'input' ||\n displayName === 'textarea';\n\n if (isInputElement) {\n const childProps = child.props as Record<string, unknown>;\n return React.cloneElement(child as React.ReactElement<Record<string, unknown>>, {\n isInvalid: (childProps.isInvalid as boolean) ?? isInvalid,\n disabled: (childProps.disabled as boolean) ?? disabled,\n required: (childProps.required as boolean) ?? required,\n });\n }\n\n return child;\n });\n\n return (\n <div ref={ref} className={cn('sc:flex sc:flex-col sc:gap-1.5', className)} {...props}>\n {enhancedChildren}\n </div>\n );\n }\n);\nTextField.displayName = 'TextField';\n\nexport { TextField };\n","/**\n * Custom Icons - Centered versions of icons from various sets\n *\n * These are copies of icons with adjusted viewBox to center properly.\n * Use these instead of the original iconify versions for consistent alignment.\n */\n\nimport React from 'react';\nimport { cn } from '../../lib/utils';\n\ninterface IconProps {\n className?: string;\n size?: number | string;\n}\n\n/**\n * Rotate Left icon (lucide-style stroked version)\n */\nexport const RotateLeftIcon: React.FC<IconProps> = ({ className, size = 24 }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n width={size}\n height={size}\n className={cn('sc:inline-block iconify', className)}\n >\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n);\n\n/**\n * Rotate Right icon (lucide-style stroked version)\n */\nexport const RotateRightIcon: React.FC<IconProps> = ({ className, size = 24 }) => (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n width={size}\n height={size}\n className={cn('sc:inline-block iconify', className)}\n >\n <path d=\"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8\" />\n <path d=\"M21 3v5h-5\" />\n </svg>\n);\n\n/**\n * Sparkles icon (based on gravity-ui:sparkles)\n * Two sparkle shapes - main 4-point star and smaller accent\n */\nexport const SparklesIcon: React.FC<IconProps> = ({ className, size = 24 }) => (\n <svg\n viewBox=\"0 0 28 28\"\n fill=\"currentColor\"\n width={size}\n height={size}\n className={cn('sc:inline-block', className)}\n >\n <path d=\"M10.06 18.701a1.628 1.628 0 0 0 2.43-.676l.77-2.34a3.82 3.82 0 0 1 2.416-2.418l2.238-.727a1.6 1.6 0 0 0 .786-.595a1.62 1.62 0 0 0-.849-2.489l-2.215-.72a3.82 3.82 0 0 1-2.42-2.414l-.727-2.237a1.62 1.62 0 0 0-.594-.785a1.655 1.655 0 0 0-1.879 0a1.63 1.63 0 0 0-.6.8L8.68 6.365a3.82 3.82 0 0 1-2.359 2.37l-2.24.726a1.626 1.626 0 0 0 .02 3.073l2.216.72a3.86 3.86 0 0 1 1.816 1.286c.266.343.471.728.606 1.14l.728 2.234c.112.318.32.593.595.787m-3.273-6.873l-2.22-.72s-.085-.038-.085-.108a.14.14 0 0 1 .084-.115L6.8 10.16a5.34 5.34 0 0 0 3.32-3.375l.721-2.219s.043-.084.114-.084s.113.084.113.084l.723 2.223a5.32 5.32 0 0 0 3.377 3.372l2.27.735a.13.13 0 0 1 .078.11a.15.15 0 0 1-.083.113l-2.225.722a5.32 5.32 0 0 0-3.374 3.374l-.715 2.2a.11.11 0 0 1-.115.093c-.104 0-.118-.075-.118-.075l-.723-2.22a5.32 5.32 0 0 0-3.376-3.385m13.017 12.954A1.2 1.2 0 0 0 20.5 25a1.2 1.2 0 0 0 .692-.216a1.2 1.2 0 0 0 .446-.6l.372-1.143a1.61 1.61 0 0 1 1.017-1.02l1.166-.378A1.21 1.21 0 0 0 25 20.505a1.2 1.2 0 0 0-.844-1.146l-1.144-.37a1.61 1.61 0 0 1-1.02-1.018l-.38-1.163a1.2 1.2 0 0 0-2.274.016l-.374 1.146a1.61 1.61 0 0 1-.993 1.017l-1.166.378a1.21 1.21 0 0 0-.807 1.139a1.2 1.2 0 0 0 .823 1.138l1.144.372a1.6 1.6 0 0 1 1.02 1.023l.379 1.163a1.2 1.2 0 0 0 .44.582m-1.371-4.194l-.27-.088l.283-.096a3.12 3.12 0 0 0 1.943-1.974l.088-.27l.089.272a3.11 3.11 0 0 0 1.974 1.975l.293.1l-.271.087a3.12 3.12 0 0 0-1.974 1.977l-.088.27l-.088-.272a3.12 3.12 0 0 0-1.98-1.98\" />\n </svg>\n);\n\n// Map of icon names to components for easy lookup\nexport const CustomIcons = {\n 'rotate-left': RotateLeftIcon,\n 'rotate-right': RotateRightIcon,\n 'sparkles': SparklesIcon,\n} as const;\n\nexport type CustomIconName = keyof typeof CustomIcons;\n"],"names":["Input","React","className","type","isInvalid","startContent","endContent","disabled","readOnly","required","variant","props","ref","surface","useSurface","isOnSurface","effectiveVariant","inputElement","jsx","cn","jsxs","labelVariants","cva","Label","LabelPrimitive","TabsRoot","value","onValueChange","TabsPrimitive","TabsList","children","listRef","indicatorStyle","setIndicatorStyle","listElement","updateIndicator","activeTab","listRect","tabRect","observer","mutations","mutation","trigger","resizeObserver","node","TabsTrigger","id","surfaceVariant","TabsContent","TabsListContainer","TabsTab","TabsPanel","TabsIndicator","TabsBase","Tabs","TextField","enhancedChildren","child","childType","displayName","childProps","RotateLeftIcon","size","SparklesIcon"],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { CatalogProduct, RenderProduct } from '@snowcone-app/sdk';
2
+ export interface ProductRenderConfigOptions {
3
+ /** Placement label to edit (e.g. "Back"). Defaults to the product's first
4
+ * renderable image placement. Unknown labels throw with `validNames`. */
5
+ placement?: string;
6
+ /** Render width passthrough (`product.width`) — bump to ~2000 for a crisp
7
+ * full-bleed preview. `Editor.Preview` hints in dev when it's too low. */
8
+ width?: number;
9
+ }
10
+ export interface ProductRenderConfig {
11
+ /** Ready for `<Editor.Root product={…}>` — advisory fields included so
12
+ * wiring mistakes fail loud at mount. */
13
+ product: RenderProduct;
14
+ /** The placement being edited — seed `design({ name: label, width, height })`
15
+ * from exactly these values. */
16
+ placement: {
17
+ label: string;
18
+ /** Artboard pixel dimensions (the placement's print-file geometry). */
19
+ width: number;
20
+ height: number;
21
+ /** width / height — for sizing the host's canvas stage. */
22
+ aspectRatio: number;
23
+ };
24
+ /** Every editable (non-color) placement label — placement pickers. */
25
+ placementLabels: string[];
26
+ /** First mockup scene that photographs the rendered variant — the default
27
+ * `<Editor.Preview mockupId>`. */
28
+ primaryMockupId: string;
29
+ /** The variant the config renders against (`undefined` when no color axis
30
+ * gates rendering). Already set on `product`. */
31
+ variantId: string | undefined;
32
+ }
33
+ /**
34
+ * Derive the full embed wiring from one `getProduct()` payload.
35
+ *
36
+ * ```ts
37
+ * const config = productToRenderConfig(await getProduct('BEEB77'));
38
+ * const seed = design({ name: config.placement.label, ...config.placement })
39
+ * .text('hello', { anchor: 'center', name: 'headline' });
40
+ *
41
+ * <Editor.Root shop={SHOP} product={config.product} design={seed} grantUrl="/api/realtime-grant">
42
+ * ```
43
+ *
44
+ * @throws EditorError `E_PRODUCT_NOT_RENDERABLE` when the product has no
45
+ * renderable image placement, no mockup scenes, or `options.placement`
46
+ * names a placement that doesn't exist (`data.validNames` lists the real ones).
47
+ */
48
+ export declare function productToRenderConfig(catalogProduct: CatalogProduct, options?: ProductRenderConfigOptions): ProductRenderConfig;