@snowcone-app/canvas 0.23.0 → 0.27.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 (73) hide show
  1. package/ERROR_CONTRACT.md +638 -0
  2. package/README.md +2 -1
  3. package/STYLING_CONTRACT.md +59 -8
  4. package/dist/{CanvasRenderer-6_HhR4-q.js → CanvasRenderer-yszzrDfo.js} +4 -4
  5. package/dist/{CanvasRenderer-6_HhR4-q.js.map → CanvasRenderer-yszzrDfo.js.map} +1 -1
  6. package/dist/{CanvasStateV1-DmZ1Qvvn.js → CanvasStateV1-BTbCD_xC.js} +3 -3
  7. package/dist/{CanvasStateV1-DmZ1Qvvn.js.map → CanvasStateV1-BTbCD_xC.js.map} +1 -1
  8. package/dist/{ContextualToolbars-BDEt9ND0.js → ContextualToolbars-BHjYsril.js} +6 -6
  9. package/dist/{ContextualToolbars-BDEt9ND0.js.map → ContextualToolbars-BHjYsril.js.map} +1 -1
  10. package/dist/{EditorCanvasImpl-Ckg7WO2j.js → EditorCanvasImpl-Ui8Ceb-h.js} +8 -8
  11. package/dist/{EditorCanvasImpl-Ckg7WO2j.js.map → EditorCanvasImpl-Ui8Ceb-h.js.map} +1 -1
  12. package/dist/{ExportManager-DoU4VK6h.js → ExportManager-BTcyj4cf.js} +2 -2
  13. package/dist/{ExportManager-DoU4VK6h.js.map → ExportManager-BTcyj4cf.js.map} +1 -1
  14. package/dist/{FontAnalyzer-CwIpgTGd.js → FontAnalyzer-EF3gLPr8.js} +30 -20
  15. package/dist/{FontAnalyzer-CwIpgTGd.js.map → FontAnalyzer-EF3gLPr8.js.map} +1 -1
  16. package/dist/{ImagePanel-BwqFDKvW.js → ImagePanel-CIk6z41z.js} +6 -6
  17. package/dist/{ImagePanel-BwqFDKvW.js.map → ImagePanel-CIk6z41z.js.map} +1 -1
  18. package/dist/{SnowconeCanvas-D1sLcIja.js → SnowconeCanvas-DuWALOAt.js} +10 -10
  19. package/dist/{SnowconeCanvas-D1sLcIja.js.map → SnowconeCanvas-DuWALOAt.js.map} +1 -1
  20. package/dist/{UnifiedFontService-CC3CSmH5.js → UnifiedFontService-CHVYDBDW.js} +869 -836
  21. package/dist/{UnifiedFontService-CC3CSmH5.js.map → UnifiedFontService-CHVYDBDW.js.map} +1 -1
  22. package/dist/advanced.mjs +14 -14
  23. package/dist/api/embed.d.ts +6 -2
  24. package/dist/components/editor/EditorLoadingOverlay.d.ts +12 -0
  25. package/dist/components/editor/EditorPlacements.d.ts +14 -0
  26. package/dist/components/editor/EditorPreview.d.ts +32 -8
  27. package/dist/components/editor/EditorRoot.d.ts +12 -0
  28. package/dist/components/editor/PreviewPrismOverlay.d.ts +23 -0
  29. package/dist/components/editor/RootInternals.d.ts +31 -0
  30. package/dist/components/editor/editorAudit.d.ts +66 -0
  31. package/dist/components/editor/index.d.ts +14 -0
  32. package/dist/components/editor/usePlacements.d.ts +14 -0
  33. package/dist/components/embed-realtime/context.d.ts +11 -2
  34. package/dist/components/embed-realtime/validateEmbed.d.ts +20 -1
  35. package/dist/contexts/EditorContext.d.ts +34 -0
  36. package/dist/core/HybridHistoryManager.d.ts +7 -0
  37. package/dist/core/elementGuards.d.ts +19 -0
  38. package/dist/{custom-icons-BW6hqmkL.js → custom-icons-CcLMyybv.js} +7 -7
  39. package/dist/{custom-icons-BW6hqmkL.js.map → custom-icons-CcLMyybv.js.map} +1 -1
  40. package/dist/design/design-builder.d.ts +41 -3
  41. package/dist/design/product-config.d.ts +10 -0
  42. package/dist/{design-builder-2VUjfmdt.js → design-builder-cZY958Rw.js} +109 -61
  43. package/dist/design-builder-cZY958Rw.js.map +1 -0
  44. package/dist/editorErrors-BhrZ_nwj.js +1219 -0
  45. package/dist/editorErrors-BhrZ_nwj.js.map +1 -0
  46. package/dist/embed-CJswRZte.js +1586 -0
  47. package/dist/embed-CJswRZte.js.map +1 -0
  48. package/dist/embed.mjs +42 -34
  49. package/dist/errors/editorError.d.ts +29 -1
  50. package/dist/{guards-8rkFAMBs.js → guards-DIX8aXWM.js} +2 -2
  51. package/dist/{guards-8rkFAMBs.js.map → guards-DIX8aXWM.js.map} +1 -1
  52. package/dist/hooks/elementPatch.d.ts +96 -0
  53. package/dist/hooks/useCommands.d.ts +20 -0
  54. package/dist/index.mjs +8 -8
  55. package/dist/internals.mjs +9 -9
  56. package/dist/style.css +1 -1
  57. package/dist/template-fields-BXl29sgh.js +716 -0
  58. package/dist/template-fields-BXl29sgh.js.map +1 -0
  59. package/dist/testing.mjs +4 -4
  60. package/dist/{usePieceGuideBundle-CbJJg66S.js → usePieceGuideBundle-DE21DQGZ.js} +2 -2
  61. package/dist/{usePieceGuideBundle-CbJJg66S.js.map → usePieceGuideBundle-DE21DQGZ.js.map} +1 -1
  62. package/dist/{validation-C9Fe9ahK.js → validation-DGXAQmUC.js} +11 -11
  63. package/dist/{validation-C9Fe9ahK.js.map → validation-DGXAQmUC.js.map} +1 -1
  64. package/example/package.json +1 -1
  65. package/llms.txt +3 -0
  66. package/package.json +7 -4
  67. package/dist/design-builder-2VUjfmdt.js.map +0 -1
  68. package/dist/editorErrors-B2mb1sVN.js +0 -1208
  69. package/dist/editorErrors-B2mb1sVN.js.map +0 -1
  70. package/dist/embed-DxsaqZJF.js +0 -1226
  71. package/dist/embed-DxsaqZJF.js.map +0 -1
  72. package/dist/template-fields-CQ6n2P4V.js +0 -548
  73. package/dist/template-fields-CQ6n2P4V.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-builder-cZY958Rw.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 * NOTE: the background is not an element, so it is NOT repaintable by\n * `useColorBinding` — designs that want a rebindable backdrop paint a\n * full-bleed `shape('rectangle', { fill: … })` instead. */\n backgroundColor?: string | ColorToken;\n /** Named color ROLES for value-matched rebinding (`useColorBinding`).\n * Mint tokens with `design.color('role')`; use them as `fill`/`color`\n * values so role and element can never drift. Validated at mint:\n * duplicate values across roles throw `E_COLOR_ROLE_COLLISION` (a\n * value-matched binding can't tell colliding roles apart), non-hex\n * values throw `E_COLOR_ROLE_INVALID`. */\n colors?: Record<string, 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 /** Hex string, or a `design.color('role')` token (resolved at build). */\n color?: string | ColorToken;\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 /** Hex string, or a `design.color('role')` token (resolved at build). */\n fill?: string | ColorToken;\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// ── Color-role tokens ────────────────────────────────────────────────────────\n\ndeclare const COLOR_TOKEN_BRAND: unique symbol;\nconst COLOR_TOKEN_MARKER = Symbol.for('snowcone.canvas.colorToken');\n\n/**\n * A validated claim that color role `N` exists in the design that minted it,\n * carrying the role's (normalized) hex value. `useColorBinding` accepts it in\n * place of a bare hex, so the chip and the painted elements can never drift —\n * and `design({ colors })` rejects role-value collisions at mint time, which a\n * value-matched binding cannot survive (one role's repaint would capture the\n * other role's elements). Mint with {@link DesignBuilder.color}; not\n * constructible by hand (branded).\n */\nexport interface ColorToken<N extends string = string> {\n readonly name: N;\n /** Normalized uppercase `#RRGGBB`. */\n readonly value: string;\n /** @internal */\n readonly [COLOR_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by `useColorBinding` (and the builder's value slots)\n * before trusting a color value. */\nexport function isColorToken(value: unknown): value is ColorToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[COLOR_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { value?: unknown }).value === 'string'\n );\n}\n\nfunction mintColorToken(name: string, value: string): ColorToken {\n return Object.freeze({ name, value, [COLOR_TOKEN_MARKER]: true }) as unknown as ColorToken;\n}\n\n/** Normalize `#RGB`/`#RRGGBB` (any case) to uppercase `#RRGGBB` — the same\n * normalization `useColorBinding` matches with. Returns null when the value\n * is not a parsable hex color. */\nexport function normalizeRoleHex(color: string): string | null {\n let hex = color.trim().toUpperCase();\n if (/^#[0-9A-F]{3}$/.test(hex)) {\n hex = '#' + hex[1]! + hex[1]! + hex[2]! + hex[2]! + hex[3]! + hex[3]!;\n }\n return /^#[0-9A-F]{6}$/.test(hex) ? hex : null;\n}\n\n/** A color value as builder options accept it: a hex string or a minted role\n * token. Resolved to the plain hex at element-build time. */\nfunction resolveColor(value: string | ColorToken): string;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined {\n return isColorToken(value) ? value.value : value;\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<\n out TText extends string = string,\n out TImage extends string = string,\n out TColor extends string = string,\n> {\n /** The CURRENT authoring artboard — anchor math and new elements target it.\n * Reassigned by {@link DesignBuilder.artboard}; the initially-active\n * placement stays {@link activeName}. */\n private current: Required<Pick<DesignArtboard, 'name' | 'width' | 'height'>> & {\n backgroundColor?: string;\n role?: ArtboardRole;\n };\n /** The placement that is active when the design mounts — always the FIRST\n * artboard (or the saved state's active one), never moved by `.artboard()`. */\n private readonly activeName: string;\n private readonly els: AnyElementConfig[] = [];\n /** Declared color roles, name → normalized uppercase `#RRGGBB`. */\n private readonly colorRoles = new Map<string, string>();\n private idCounter = 0;\n /** Every artboard of a multi-artboard design — set by `fromState({ artboards:\n * 'all' })` or the first `.artboard()` call; unset for single-artboard\n * 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 for (const [roleName, raw] of Object.entries(artboard.colors ?? {})) {\n const value = normalizeRoleHex(raw);\n if (value === null) {\n throw new EditorError(\n 'E_COLOR_ROLE_INVALID',\n `color role \"${roleName}\" is ${JSON.stringify(raw)} — roles must be #RGB/#RRGGBB hex ` +\n `(useColorBinding matches by normalized hex value; other syntaxes can't bind).`,\n { roleName, value: raw, docSlug: 'embed-mode' },\n );\n }\n const collision = [...this.colorRoles.entries()].find(([, v]) => v === value);\n if (collision) {\n throw new EditorError(\n 'E_COLOR_ROLE_COLLISION',\n `color roles \"${collision[0]}\" and \"${roleName}\" both normalize to ${value}. ` +\n `useColorBinding matches elements by VALUE, so colliding roles would repaint ` +\n `each other's elements — give every role a distinct hex.`,\n { roles: [collision[0], roleName], value, docSlug: 'embed-mode' },\n );\n }\n this.colorRoles.set(roleName, value);\n }\n this.current = {\n name: artboard.name || 'Front',\n width: artboard.width,\n height: artboard.height,\n backgroundColor: resolveColor(artboard.backgroundColor),\n role: artboard.role,\n };\n this.activeName = this.current.name;\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 /** A wire-shape snapshot of an authoring artboard, applying the same\n * background-export rule everywhere (a design background is part of the\n * design — exports and server renders paint it). */\n private static snapshotArtboard(a: {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n role?: ArtboardRole;\n }): StateArtboard {\n return {\n name: a.name,\n width: a.width,\n height: a.height,\n ...(a.backgroundColor\n ? {\n backgroundColor: a.backgroundColor,\n exportBackground: a.backgroundColor !== 'transparent',\n }\n : {}),\n ...(a.role ? { role: a.role } : {}),\n };\n }\n\n /**\n * Open a NEW artboard (placement) and make it the authoring context: layer\n * calls after this target the new artboard — anchor math uses its\n * dimensions, and the elements carry its membership tag (the public\n * `artboard` field, PR #482). The FIRST artboard (from `design({ name })`)\n * remains the initially-active placement when the design mounts; switch at\n * runtime with `usePlacements()` / `<Editor.Placements/>` (ADR-0088).\n *\n * `label` must be the product's placement label (case-sensitive) — the same\n * artboard-name-is-placement-label rule as `design({ name })`.\n *\n * ```ts\n * const seed = design({ name: 'Front', width: 1480, height: 2328 })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .artboard('Back', { width: 1480, height: 2328 })\n * .text('EST. 2026', { anchor: 'center', name: 'backline' });\n * ```\n *\n * @throws EditorError `E_DUPLICATE_ARTBOARD` — the design already has an\n * artboard with this label.\n */\n artboard(\n label: string,\n dims: { width: number; height: number; backgroundColor?: string | ColorToken; role?: ArtboardRole },\n ): this {\n if (!(dims.width > 0) || !(dims.height > 0)) {\n throw new Error('design().artboard(): width and height must be positive numbers');\n }\n if (!this.allArtboards) {\n this.allArtboards = [DesignBuilder.snapshotArtboard(this.current)];\n }\n if (this.allArtboards.some((a) => a.name === label)) {\n throw new EditorError(\n 'E_DUPLICATE_ARTBOARD',\n `the design already has an artboard \"${label}\" — artboard names are placement ` +\n `labels and must be unique. Existing: ${JSON.stringify(this.allArtboards.map((a) => a.name))}.`,\n { artboardName: label, validNames: this.allArtboards.map((a) => a.name), docSlug: 'embed-mode' },\n );\n }\n // Elements authored so far belong to the artboard that was current when\n // they were added — stamp the untagged ones before switching context.\n for (const el of this.els) {\n if (!el.artboard) el.artboard = this.current.name;\n }\n this.current = {\n name: label,\n width: dims.width,\n height: dims.height,\n backgroundColor: resolveColor(dims.backgroundColor),\n role: dims.role,\n };\n this.allArtboards.push(DesignBuilder.snapshotArtboard(this.current));\n return this;\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, TColor>;\n background(imageUrl: string, opts?: BackgroundOptions): DesignBuilder<TText, TImage | 'Background', TColor>;\n background(imageUrl: string, opts: BackgroundOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\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, TColor>;\n image(imageUrl: string, opts?: ImageOptions): DesignBuilder<TText, TImage, TColor>;\n image(imageUrl: string, opts: ImageOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\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, TColor>;\n text(text: string, opts?: TextOptions): DesignBuilder<TText, TImage, TColor>;\n text(text: string, opts: TextOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n const fontSize = opts.fontSize ?? Math.round(this.current.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: resolveColor(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 /**\n * Mint a {@link ColorToken} for a declared color role. Compile time: `name`\n * is constrained to the roles declared in `design({ colors })`. Runtime:\n * validated against the declared roles (CMS/JSON-built designs degrade to\n * `string` typing and rely on this throw).\n *\n * Use the token wherever the design paints the role (`fill`, `color`) AND\n * as the `useColorBinding` argument — one source of truth, no hand-synced\n * hex pairs.\n *\n * @throws EditorError `E_COLOR_ROLE_NOT_FOUND` — no role with this name;\n * lists the declared roles.\n */\n color<N extends TColor>(name: N): ColorToken<N> {\n const value = this.colorRoles.get(name);\n if (value === undefined) {\n throw new EditorError(\n 'E_COLOR_ROLE_NOT_FOUND',\n `no color role \"${name}\" in this design. Declared roles: ` +\n `${JSON.stringify([...this.colorRoles.keys()])}. Declare roles up front — ` +\n `design({ …, colors: { ${name}: '#FF2D8A' } }).`,\n { roleName: name, validNames: [...this.colorRoles.keys()], docSlug: 'embed-mode' },\n );\n }\n return mintColorToken(name, value) as ColorToken<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.current;\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: resolveColor(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 since the last context switch are\n // untagged — they belong to the CURRENT authoring artboard; stamp them\n // so the multi-artboard state groups them correctly.\n elements: this.els.map((e) =>\n e.artboard ? { ...e } : { ...e, artboard: this.current.name },\n ),\n artboards: this.allArtboards.map((a) => ({ ...a })),\n // The initially-active placement (first artboard / saved state's\n // active) — NOT the last `.artboard()` authoring context.\n activeArtboard: this.activeName,\n };\n }\n return {\n elements: this.elements,\n // A background set on a design is part of the design: flag it renderable\n // so exports and server renders paint it, matching the editor (the\n // export worker skips it without `exportBackground`) — see snapshotArtboard.\n artboards: [DesignBuilder.snapshotArtboard(this.current)],\n activeArtboard: this.current.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<const C extends Record<string, string>>(\n artboard: DesignArtboard & { colors: C },\n): DesignBuilder<never, never, Extract<keyof C, string>>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, never>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, string> {\n return new DesignBuilder<never, never, string>(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","i","type","serializeImageElement","_artboard","rest","td","hasMasks","fullWidth","fullHeight","visibleWidth","visibleHeight","serialized","DEFAULT_ANCHOR","LAYER_TOKEN_MARKER","isLayerToken","value","mintLayerToken","kind","name","COLOR_TOKEN_MARKER","isColorToken","mintColorToken","normalizeRoleHex","color","hex","resolveColor","elementLayerKind","hPart","a","vPart","anchorToCenter","anchor","elW","elH","artW","artH","padding","h","v","x","y","coverCrop","ar","artAR","fullW","cropWidth","fullH","cropHeight","DesignBuilder","artboard","roleName","raw","EditorError","collision","options","builder","prefix","label","dims","imageUrl","opts","c","w","text","fontSize","e","textNames","imageNames","size","transformData","design","layerKindOfConfig"],"mappings":"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"}