@snowcone-app/canvas 0.20.0 → 0.21.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.
- package/README.md +124 -13
- package/STYLING_CONTRACT.md +427 -0
- package/dist/{EditorCanvasImpl-UBKdG0c0.js → EditorCanvasImpl-2ZgIUVpK.js} +4 -4
- package/dist/{EditorCanvasImpl-UBKdG0c0.js.map → EditorCanvasImpl-2ZgIUVpK.js.map} +1 -1
- package/dist/{SnowconeCanvas-B01o5LmS.js → SnowconeCanvas-DqZicdxK.js} +2 -2
- package/dist/{SnowconeCanvas-B01o5LmS.js.map → SnowconeCanvas-DqZicdxK.js.map} +1 -1
- package/dist/UnifiedFontService-B1PGVu8O.js.map +1 -1
- package/dist/components/editor/EditorAdd.d.ts +2 -1
- package/dist/components/editor/EditorBuy.d.ts +2 -1
- package/dist/components/editor/EditorCanvas.d.ts +2 -1
- package/dist/components/editor/EditorControls.d.ts +2 -1
- package/dist/components/editor/EditorHistory.d.ts +2 -1
- package/dist/components/editor/EditorLayers.d.ts +2 -1
- package/dist/components/editor/EditorMenu.d.ts +2 -1
- package/dist/components/editor/EditorPreview.d.ts +2 -1
- package/dist/components/editor/EditorRoot.d.ts +2 -1
- package/dist/components/editor/EditorTextField.d.ts +2 -1
- package/dist/components/editor/useProductSelection.d.ts +2 -1
- package/dist/components/embed-realtime/context.d.ts +1 -1
- package/dist/contexts/EditorContext.d.ts +1 -1
- package/dist/default-editor/SnowconeEditor.d.ts +2 -1
- package/dist/design/design-builder.d.ts +2 -2
- package/dist/{design-builder-DMKx9UXD.js → design-builder-CPF60RU2.js} +1 -2
- package/dist/design-builder-CPF60RU2.js.map +1 -0
- package/dist/editorErrors-CK1RISFa.js.map +1 -1
- package/dist/{embed-uALcVSYN.js → embed-2tp6Ue90.js} +4 -4
- package/dist/embed-2tp6Ue90.js.map +1 -0
- package/dist/embed.mjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/{template-fields-B9RagVpT.js → template-fields-FgImixyv.js} +2 -2
- package/dist/{template-fields-B9RagVpT.js.map → template-fields-FgImixyv.js.map} +1 -1
- package/dist/testing/assertRenders.d.ts +2 -1
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/example/README.md +179 -0
- package/example/index.html +12 -0
- package/example/package.json +24 -0
- package/example/src/Widget.tsx +62 -0
- package/example/src/main.tsx +9 -0
- package/example/tsconfig.json +14 -0
- package/example/vite.config.ts +11 -0
- package/llms.txt +45 -0
- package/package.json +6 -2
- package/dist/design-builder-DMKx9UXD.js.map +0 -1
- package/dist/embed-uALcVSYN.js.map +0 -1
|
@@ -6,6 +6,7 @@ export interface EditorAddProps {
|
|
|
6
6
|
/**
|
|
7
7
|
* The ⊕ add-element menu.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
10
|
+
* surface, CI-gated); additive-only from here.
|
|
10
11
|
*/
|
|
11
12
|
export declare function EditorAdd({ className, style }: EditorAddProps): React.ReactElement;
|
|
@@ -55,6 +55,7 @@ export declare function evaluateBuyCompleteness(input: {
|
|
|
55
55
|
/**
|
|
56
56
|
* The add-to-cart button, completeness-gated and payload-assembling.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
58
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
59
|
+
* surface, CI-gated); additive-only from here.
|
|
59
60
|
*/
|
|
60
61
|
export declare function EditorBuy(props: EditorBuyProps): React.ReactElement;
|
|
@@ -24,7 +24,8 @@ export interface EditorCanvasProps {
|
|
|
24
24
|
* (`E_DUPLICATE_CANVAS`); mount none for the no-canvas personalization
|
|
25
25
|
* composition.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
27
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
28
|
+
* surface, CI-gated); additive-only from here.
|
|
28
29
|
*/
|
|
29
30
|
export declare const EditorCanvas: React.ForwardRefExoticComponent<EditorCanvasProps & React.RefAttributes<SnowconeCanvasHandle>>;
|
|
30
31
|
export type { EditorCanvasImplProps };
|
|
@@ -9,6 +9,7 @@ export interface EditorControlsProps {
|
|
|
9
9
|
/**
|
|
10
10
|
* The contextual controls zone.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
13
|
+
* surface, CI-gated); additive-only from here.
|
|
13
14
|
*/
|
|
14
15
|
export declare function EditorControls({ placement, className, style, }: EditorControlsProps): React.ReactElement | null;
|
|
@@ -6,6 +6,7 @@ export interface EditorHistoryProps {
|
|
|
6
6
|
/**
|
|
7
7
|
* Undo/redo for the editor document.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
10
|
+
* surface, CI-gated); additive-only from here.
|
|
10
11
|
*/
|
|
11
12
|
export declare function EditorHistory({ className, style }: EditorHistoryProps): React.ReactElement;
|
|
@@ -11,6 +11,7 @@ export interface EditorLayersProps {
|
|
|
11
11
|
* layers read-only); with a mounted `Editor.Canvas` it is the live document
|
|
12
12
|
* panel.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
15
|
+
* surface, CI-gated); additive-only from here.
|
|
15
16
|
*/
|
|
16
17
|
export declare function EditorLayers({ direction, className, style, }: EditorLayersProps): React.ReactElement;
|
|
@@ -6,6 +6,7 @@ export interface EditorMenuProps {
|
|
|
6
6
|
/**
|
|
7
7
|
* The editor's overflow menu.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
10
|
+
* surface, CI-gated); additive-only from here.
|
|
10
11
|
*/
|
|
11
12
|
export declare function EditorMenu({ className, style }: EditorMenuProps): React.ReactElement;
|
|
@@ -34,6 +34,7 @@ export declare function resolutionMismatchHint(naturalWidth: number, devicePx: n
|
|
|
34
34
|
/**
|
|
35
35
|
* The live product mockup. Mount as many as you need under one Root.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
38
|
+
* surface, CI-gated); additive-only from here.
|
|
38
39
|
*/
|
|
39
40
|
export declare function EditorPreview({ mockupId, fallback, alt, className, style, }: EditorPreviewProps): React.ReactElement;
|
|
@@ -81,6 +81,7 @@ export declare function slowRenderHint(placement: string): string;
|
|
|
81
81
|
* model; see `RealtimeEmbed` for the legacy turnkey wrapper this was
|
|
82
82
|
* extracted from.
|
|
83
83
|
*
|
|
84
|
-
*
|
|
84
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
85
|
+
* surface, CI-gated); additive-only from here.
|
|
85
86
|
*/
|
|
86
87
|
export declare function EditorRoot(props: EditorRootProps): React.ReactElement;
|
|
@@ -17,6 +17,7 @@ export interface EditorTextFieldProps {
|
|
|
17
17
|
* A text input bound to a design layer. Works with or without a mounted
|
|
18
18
|
* `Editor.Canvas`.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
21
|
+
* surface, CI-gated); additive-only from here.
|
|
21
22
|
*/
|
|
22
23
|
export declare function EditorTextField(props: EditorTextFieldProps): React.ReactElement;
|
|
@@ -34,6 +34,7 @@ export interface ProductSelectionApi {
|
|
|
34
34
|
/**
|
|
35
35
|
* Read/write the Buy-owned product selection under this `Editor.Root`.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
38
|
+
* surface, CI-gated); additive-only from here.
|
|
38
39
|
*/
|
|
39
40
|
export declare function useProductSelection(): ProductSelectionApi;
|
|
@@ -39,7 +39,7 @@ export interface EmbedContextValue {
|
|
|
39
39
|
* write, last-write-wins. Commerce parts read THIS, never the seed. Stable
|
|
40
40
|
* identity; subscribe for updates (`useSyncExternalStore`-shaped).
|
|
41
41
|
*
|
|
42
|
-
*
|
|
42
|
+
* Stable since ADR-0085 P4; additive-only from here.
|
|
43
43
|
*/
|
|
44
44
|
stateStore: DesignStateStore;
|
|
45
45
|
}
|
|
@@ -191,7 +191,7 @@ export declare const EditorProvider: React.FC<EditorProviderProps>;
|
|
|
191
191
|
* - **Undo/redo**: `undo`, `redo`, `canUndo`, `canRedo`
|
|
192
192
|
*
|
|
193
193
|
* Everything else on the returned value is `@experimental` and may change in
|
|
194
|
-
* minors
|
|
194
|
+
* minors — the stable subset above is the documented headless contract.
|
|
195
195
|
*
|
|
196
196
|
* Must be called inside an `EditorProvider`. For focused access, prefer the
|
|
197
197
|
* sub-context hooks which expose smaller slices of state and cause fewer re-renders:
|
|
@@ -34,6 +34,7 @@ export declare function formatBuyPrice(cents: number): string;
|
|
|
34
34
|
* Customizing beyond the props means composing `Editor.*` yourself — this
|
|
35
35
|
* component is ~300 lines of public-surface composition you can copy and own.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* Stable since ADR-0085 P4 (this component IS the gate artifact — composed
|
|
38
|
+
* only from the public embed entry); additive-only from here.
|
|
38
39
|
*/
|
|
39
40
|
export declare function SnowconeEditor(props: SnowconeEditorProps): React.ReactElement;
|
|
@@ -111,7 +111,8 @@ declare const LAYER_TOKEN_BRAND: unique symbol;
|
|
|
111
111
|
* accepts — bare strings are rejected (`E_LAYER_TOKEN_REQUIRED`). Mint with
|
|
112
112
|
* {@link DesignBuilder.layer}; not constructible by hand (branded).
|
|
113
113
|
*
|
|
114
|
-
*
|
|
114
|
+
* Stable since ADR-0085 P4 (the default editor is rebuilt from this public
|
|
115
|
+
* surface, CI-gated); additive-only from here.
|
|
115
116
|
*/
|
|
116
117
|
export interface LayerToken<K extends LayerKind = LayerKind, N extends string = string> {
|
|
117
118
|
readonly kind: K;
|
|
@@ -201,7 +202,6 @@ export declare class DesignBuilder<out TText extends string = string, out TImage
|
|
|
201
202
|
* the valid names per kind.
|
|
202
203
|
* @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that
|
|
203
204
|
* is not bindable (shape/group/path).
|
|
204
|
-
* @experimental ADR-0085; subject to change until the P4 gate.
|
|
205
205
|
*/
|
|
206
206
|
layer<N extends TText | TImage>(name: N): LayerToken<InferKind<N, TText, TImage>, N>;
|
|
207
207
|
/** Names of bindable layers of one kind, in stack order. @internal */
|
|
@@ -262,7 +262,6 @@ class m {
|
|
|
262
262
|
* the valid names per kind.
|
|
263
263
|
* @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that
|
|
264
264
|
* is not bindable (shape/group/path).
|
|
265
|
-
* @experimental ADR-0085; subject to change until the P4 gate.
|
|
266
265
|
*/
|
|
267
266
|
layer(r) {
|
|
268
267
|
const e = this.els.find((o) => o.name === r);
|
|
@@ -365,4 +364,4 @@ export {
|
|
|
365
364
|
v as l,
|
|
366
365
|
x as s
|
|
367
366
|
};
|
|
368
|
-
//# sourceMappingURL=design-builder-
|
|
367
|
+
//# sourceMappingURL=design-builder-CPF60RU2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"design-builder-CPF60RU2.js","sources":["../src/rendering/serialize-for-server.ts","../src/design/design-builder.ts"],"sourcesContent":["/**\n * Serialize canvas state (from onChange/toJSON) to the format the export worker expects.\n *\n * This bridges the gap between SnowconeCanvas.onChange() output (element configs with\n * transformType and nested transformData) and the export worker's expected format\n * (elements with 'type' field and flattened image properties).\n *\n * Use this when sending canvas state to a server-side renderer that runs the export\n * worker bundle. The client-side serializeForWorkerExport() requires live element\n * instances; this function works on plain JSON from onChange/toJSON.\n */\n\n/**\n * Wire-schema version of the server-render `canvas_state` payload (ADR-0079).\n *\n * This is a PUBLIC contract: third parties send `ServerRenderRequest` (produced\n * by {@link serializeStateForServer}) to Snowcone's realtime renderer. Bump the\n * MAJOR whenever the element/artboard shape changes incompatibly so a renderer\n * can reject states it cannot render instead of silently mis-rendering. Servers\n * treat a MISSING `schemaVersion` as `1` — legacy clients that predate the\n * stamp keep working.\n */\nexport const CANVAS_STATE_SCHEMA_VERSION = 1;\n\n// NOTE: this is a `type`, not an `interface`, ON PURPOSE. The SDK's\n// `RenderSession.renderState(placement, state)` takes a `RenderState` that has a\n// string index signature (`[key: string]: unknown`). A named `interface` does\n// NOT get TypeScript's implicit index signature, so an `interface` value is not\n// assignable to an indexed type — passing `serializeStateForServer()`'s result\n// straight into `renderState()` (the headline realtime flow) fails with TS2345\n// \"Index signature for type 'string' is missing\". A `type` alias DOES get the\n// implicit signature, so the flow type-checks with no cast. Do not convert this\n// back to `interface`. Pinned by @snowcone-app/contracts\n// (src/realtime-getting-started.contract.ts).\nexport type ServerRenderRequest = {\n /** Wire-schema version — see {@link CANVAS_STATE_SCHEMA_VERSION}. */\n schemaVersion: number;\n artboards: Array<{\n id: string;\n name: string;\n x: number;\n y: number;\n width: number;\n height: number;\n backgroundColor: string;\n exportBackground: boolean;\n elements: any[];\n distressTexture?: any;\n imageMask?: any;\n }>;\n};\n\n/**\n * Quantize a geometry value to a stable precision (0.01px).\n *\n * `SnowconeCanvas.onChange` fires on the render loop and reports element\n * geometry with sub-pixel float drift (e.g. `height: 355.9609375`) even when the\n * design is idle. Serialized verbatim, every frame produces a *different* wire\n * state, so a `onChange → renderState` wiring streams renders continuously while\n * the user does nothing — silently burning the realtime render budget. Rounding\n * geometry to a precision well below a pixel collapses that drift so identical\n * idle frames serialize identically (and the SDK's send-dedupe drops them).\n */\nfunction q(n: unknown): unknown {\n return typeof n === 'number' && Number.isFinite(n) ? Math.round(n * 100) / 100 : n;\n}\n\n/** Return a shallow copy with geometry fields quantized (see {@link q}). */\nfunction quantizeGeometry<T extends Record<string, any>>(el: T): T {\n return {\n ...el,\n ...(el.x !== undefined && { x: q(el.x) }),\n ...(el.y !== undefined && { y: q(el.y) }),\n ...(el.width !== undefined && { width: q(el.width) }),\n ...(el.height !== undefined && { height: q(el.height) }),\n ...(el.rotation !== undefined && { rotation: q(el.rotation) }),\n };\n}\n\n/**\n * Convert onChange canvas state to the server render format.\n *\n * Emits EVERY artboard in the state, each holding its own elements: elements\n * are grouped by their `artboard` tag (the artboard NAME `SnowconeCanvas.onChange`\n * stamps on each element). Elements without a tag — legacy saved states — land\n * on the first artboard, which also keeps single-artboard output byte-identical\n * to before. The tag itself is client-side metadata and is stripped from the\n * wire elements, so the per-placement `{schemaVersion, artboards: [...]}` shape\n * the server consumes is unchanged.\n *\n * For a multi-placement product, slice the result per placement:\n *\n * ```ts\n * const wire = serializeStateForServer(state);\n * for (const ab of wire.artboards) {\n * session.renderState(ab.name, { schemaVersion: wire.schemaVersion, artboards: [ab] });\n * }\n * ```\n *\n * @param state - The state from SnowconeCanvas onChange callback\n * @returns ServerRenderRequest ready to send via sendCanvasState()\n */\nexport function serializeStateForServer(state: {\n elements?: any[];\n artboards?: Array<{\n name?: string;\n width?: number;\n height?: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n clipShape?: any;\n distressTexture?: any;\n imageMask?: any;\n /** Pacing role (multi-page templates, ADR-0086) — client/template\n * metadata, stripped from the wire like the element `artboard` tag. */\n role?: string;\n }>;\n activeArtboard?: string;\n}): ServerRenderRequest {\n const srcArtboards = state.artboards?.length\n ? state.artboards\n : [{ name: 'Front', width: 800, height: 800 }];\n\n // Group elements by their artboard tag (artboard NAME, stamped by\n // SnowconeCanvas.onChange). Untagged or unknown-tagged elements fall back to\n // the first artboard — today's behavior for legacy flat states.\n const elementsByArtboard = srcArtboards.map<any[]>(() => []);\n for (const el of state.elements || []) {\n const tag = el?.artboard;\n const idx = typeof tag === 'string'\n ? srcArtboards.findIndex((ab) => (ab.name || 'Front') === tag)\n : -1;\n elementsByArtboard[idx >= 0 ? idx : 0].push(serializeElement(el));\n }\n\n // Wire artboards are built field-by-field: template metadata on the source\n // artboards (the `role` pacing tag, ADR-0086) never reaches the server —\n // same rule as the element `artboard` membership tag below.\n return {\n schemaVersion: CANVAS_STATE_SCHEMA_VERSION,\n artboards: srcArtboards.map((srcArtboard, i) => ({\n id: `artboard-${i + 1}`,\n name: srcArtboard.name || 'Front',\n x: 0,\n y: 0,\n width: q(srcArtboard.width || 800) as number,\n height: q(srcArtboard.height || 800) as number,\n backgroundColor: srcArtboard.backgroundColor || 'transparent',\n // Pass the artboard's flag through — the server worker only paints the\n // background when this is true. Hardcoding `false` here silently dropped\n // explicitly-set artboard backgrounds from every realtime mockup while\n // the editor kept showing them (WYSIWYG drift).\n exportBackground: srcArtboard.exportBackground ?? false,\n elements: elementsByArtboard[i],\n distressTexture: srcArtboard.distressTexture,\n imageMask: srcArtboard.imageMask,\n })),\n };\n}\n\n/**\n * Serialize a single element from toJSON() format to export worker format.\n */\nfunction serializeElement(el: any): any {\n const type = el.type || el.transformType;\n\n if (type === 'image') {\n return serializeImageElement(el);\n }\n\n // Text, shape, group, path elements: map transformType → type, keep everything else\n // Text, shape, group, path elements: pass through as-is with type mapped.\n // Builtin mask URLs (builtin-mask:*) are kept as-is — the server worker\n // generates them procedurally via generateMaskBitmap().\n // Geometry is quantized (see `q`) so idle render-loop float drift doesn't\n // produce a new wire state every frame.\n // The `artboard` membership tag is CLIENT-side metadata (it already decided\n // which wire artboard this element belongs to) — strip it so the server\n // payload shape stays unchanged.\n const { artboard: _artboard, ...rest } = el;\n return quantizeGeometry({\n ...rest,\n type,\n });\n}\n\n/**\n * Serialize an image element, flattening transformData to top-level fields\n * as expected by SerializedImageElement and renderImageElement.\n */\nfunction serializeImageElement(el: any): any {\n const td = el.transformData || {};\n const hasMasks = el.masks && el.masks.length > 0;\n\n // Visible dimensions depend on crop and masks\n const fullWidth = td.width || 0;\n const fullHeight = td.height || 0;\n const visibleWidth = hasMasks ? fullWidth : fullWidth * (td.cropWidth ?? 1);\n const visibleHeight = hasMasks ? fullHeight : fullHeight * (td.cropHeight ?? 1);\n\n const serialized: any = {\n id: el.id,\n type: 'image' as const,\n x: el.x || 0,\n y: el.y || 0,\n width: visibleWidth,\n height: visibleHeight,\n rotation: el.rotation || 0,\n imageUrl: el.imageUrl,\n imageAspectRatio: el.imageAspectRatio,\n };\n\n // Crop (only if actually cropped and no masks)\n const isCropped = !hasMasks && (td.cropX !== 0 || td.cropY !== 0 || td.cropWidth !== 1 || td.cropHeight !== 1);\n if (isCropped) {\n // Note: crop values stay as fractions here — the server's Chrome tab\n // will convert to pixels using the actual bitmap dimensions after fetch.\n // The client doesn't have the bitmap dimensions available in onChange state.\n serialized.cropX = td.cropX;\n serialized.cropY = td.cropY;\n serialized.cropWidth = td.cropWidth;\n serialized.cropHeight = td.cropHeight;\n serialized.needsCropPixelConversion = true; // Flag for server to convert\n }\n\n // Flip and border radius\n if (td.flipHorizontal) serialized.flipHorizontal = td.flipHorizontal;\n if (td.flipVertical) serialized.flipVertical = td.flipVertical;\n if (td.borderRadius) serialized.borderRadius = td.borderRadius;\n if (el.opacity !== undefined) serialized.opacity = el.opacity;\n\n // Effects\n if (el.distressEffect) serialized.distressEffect = el.distressEffect;\n if (el.masks?.length > 0) serialized.masks = el.masks;\n if (el.blendMode) serialized.blendMode = el.blendMode;\n if (el.knockoutParts) serialized.knockoutParts = el.knockoutParts;\n if (el.stroke) serialized.stroke = el.stroke;\n\n // Quantize geometry so idle render-loop float drift doesn't churn the wire state.\n return quantizeGeometry(serialized);\n}\n","/**\n * Declarative design builder — compose a multi-element design by *intent*\n * (full-bleed background, anchored text, anchored shapes) and never by raw\n * coordinates.\n *\n * WHY THIS EXISTS\n * ---------------\n * The canvas/render contract positions every element by its CENTER point\n * (`x, y` is the centre, not the top-left — see CLAUDE.md). That convention is\n * correct for the interactive editor (rotation/scale pivot around centre) but is\n * a foot-gun for anyone hand-constructing a `canvas_state`: the natural\n * \"fill the placement\" attempt (`x:0, y:0, width:full`) centres the element on\n * the artboard's top-left CORNER, so it renders into the top-left quadrant. An\n * expert integrator hit exactly this and mis-diagnosed it as a server bug.\n *\n * This builder makes that class of mistake impossible: you describe *what* you\n * want (a background that fills, a title anchored top-centre, a star bottom-left)\n * and it emits a correct editor-shape canvas state. The centre-coordinate maths\n * lives here, once, instead of in every integrator's head.\n *\n * THREE RENDER TARGETS, ONE DESIGN\n * --------------------------------\n * The builder emits the EDITOR shape (`transformType` + nested `transformData`,\n * centre coords), which is exactly what both consumers already accept:\n * - `design.toJSON()` → `<SnowconeCanvas initialElements=… />` (interactive)\n * - `design.toServerRequest()` → `session.renderState(placement, …)` (realtime)\n * - (single asset on a placement → use `buildMockupUrl` in the SDK instead)\n *\n * So a developer can author a design headlessly, render it live, AND hand the\n * same design to the editor for the end user to drag/scale/rotate from there.\n */\n\nimport type {\n AnyElementConfig,\n ArtboardRole,\n ImageElementConfig,\n CustomElementConfig,\n ShapeElementConfig,\n ShapeTransformData,\n} from '../types/index.js';\nimport { serializeStateForServer, type ServerRenderRequest } from '../rendering/serialize-for-server.js';\nimport { EditorError } from '../errors/editorError.js';\n\n/** A nine-point anchor: where the element sits relative to the artboard. */\nexport type Anchor =\n | 'top-left' | 'top' | 'top-right'\n | 'left' | 'center' | 'right'\n | 'bottom-left' | 'bottom' | 'bottom-right';\n\nexport type ShapeKind =\n | 'rectangle' | 'circle' | 'ellipse' | 'triangle' | 'polygon' | 'star' | 'line';\n\n/** The artboard a design is composed on — one print placement. `width`/`height`\n * are the placement's pixel dimensions (e.g. 1480×2328 for a phone-case Front).\n * `name` MUST match the placement label you render (case-sensitive). */\nexport interface DesignArtboard {\n /** Placement label (case-sensitive — e.g. 'Front'). Defaults to 'Front'. */\n name?: string;\n width: number;\n height: number;\n /** Optional solid background fill behind all elements. Rendered everywhere\n * the design is — editor, exports, and server renders (realtime mockups). */\n backgroundColor?: string;\n /** Pacing role in a multi-page template (ADR-0086 albums). Template\n * metadata only — never sent on the server render wire. */\n role?: ArtboardRole;\n}\n\n/** One artboard of a saved multi-artboard state, as {@link DesignBuilder.fromState}\n * accepts and `toJSON()` emits. */\nexport interface StateArtboard {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n /** Pacing role in a multi-page template (ADR-0086 albums). */\n role?: ArtboardRole;\n}\n\nexport interface FromStateOptions {\n /**\n * Which artboards to load:\n * - `'active'` (default) — today's behavior: the active artboard becomes the\n * builder's artboard; elements tagged for other artboards are skipped.\n * - `'all'` — multi-page templates (ADR-0086): every artboard and its\n * elements are kept, with `artboard` membership tags preserved, so\n * `toJSON()` / `toServerRequest()` stay multi-artboard. Template fields\n * scope per page via `TemplateField.artboard`.\n */\n artboards?: 'active' | 'all';\n}\n\n/** A stable layer name. Set it so you can bind the layer in an interactive\n * `<SnowconeCanvas/>` editor — `useImageBinding(name)` / `useTextBinding(name)`\n * find the element by `name`. Keep names unique per design. */\ntype NamedOption = { name?: string };\n\nexport interface BackgroundOptions extends NamedOption {\n /** Source image aspect ratio (w/h). When given, the image is COVER-fit (filled\n * + centre-cropped) with no distortion. When omitted, the image fills the\n * artboard box directly (correct when it's already the placement's aspect,\n * e.g. an AI image generated at the placement size). */\n aspectRatio?: number;\n opacity?: number;\n}\n\nexport interface ImageOptions extends NamedOption {\n anchor?: Anchor;\n /** Display width in artboard px. Defaults to a third of the artboard width. */\n width?: number;\n /** Display height in px. Defaults to `width / aspectRatio` (or square). */\n height?: number;\n aspectRatio?: number;\n opacity?: number;\n rotation?: number;\n /** Inset from the artboard edge for non-centre anchors, in px. Default 0. */\n padding?: number;\n}\n\nexport interface TextOptions extends NamedOption {\n anchor?: Anchor;\n fontSize?: number;\n fontFamily?: string;\n color?: string;\n /** Horizontal text alignment. Named `textAlign` to match the canvas editor's\n * element field (not `align`), so the builder and the editor read the same. */\n textAlign?: 'left' | 'center' | 'right';\n bold?: boolean;\n italic?: boolean;\n rotation?: number;\n padding?: number;\n}\n\nexport interface ShapeOptions extends NamedOption {\n anchor?: Anchor;\n /** Square size, or explicit box. Default 200×200. */\n size?: number | { width: number; height: number };\n fill?: string;\n rotation?: number;\n opacity?: number;\n /** star points / polygon sides. */\n points?: number;\n sides?: number;\n padding?: number;\n}\n\nconst DEFAULT_ANCHOR: Anchor = 'center';\n\n// ── Typed layer tokens (ADR-0085 §1 / T1) ────────────────────────────────────\n\n/** The bindable layer kinds. Shapes are not bindable in v1. */\nexport type LayerKind = 'text' | 'image';\n\n/**\n * Any design builder, regardless of its compile-time layer-name unions.\n * CONSUMERS of a builder (props, context) must use this — `DesignBuilder`\n * (= `DesignBuilder<string, string>`) is not a supertype of a statically\n * typed builder because `layer()`'s name constraint narrows with the design.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance escape: `any` here means \"accept every name union\", checked at mint/mount instead\nexport type AnyDesign = DesignBuilder<any, any>;\n\ndeclare const LAYER_TOKEN_BRAND: unique symbol;\n/** Runtime marker carried by every minted token (survives serialization\n * boundaries within one realm; `Symbol.for` so duplicated module instances\n * still agree). */\nconst LAYER_TOKEN_MARKER = Symbol.for('snowcone.canvas.layerToken');\n\n/**\n * A validated claim that layer `N` of kind `K` exists in the design that\n * minted it. The ONLY currency `Editor.TextField` (and future `ImageField`)\n * accepts — bare strings are rejected (`E_LAYER_TOKEN_REQUIRED`). Mint with\n * {@link DesignBuilder.layer}; not constructible by hand (branded).\n *\n * Stable since ADR-0085 P4 (the default editor is rebuilt from this public\n * surface, CI-gated); additive-only from here.\n */\nexport interface LayerToken<K extends LayerKind = LayerKind, N extends string = string> {\n readonly kind: K;\n readonly name: N;\n /** @internal */\n readonly [LAYER_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by parts before trusting a `layer` prop. */\nexport function isLayerToken(value: unknown): value is LayerToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[LAYER_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n ((value as { kind?: unknown }).kind === 'text' || (value as { kind?: unknown }).kind === 'image')\n );\n}\n\nfunction mintLayerToken(kind: LayerKind, name: string): LayerToken {\n return Object.freeze({ kind, name, [LAYER_TOKEN_MARKER]: true }) as unknown as LayerToken;\n}\n\n/** The token kind inferred for name `N` against the builder's accumulated\n * per-kind name unions. A dynamically-loaded design (both unions degraded to\n * `string`) yields the full `'text' | 'image'` union — typing degrades, the\n * runtime validation in `layer()` is the net that still fires (0G#2). */\ntype InferKind<N extends string, TText extends string, TImage extends string> =\n [N] extends [TText] ? ([N] extends [TImage] ? LayerKind : 'text') : 'image';\n\n/** Kind of an element config, for token validation. `null` = not bindable. */\nfunction elementLayerKind(el: AnyElementConfig): LayerKind | null {\n const t = (el as { transformType?: string }).transformType;\n if (t === 'image') return 'image';\n if (t === 'shape' || t === 'group' || t === 'path') return null;\n // Every text transform ('custom', 'circle', 'arch', …) is a text layer.\n return 'text';\n}\n\nfunction hPart(a: Anchor): 'left' | 'center' | 'right' {\n if (a === 'top-left' || a === 'left' || a === 'bottom-left') return 'left';\n if (a === 'top-right' || a === 'right' || a === 'bottom-right') return 'right';\n return 'center';\n}\n\nfunction vPart(a: Anchor): 'top' | 'middle' | 'bottom' {\n if (a === 'top-left' || a === 'top' || a === 'top-right') return 'top';\n if (a === 'bottom-left' || a === 'bottom' || a === 'bottom-right') return 'bottom';\n return 'middle';\n}\n\n/**\n * Resolve an element's CENTRE (x, y) so its bounding box sits at `anchor` on the\n * artboard, inset by `padding`. This is the whole point of the builder: a\n * `center` anchor on a full-bleed element returns the artboard centre, never\n * (0, 0).\n */\nfunction anchorToCenter(\n anchor: Anchor,\n elW: number,\n elH: number,\n artW: number,\n artH: number,\n padding: number,\n): { x: number; y: number } {\n const h = hPart(anchor);\n const v = vPart(anchor);\n const x = h === 'left' ? elW / 2 + padding\n : h === 'right' ? artW - elW / 2 - padding\n : artW / 2;\n const y = v === 'top' ? elH / 2 + padding\n : v === 'bottom' ? artH - elH / 2 - padding\n : artH / 2;\n return { x, y };\n}\n\n/** Compute the centred cover-crop (fractions, 0–1) for a source of aspect `ar`\n * filling an `artW×artH` box, plus the full (uncropped) display size the editor\n * stores in `transformData`. Mirrors the editor's cover behaviour but needs only\n * the aspect ratio, not the pixel dimensions. */\nfunction coverCrop(ar: number, artW: number, artH: number) {\n const artAR = artW / artH;\n if (ar >= artAR) {\n // Source is wider than the box → crop the sides.\n const fullW = artH * ar;\n const cropWidth = artW / fullW; // = artAR / ar\n return {\n width: fullW, height: artH,\n cropX: (1 - cropWidth) / 2, cropY: 0, cropWidth, cropHeight: 1,\n };\n }\n // Source is taller than the box → crop top/bottom.\n const fullH = artW / ar;\n const cropHeight = artH / fullH; // = ar / artAR\n return {\n width: artW, height: fullH,\n cropX: 0, cropY: (1 - cropHeight) / 2, cropWidth: 1, cropHeight,\n };\n}\n\n/**\n * A composed design. Add elements by intent, then emit it for the editor or the\n * render path. Methods are chainable.\n *\n * The two type parameters accumulate the design's NAMED layer literals per\n * kind (ADR-0085 §1): naming a layer in `.text()` / `.image()` /\n * `.background()` widens the corresponding union, so `design.layer(\"naem\")`\n * is a compile error listing the valid names. Designs loaded from JSON\n * ({@link DesignBuilder.fromState}) degrade both unions to `string` — same\n * API, runtime-validated instead (the degradation is deliberate and LOUD in\n * docs; never block the dynamic case).\n */\nexport class DesignBuilder<out TText extends string = string, out TImage extends string = string> {\n private readonly artboard: Required<Pick<DesignArtboard, 'name' | 'width' | 'height'>> & {\n backgroundColor?: string;\n role?: ArtboardRole;\n };\n private readonly els: AnyElementConfig[] = [];\n private idCounter = 0;\n /** Every artboard of a multi-page state (fromState `{ artboards: 'all' }`);\n * unset for single-artboard builders. */\n private allArtboards?: StateArtboard[];\n\n constructor(artboard: DesignArtboard) {\n if (!(artboard.width > 0) || !(artboard.height > 0)) {\n throw new Error('design(): artboard width and height must be positive numbers');\n }\n this.artboard = {\n name: artboard.name || 'Front',\n width: artboard.width,\n height: artboard.height,\n backgroundColor: artboard.backgroundColor,\n role: artboard.role,\n };\n }\n\n /**\n * Seed a builder from a previously saved canvas state — the editor shape\n * `<SnowconeCanvas onChange>` emits (and `toJSON()` produces).\n *\n * This is how a saved editable template re-enters the turnkey embed path:\n * load the template's state JSON, lift it back into a `DesignBuilder`, and\n * hand it to `<RealtimeEmbed design={…}>`. By default the active artboard\n * becomes the builder's artboard; elements tagged for other artboards are\n * skipped (untagged elements are assumed to belong to the active one).\n *\n * Pass `{ artboards: 'all' }` to load a multi-page template (ADR-0086)\n * whole: every artboard (with its pacing `role`) and every element are\n * kept, membership tags preserved, so `toJSON()` / `toServerRequest()`\n * emit all pages and `TemplateField.artboard` can scope fields per page.\n */\n static fromState(\n state: {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n },\n options: FromStateOptions = {},\n ): DesignBuilder {\n const artboard =\n state.artboards.find((a) => a.name === state.activeArtboard) ?? state.artboards[0];\n if (!artboard) {\n throw new Error('DesignBuilder.fromState(): state has no artboards');\n }\n const builder = new DesignBuilder({\n name: artboard.name,\n width: artboard.width,\n height: artboard.height,\n backgroundColor: artboard.backgroundColor,\n role: artboard.role,\n });\n if (options.artboards === 'all') {\n builder.allArtboards = state.artboards.map((a) => ({ ...a }));\n for (const el of state.elements) {\n // Untagged elements belong to the active artboard (same assumption as\n // the active-only mode) — stamp them so the multi-artboard state keeps\n // them there instead of collapsing onto the first artboard.\n builder.els.push(el.artboard ? { ...el } : { ...el, artboard: artboard.name });\n }\n return builder;\n }\n for (const el of state.elements) {\n if (el.artboard && el.artboard !== artboard.name) continue;\n builder.els.push({ ...el });\n }\n return builder;\n }\n\n private nextId(prefix: string): string {\n return `${prefix}-${++this.idCounter}`;\n }\n\n /** Full-bleed background image (fills the whole artboard). Cover-fit (no\n * distortion) when `aspectRatio` is supplied. Inserted at the bottom of the\n * stack regardless of call order. The default layer name `'Background'`\n * joins the IMAGE-layer token union (auto-injected names are deliberately\n * not one union with text — ADR-0085 §1). */\n background<const N extends string>(\n imageUrl: string,\n opts: BackgroundOptions & { name: N },\n ): DesignBuilder<TText, TImage | N>;\n background(imageUrl: string, opts?: BackgroundOptions): DesignBuilder<TText, TImage | 'Background'>;\n background(imageUrl: string, opts: BackgroundOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.artboard;\n let td: ImageElementConfig['transformData'];\n if (opts.aspectRatio && opts.aspectRatio > 0) {\n const c = coverCrop(opts.aspectRatio, artW, artH);\n td = {\n type: 'image', width: c.width, height: c.height,\n cropX: c.cropX, cropY: c.cropY, cropWidth: c.cropWidth, cropHeight: c.cropHeight,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n } else {\n td = {\n type: 'image', width: artW, height: artH,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n }\n const el: ImageElementConfig = {\n id: this.nextId('bg'),\n transformType: 'image',\n x: artW / 2,\n y: artH / 2,\n rotation: 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: td,\n name: opts.name ?? 'Background',\n };\n // Background goes underneath everything else.\n this.els.unshift(el);\n return this;\n }\n\n /** An anchored image element (not full-bleed). */\n image<const N extends string>(\n imageUrl: string,\n opts: ImageOptions & { name: N },\n ): DesignBuilder<TText, TImage | N>;\n image(imageUrl: string, opts?: ImageOptions): DesignBuilder<TText, TImage>;\n image(imageUrl: string, opts: ImageOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.artboard;\n const w = opts.width ?? Math.round(artW / 3);\n const h = opts.height ?? (opts.aspectRatio ? Math.round(w / opts.aspectRatio) : w);\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const el: ImageElementConfig = {\n id: this.nextId('img'),\n transformType: 'image',\n x, y,\n rotation: opts.rotation ?? 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: {\n type: 'image', width: w, height: h,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** An anchored text layer. Horizontal centring is exact (textAlign); vertical\n * anchoring uses the font size as the line-height estimate. */\n text<const N extends string>(\n text: string,\n opts: TextOptions & { name: N },\n ): DesignBuilder<TText | N, TImage>;\n text(text: string, opts?: TextOptions): DesignBuilder<TText, TImage>;\n text(text: string, opts: TextOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.artboard;\n const fontSize = opts.fontSize ?? Math.round(this.artboard.height / 12);\n // Text width is metric-dependent (unknown headless); horizontal placement is\n // handled by textAlign, so anchor only drives the vertical centre + the\n // horizontal *reference* point. Use a zero-width box horizontally so the\n // centre sits exactly on the anchor's x line.\n const { x, y } = anchorToCenter(opts.anchor ?? 'top', 0, fontSize, artW, artH, opts.padding ?? Math.round(artH / 20));\n const el: CustomElementConfig = {\n id: this.nextId('text'),\n transformType: 'custom',\n x, y,\n rotation: opts.rotation ?? 0,\n text,\n fontSize,\n fontFamily: opts.fontFamily ?? 'Anton',\n color: opts.color ?? '#ffffff',\n textAlign: opts.textAlign ?? 'center',\n bold: opts.bold ?? false,\n italic: opts.italic ?? false,\n transformData: { type: 'custom' },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /**\n * Mint a {@link LayerToken} for a named layer — the only currency\n * `Editor.TextField` accepts (ADR-0085 §1). Compile time: `name` is\n * constrained to this design's declared layer names, kind inferred from\n * which per-kind union it falls in. Runtime: validated EAGERLY against the\n * built elements (nearest the mistake), so CMS/JSON-loaded designs get the\n * same protection with `string` typing.\n *\n * @throws EditorError `E_LAYER_NOT_FOUND` — no layer with this name; lists\n * the valid names per kind.\n * @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that\n * is not bindable (shape/group/path).\n */\n layer<N extends TText | TImage>(name: N): LayerToken<InferKind<N, TText, TImage>, N> {\n const el = this.els.find((e) => (e as { name?: string }).name === name);\n if (!el) {\n const textNames = this.layerNamesOfKind('text');\n const imageNames = this.layerNamesOfKind('image');\n throw new EditorError(\n 'E_LAYER_NOT_FOUND',\n `no layer \"${name}\" in this design. Text layers: ${JSON.stringify(textNames)}; ` +\n `image layers: ${JSON.stringify(imageNames)}. Fix the name, or add the layer ` +\n `first — e.g. design(…).text('…', { name: '${name}' }).`,\n { layerName: name, validNames: [...textNames, ...imageNames], textNames, imageNames, docSlug: 'embed-mode' },\n );\n }\n const kind = elementLayerKind(el);\n if (!kind) {\n throw new EditorError(\n 'E_LAYER_WRONG_KIND',\n `layer \"${name}\" is a ${(el as { transformType?: string }).transformType} element — ` +\n `only text and image layers are bindable. Bindable layers: ` +\n `${JSON.stringify([...this.layerNamesOfKind('text'), ...this.layerNamesOfKind('image')])}.`,\n { layerName: name, actualType: (el as { transformType?: string }).transformType, docSlug: 'embed-mode' },\n );\n }\n return mintLayerToken(kind, name) as LayerToken<InferKind<N, TText, TImage>, N>;\n }\n\n /** Names of bindable layers of one kind, in stack order. @internal */\n layerNamesOfKind(kind: LayerKind): string[] {\n return this.els\n .filter((e) => elementLayerKind(e) === kind)\n .map((e) => (e as { name?: string }).name)\n .filter((n): n is string => Boolean(n));\n }\n\n /** An anchored vector shape. */\n shape(kind: ShapeKind, opts: ShapeOptions = {}): this {\n const { width: artW, height: artH } = this.artboard;\n const size = opts.size ?? Math.round(artW / 6);\n const w = typeof size === 'number' ? size : size.width;\n const h = typeof size === 'number' ? size : size.height;\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const transformData: ShapeTransformData = {\n type: 'shape',\n shapeType: kind,\n width: w,\n height: h,\n fillColor: opts.fill ?? '#000000',\n fillOpacity: opts.opacity ?? 1,\n ...(opts.points !== undefined ? { points: opts.points } : {}),\n ...(opts.sides !== undefined ? { sides: opts.sides } : {}),\n };\n const el: ShapeElementConfig = {\n id: this.nextId('shape'),\n transformType: 'shape',\n x, y,\n rotation: opts.rotation ?? 0,\n transformData,\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** The composed elements, in render order (background first). Editor shape. */\n get elements(): AnyElementConfig[] {\n return this.els.map((e) => ({ ...e }));\n }\n\n /** Editor-shape canvas state for `<SnowconeCanvas initialElements=… />` and\n * `serializeStateForServer`. `activeArtboard` is the artboard name. A\n * builder loaded with `fromState(…, { artboards: 'all' })` emits every\n * page (artboard `role`s and element membership tags included). */\n toJSON(): {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n } {\n if (this.allArtboards) {\n return {\n // Elements added via builder methods after fromState are untagged —\n // they belong to the builder's (active) artboard; stamp them so the\n // multi-artboard state groups them correctly.\n elements: this.els.map((e) =>\n e.artboard ? { ...e } : { ...e, artboard: this.artboard.name },\n ),\n artboards: this.allArtboards.map((a) => ({ ...a })),\n activeArtboard: this.artboard.name,\n };\n }\n return {\n elements: this.elements,\n artboards: [{\n name: this.artboard.name,\n width: this.artboard.width,\n height: this.artboard.height,\n // A background set on a design is part of the design: flag it\n // renderable so exports and server renders paint it, matching the\n // editor (the export worker skips it without `exportBackground`).\n ...(this.artboard.backgroundColor\n ? {\n backgroundColor: this.artboard.backgroundColor,\n exportBackground: this.artboard.backgroundColor !== 'transparent',\n }\n : {}),\n ...(this.artboard.role ? { role: this.artboard.role } : {}),\n }],\n activeArtboard: this.artboard.name,\n };\n }\n\n /** Server-render payload for `RenderSession.renderState(placement, …)`.\n * Equivalent to `serializeStateForServer(design.toJSON())`. */\n toServerRequest(): ServerRenderRequest {\n return serializeStateForServer(this.toJSON());\n }\n}\n\n/**\n * Start a design on an artboard (one print placement).\n *\n * @example\n * const d = design({ name: 'Front', width: 1480, height: 2328 })\n * .background(aiImageUrl, { aspectRatio: 1480 / 2328, name: 'art' })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .shape('star', { anchor: 'bottom-left', fill: '#000' });\n *\n * session.renderState('Front', d.toServerRequest()); // live\n * // …or hand d.toJSON() to <SnowconeCanvas/> for interactive editing — the\n * // `name`s let you bind layers with useImageBinding('art') / useTextBinding('headline').\n */\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never> {\n return new DesignBuilder<never, never>(artboard);\n}\n\n/**\n * Resolve a layer's kind against a live element/config list — the mount-time\n * revalidation `Editor.TextField` runs (the token is a validated claim; the\n * mounted Root's design state is the authority). @internal\n */\nexport function layerKindOfConfig(el: AnyElementConfig): LayerKind | null {\n return elementLayerKind(el);\n}\n"],"names":["CANVAS_STATE_SCHEMA_VERSION","q","n","quantizeGeometry","el","serializeStateForServer","state","srcArtboards","_a","elementsByArtboard","tag","idx","ab","serializeElement","srcArtboard","type","serializeImageElement","_artboard","rest","td","hasMasks","fullWidth","fullHeight","visibleWidth","visibleHeight","serialized","DEFAULT_ANCHOR","LAYER_TOKEN_MARKER","isLayerToken","value","mintLayerToken","kind","name","elementLayerKind","t","hPart","a","vPart","anchorToCenter","anchor","elW","elH","artW","artH","padding","h","v","x","y","coverCrop","ar","artAR","fullW","cropWidth","fullH","cropHeight","DesignBuilder","artboard","options","builder","prefix","imageUrl","opts","c","w","text","fontSize","e","textNames","imageNames","EditorError","size","transformData","design","layerKindOfConfig"],"mappings":"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"}
|