@snowcone-app/canvas 0.22.0 → 0.26.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 (59) hide show
  1. package/ERROR_CONTRACT.md +600 -0
  2. package/README.md +2 -1
  3. package/STYLING_CONTRACT.md +173 -93
  4. package/dist/{ContextualToolbars-BDEt9ND0.js → ContextualToolbars-DUhBKVxe.js} +3 -3
  5. package/dist/{ContextualToolbars-BDEt9ND0.js.map → ContextualToolbars-DUhBKVxe.js.map} +1 -1
  6. package/dist/{EditorCanvasImpl-BH_bKy-F.js → EditorCanvasImpl-BLZV_el0.js} +6 -6
  7. package/dist/{EditorCanvasImpl-BH_bKy-F.js.map → EditorCanvasImpl-BLZV_el0.js.map} +1 -1
  8. package/dist/{ImagePanel-BwqFDKvW.js → ImagePanel-sLSjaPWS.js} +3 -3
  9. package/dist/{ImagePanel-BwqFDKvW.js.map → ImagePanel-sLSjaPWS.js.map} +1 -1
  10. package/dist/{SnowconeCanvas-Cx7nXlBI.js → SnowconeCanvas-4ef4a61J.js} +6 -6
  11. package/dist/{SnowconeCanvas-Cx7nXlBI.js.map → SnowconeCanvas-4ef4a61J.js.map} +1 -1
  12. package/dist/{UnifiedFontService-CC3CSmH5.js → UnifiedFontService-Bx2MxFfy.js} +867 -834
  13. package/dist/{UnifiedFontService-CC3CSmH5.js.map → UnifiedFontService-Bx2MxFfy.js.map} +1 -1
  14. package/dist/advanced.mjs +7 -7
  15. package/dist/api/embed.d.ts +4 -4
  16. package/dist/components/editor/EditorLoadingOverlay.d.ts +12 -0
  17. package/dist/components/editor/EditorPlacements.d.ts +14 -0
  18. package/dist/components/editor/EditorPreview.d.ts +32 -8
  19. package/dist/components/editor/EditorRoot.d.ts +12 -0
  20. package/dist/components/editor/PreviewPrismOverlay.d.ts +23 -0
  21. package/dist/components/editor/RootInternals.d.ts +31 -0
  22. package/dist/components/editor/editorAudit.d.ts +66 -0
  23. package/dist/components/editor/index.d.ts +14 -0
  24. package/dist/components/editor/usePlacements.d.ts +14 -0
  25. package/dist/components/embed-realtime/context.d.ts +11 -2
  26. package/dist/components/embed-realtime/validateEmbed.d.ts +20 -1
  27. package/dist/{custom-icons-BW6hqmkL.js → custom-icons-DeYIrTK1.js} +7 -7
  28. package/dist/{custom-icons-BW6hqmkL.js.map → custom-icons-DeYIrTK1.js.map} +1 -1
  29. package/dist/design/design-builder.d.ts +108 -15
  30. package/dist/design/product-config.d.ts +10 -0
  31. package/dist/design-builder-U7Su9MMC.js +470 -0
  32. package/dist/design-builder-U7Su9MMC.js.map +1 -0
  33. package/dist/editorErrors-B2mb1sVN.js.map +1 -1
  34. package/dist/embed-DXGydZOT.js +1586 -0
  35. package/dist/embed-DXGydZOT.js.map +1 -0
  36. package/dist/embed.mjs +34 -31
  37. package/dist/errors/editorError.d.ts +27 -1
  38. package/dist/hooks/bindingGuards.d.ts +16 -0
  39. package/dist/hooks/useColorBinding.d.ts +2 -1
  40. package/dist/hooks/useFontBinding.d.ts +2 -1
  41. package/dist/hooks/useImageBinding.d.ts +2 -1
  42. package/dist/hooks/useTextBinding.d.ts +2 -1
  43. package/dist/index.mjs +6 -6
  44. package/dist/style.css +1 -1
  45. package/dist/template-fields-DJ157D3E.js +548 -0
  46. package/dist/template-fields-DJ157D3E.js.map +1 -0
  47. package/dist/testing/failureFixtures.d.ts +2 -2
  48. package/dist/testing.mjs +1 -1
  49. package/dist/{validation-C9Fe9ahK.js → validation-DnAkXdUg.js} +7 -7
  50. package/dist/{validation-C9Fe9ahK.js.map → validation-DnAkXdUg.js.map} +1 -1
  51. package/example/package.json +1 -1
  52. package/llms.txt +3 -0
  53. package/package.json +6 -3
  54. package/dist/design-builder-CT4DHIPe.js +0 -367
  55. package/dist/design-builder-CT4DHIPe.js.map +0 -1
  56. package/dist/embed-8ecUMMH-.js +0 -1226
  57. package/dist/embed-8ecUMMH-.js.map +0 -1
  58. package/dist/template-fields-C2pSfMBh.js +0 -494
  59. package/dist/template-fields-C2pSfMBh.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SnowconeCanvas-Cx7nXlBI.js","sources":["../src/components/embed/KitLayout.tsx","../src/components/embed/ErrorBoundary.tsx","../src/components/embed/EmbedRoot.tsx","../src/hooks/useAutoExport.ts","../src/hooks/useContentReady.ts","../src/utils/ArtworkPlacement.ts","../src/utils/ImageLoader.ts","../src/components/embed/kernel.tsx","../src/components/embed/SnowconeCanvas.tsx"],"sourcesContent":["/**\n * KitLayout — renders a resolved {@link KitDefinition}'s layout slots.\n *\n * The kit system defines named slots (`topbar`, `left`, `canvas`, `right`,\n * `bottombar`) that map to section IDs. Each section ID resolves, via the\n * section registry, to a React component that reads its state from the editor\n * contexts. This component is the missing renderer: it walks the kit's slots,\n * resolves each section, and lays them out around the canvas.\n *\n * Before this existed, `SnowconeCanvas` ignored `kit.layout.slots` entirely and\n * rendered only the bare `<Canvas>` — so the documented default kit\n * (`pro-studio`, advertised as \"full panels, all features\") produced an editor\n * with ZERO toolbar / panels / buttons. Mounting it and querying for any\n * interactive control returned nothing. KitLayout fixes that.\n *\n * The special section id `canvas` renders the caller-supplied canvas element\n * (which needs layout-specific props), not a registry component.\n */\n\nimport React, { useMemo, useEffect, useRef, type ReactNode } from 'react';\nimport type { KitDefinition } from '../../kits/types.js';\nimport {\n getSection,\n registerBuiltinSectionsSync,\n} from '../../kits/sections.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport { EffectsPanel } from './EffectsPanel.js';\nimport { ExportPanel } from './ExportPanel.js';\nimport { ImagePanel } from './ImagePanel.js';\nimport { GlyphPanel } from './GlyphPanel.js';\nimport { ArtboardTabs } from './ArtboardTabs.js';\nimport { ContextualToolbars } from '../ContextualToolbars.js';\nimport { ZoomControls } from './ZoomControls.js';\nimport { CropPanel } from '../CropPanel.js';\nimport {\n EditorPartsRegistry,\n EditorRootInternalsContext,\n makeInvariantReporter,\n type EditorRootInternals,\n} from '../editor/RootInternals.js';\nimport { DesignStateStore } from '../editor/designStateStore.js';\nimport { CommerceStateStore } from '../editor/commerceStore.js';\n\nconst log = createLogger('KitLayout');\n\n/** Section id used to mark where the live canvas should be rendered. */\nconst CANVAS_SECTION_ID = 'canvas';\n\n/**\n * Minimum container width (px) for a kit's chrome (toolbar / side panels /\n * menus) to actually be usable. Below this, side-panel slots (`flexShrink: 0`)\n * starve the canvas while the floating contextual toolbar — which portals to\n * `position: fixed` and clamps itself to `window.innerWidth` — renders off to\n * the side of, or visually detached from, a too-small editor. The net effect a\n * developer sees is a bare canvas with no discoverable controls. 320px is the\n * canonical smallest usable editor width (one panel + a visible canvas column).\n */\nconst MIN_CHROME_WIDTH = 320;\n\nlet registered = false;\n\n/**\n * Idempotently register the built-in sections that kit presets reference.\n *\n * Uses the synchronous registration path with direct component references so\n * the first render already has every section available (the async lazy-import\n * variant would leave the first paint empty). The part-backed entries\n * (`layers-panel` / `menu-button` / `undo-redo-controls` / `add-element-menu`)\n * are registered by the registry itself from the `Editor.*` references —\n * D6: one catalog, two access styles.\n */\nfunction ensureSectionsRegistered(): void {\n if (registered) return;\n registered = true;\n\n registerBuiltinSectionsSync({\n EffectsPanel: EffectsPanel as React.ComponentType<{}>,\n ExportPanel: ExportPanel as React.ComponentType<{}>,\n ImagePanel: ImagePanel as React.ComponentType<{}>,\n GlyphPanel: GlyphPanel as React.ComponentType<{}>,\n ArtboardTabs: ArtboardTabs as React.ComponentType<{}>,\n ContextualToolbars: ContextualToolbars as React.ComponentType<{}>,\n ZoomControls: ZoomControls as React.ComponentType<{}>,\n CropPanel: CropPanel as React.ComponentType<{}>,\n });\n}\n\n/**\n * Host context for part-backed sections in a kit layout (D6).\n *\n * The re-pointed catalog entries are real `Editor.*` parts, which read the\n * Root internals (part registry, stores) and throw `E_OUTSIDE_ROOT` when\n * mounted bare. Kit slot sections are ALWAYS kit-scoped: this provides a\n * standalone internals value around each slot's sections —\n *\n * - the registry is created with the kit's canvas pre-registered (a kit\n * layout always mounts one), so `Editor.Layers` renders the live document\n * panel and requires-canvas parts are satisfied;\n * - the stores are empty placeholders — no part-backed section reads design\n * or commerce state (Buy/TextField are not catalog sections), they exist\n * to satisfy the internals shape;\n * - invariant reports log loud instead of routing to a Root error card\n * (there is no Root boundary here; the legacy editor keeps its behavior).\n *\n * Deliberately UNCONDITIONAL (it shadows any outer `<Editor.Root>` for the\n * slot's sections): a legacy `<RealtimeEmbed>` is itself an `EditorRoot`,\n * and a `<RealtimeCanvas kit=\"pro-studio\">` inside it never registers an\n * `Editor.Canvas` with that Root — sections registering requires-canvas\n * into the REAL registry would falsely throw `E_REQUIRES_CANVAS`. Scoping\n * is per-slot and never wraps the canvas slot, so the canvas overlay (the\n * Root's `CanvasBindingBridge`) keeps seeing the real internals.\n */\nfunction KitSectionHost({ children }: { children: ReactNode }): React.ReactElement {\n const internalsRef = useRef<EditorRootInternals | null>(null);\n if (internalsRef.current === null) {\n const registry = new EditorPartsRegistry();\n registry.registerCanvas('legacy-kit-canvas');\n internalsRef.current = {\n store: new DesignStateStore({ elements: [], artboards: [], activeArtboard: '' }),\n commerce: new CommerceStateStore({ productId: '', mockupIds: [] }),\n registry,\n ...makeInvariantReporter((error) => log.error('[kit section]', error)),\n };\n }\n return (\n <EditorRootInternalsContext.Provider value={internalsRef.current}>\n {children}\n </EditorRootInternalsContext.Provider>\n );\n}\n\n/** Render the section components for one slot, skipping `canvas` + unknowns.\n * Sections render inside a {@link KitSectionHost} so the part-backed catalog\n * entries (D6) have the internals they need. */\nfunction SlotSections({ ids }: { ids: string[] | undefined }) {\n if (!ids || ids.length === 0) return null;\n return (\n <KitSectionHost>\n {ids.map((id) => {\n if (id === CANVAS_SECTION_ID) return null;\n const section = getSection(id);\n if (!section) {\n // Guarded: `process` is undefined in browser bundlers (Vite/Rollup/esbuild)\n // that don't shim it. An unguarded deref white-screens the consumer app\n // (ReferenceError: process is not defined). See packaging browser-mount gate.\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n log.warn(\n `Kit layout references unknown section \"${id}\" — skipping. ` +\n `Register it with registerSection(\"${id}\", { ... }).`,\n );\n }\n return null;\n }\n const Component = section.component;\n return <Component key={id} />;\n })}\n </KitSectionHost>\n );\n}\n\nexport interface KitLayoutProps {\n /** The resolved kit whose slots should be rendered. */\n kit: KitDefinition;\n /** The live canvas element to drop into the `canvas` slot. */\n canvas: React.ReactNode;\n}\n\n/**\n * Lay out a kit's slots around the canvas.\n *\n * Layout shape:\n * ```\n * ┌───────────────── topbar ─────────────────┐\n * │ left │ canvas │ right │\n * └───────────────── bottombar ───────────────┘\n * ```\n * Empty slots collapse (render nothing), so `embed-only` (canvas-only) renders\n * exactly the bare canvas with no chrome — preserving headless embedding.\n */\nexport function KitLayout({ kit, canvas }: KitLayoutProps) {\n ensureSectionsRegistered();\n\n const slots = kit.layout.slots;\n const hasTopbar = !!slots.topbar?.some((id) => id !== CANVAS_SECTION_ID);\n const hasLeft = !!slots.left?.some((id) => id !== CANVAS_SECTION_ID);\n const hasRight = !!slots.right?.some((id) => id !== CANVAS_SECTION_ID);\n const hasBottombar = !!slots.bottombar?.some((id) => id !== CANVAS_SECTION_ID);\n\n // Fast path: canvas-only kit (e.g. embed-only) renders just the canvas with\n // no wrapper chrome — identical to the pre-kit behaviour for headless use.\n const isCanvasOnly = useMemo(\n () => !hasTopbar && !hasLeft && !hasRight && !hasBottombar,\n [hasTopbar, hasLeft, hasRight, hasBottombar],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n\n // Dev-only guard: a kit declares chrome (toolbar / panels / menus) but the\n // measured container is too narrow for that chrome to actually render\n // usably, so the developer gets a bare canvas with no discoverable\n // controls — silently. Warn with the kit name, measured width, and the fix.\n // Skipped for canvas-only kits (e.g. `embed-only`), where a chrome-less\n // canvas is intended: `hasChrome` is false, and the `isCanvasOnly` early\n // return below never renders `rootRef`, so the observer never attaches.\n const hasChrome = hasTopbar || hasLeft || hasRight || hasBottombar;\n const warnedRef = useRef(false);\n useEffect(() => {\n if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'development') return;\n if (!hasChrome) return;\n const el = rootRef.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n\n const check = (width: number) => {\n if (width > 0 && width < MIN_CHROME_WIDTH && !warnedRef.current) {\n warnedRef.current = true;\n log.warn(\n `Kit \"${kit.name}\" declares chrome (toolbar / panels) but its container ` +\n `is only ${Math.round(width)}px wide — below the ~${MIN_CHROME_WIDTH}px ` +\n `needed to render it, so the toolbar / panels are suppressed and the ` +\n `editor looks like a bare canvas. Give the editor more container width, ` +\n `or pass kit=\"embed-only\" if a chrome-less canvas is intended.`,\n );\n } else if (width >= MIN_CHROME_WIDTH) {\n // Width recovered (e.g. container grew) — re-arm so a later shrink warns again.\n warnedRef.current = false;\n }\n };\n\n check(el.getBoundingClientRect().width);\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) check(entry.contentRect.width);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, [hasChrome, kit.name]);\n\n if (isCanvasOnly) {\n return <>{canvas}</>;\n }\n\n return (\n <div ref={rootRef} className=\"sc-kit-layout\" style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>\n {hasTopbar && (\n <div\n className=\"sc-kit-topbar sc-toolbar\"\n style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', flexShrink: 0 }}\n >\n <SlotSections ids={slots.topbar} />\n </div>\n )}\n <div className=\"sc-kit-body\" style={{ display: 'flex', flex: 1, minHeight: 0 }}>\n {hasLeft && (\n <div className=\"sc-kit-left\" style={{ flexShrink: 0, overflow: 'auto' }}>\n <SlotSections ids={slots.left} />\n </div>\n )}\n <div className=\"sc-kit-canvas\" style={{ flex: 1, minWidth: 0, position: 'relative' }}>\n {canvas}\n </div>\n {hasRight && (\n <div className=\"sc-kit-right\" style={{ flexShrink: 0, overflow: 'auto' }}>\n <SlotSections ids={slots.right} />\n </div>\n )}\n </div>\n {hasBottombar && (\n <div\n className=\"sc-kit-bottombar sc-toolbar\"\n style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}\n >\n <SlotSections ids={slots.bottombar} />\n </div>\n )}\n </div>\n );\n}\n\nexport default KitLayout;\n","/**\n * ErrorBoundary - Error boundary component for graceful error handling\n * Catches JavaScript errors in component tree and displays fallback UI\n *\n * @example\n * ```tsx\n * import { ErrorBoundary } from '@snowcone-app/canvas/embed';\n *\n * function App() {\n * return (\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>Something went wrong</h2>\n * <pre>{error.message}</pre>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}\n * onError={(error) => {\n * console.error('Editor error:', error);\n * analytics.track('Editor Error', { message: error.message });\n * }}\n * >\n * <EditorProvider>\n * <Canvas />\n * </EditorProvider>\n * </ErrorBoundary>\n * );\n * }\n * ```\n */\n\nimport { Component, ReactNode, ErrorInfo } from 'react';\nimport { createLogger } from '../../utils/logger.js';\nimport type { CanvasError } from '../../types/index.js';\n\nconst logger = createLogger('ErrorBoundary');\n\nexport interface ErrorBoundaryProps {\n children: ReactNode;\n /** Fallback UI to render when an error is caught. Can be a static ReactNode or a render function. */\n fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n /**\n * Render function for custom error UI.\n * Takes the error and a reset function, returns a ReactNode.\n * When both `fallback` and `renderError` are provided, `renderError` takes precedence.\n */\n renderError?: (error: Error, reset: () => void) => ReactNode;\n /** Callback fired when an error is caught. Receives the raw Error and React ErrorInfo. */\n onError?: (error: Error, errorInfo: ErrorInfo) => void;\n /** Callback fired when an error is caught, receiving a structured CanvasError. */\n onCanvasError?: (error: CanvasError) => void;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n}\n\n/**\n * Error boundary component for graceful error handling.\n * Catches React rendering errors in the component tree and displays fallback UI.\n * Supports both plain Error and structured CanvasError callbacks.\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n hasError: true,\n error,\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n // Log error to console\n logger.error('Caught error:', error, errorInfo);\n\n // Call custom error handler if provided (plain Error)\n if (this.props.onError) {\n this.props.onError(error, errorInfo);\n }\n\n // Call structured CanvasError handler if provided\n if (this.props.onCanvasError) {\n const canvasError: CanvasError = {\n category: 'unknown',\n message: error.message,\n originalError: error,\n recoverable: false,\n };\n this.props.onCanvasError(canvasError);\n }\n }\n\n resetError = (): void => {\n this.setState({\n hasError: false,\n error: null,\n });\n };\n\n render(): ReactNode {\n if (this.state.hasError && this.state.error) {\n // renderError takes precedence over fallback\n if (this.props.renderError) {\n return this.props.renderError(this.state.error, this.resetError);\n }\n\n // Custom fallback (legacy pattern, still supported)\n if (this.props.fallback) {\n if (typeof this.props.fallback === 'function') {\n return this.props.fallback(this.state.error, this.resetError);\n }\n return this.props.fallback;\n }\n\n // Default fallback UI\n return (\n <div\n style={{\n padding: '24px',\n border: '2px solid var(--danger)',\n borderRadius: '8px',\n backgroundColor: 'color-mix(in oklch, var(--danger) 10%, var(--background))',\n color: 'var(--danger)',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n <h2 style={{ margin: '0 0 16px 0', fontSize: '18px', fontWeight: 600 }}>\n Something went wrong\n </h2>\n <pre\n style={{\n margin: '0 0 16px 0',\n padding: '12px',\n backgroundColor: 'color-mix(in oklch, var(--danger) 15%, var(--background))',\n borderRadius: '4px',\n fontSize: '14px',\n overflow: 'auto',\n maxHeight: '200px',\n }}\n >\n {this.state.error.message}\n </pre>\n <button\n onClick={this.resetError}\n style={{\n padding: '8px 16px',\n backgroundColor: 'var(--danger)',\n color: 'var(--danger-foreground)',\n border: 'none',\n borderRadius: '4px',\n fontSize: '14px',\n fontWeight: 500,\n cursor: 'pointer',\n }}\n >\n Try again\n </button>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","/**\n * `EmbedRoot` — the embed's provider shell, extracted from `SnowconeCanvas`\n * (P1 provider inversion, ADR-0085).\n *\n * Owns the provider boundaries the standalone embed needs: theme, the editor\n * document (`EditorProvider`), and optional privileged services. Under an\n * `<Editor.Root>` the editor document is mounted by the ROOT instead (D9:\n * state always, renderer lazy) — `Editor.Canvas` passes\n * `externalProvider` so this shell skips its own `EditorProvider` and the\n * kernel writes into the Root-owned document.\n */\n\nimport React, { type ReactNode } from 'react';\nimport { EditorProvider } from '../../contexts/EditorContext.js';\nimport { ThemeProvider } from '../../contexts/ThemeContext.js';\nimport { CanvasServicesProvider, type CanvasServices } from '../../contexts/CanvasServicesContext.js';\n\nexport interface EmbedRootProps {\n /** Inherit the host app's theme instead of managing document-level theme. */\n inheritTheme?: boolean;\n /**\n * Skip mounting an `EditorProvider` — an ancestor (an `<Editor.Root>`, or a\n * host-managed provider for shared mobile/desktop layouts) already provides\n * the editor document.\n */\n externalProvider?: boolean;\n /** Privileged-service wiring (Image Browser search / AI generation). */\n services?: CanvasServices;\n /** Forwarded to `EditorProvider` (fitToArtboard fill fraction). */\n viewPadding?: number;\n children: ReactNode;\n}\n\n/**\n * Provider shell for the embeddable canvas. `SnowconeCanvas` is\n * `<EmbedRoot><KitLayout/></EmbedRoot>` plus the kernel hooks; custom layouts\n * can reuse the same shell.\n */\nexport function EmbedRoot({\n inheritTheme,\n externalProvider,\n services,\n viewPadding,\n children,\n}: EmbedRootProps): React.ReactElement {\n // When externalProvider is true, skip creating EditorProvider\n // (assumes an ancestor already provides one)\n const tree = externalProvider ? (\n <ThemeProvider defaultTheme=\"light\" passive={inheritTheme}>\n {children}\n </ThemeProvider>\n ) : (\n <ThemeProvider defaultTheme=\"light\" passive={inheritTheme}>\n <EditorProvider viewPadding={viewPadding}>{children}</EditorProvider>\n </ThemeProvider>\n );\n\n // Only mount a services provider when the prop is given — otherwise inherit\n // any provider above us (e.g. Editor.Root's), instead of shadowing it\n // with an empty one.\n if (services) {\n return <CanvasServicesProvider services={services}>{tree}</CanvasServicesProvider>;\n }\n return tree;\n}\n","/**\n * useAutoExport - React hook for automatic export on state changes\n *\n * Features:\n * - Automatically exports when editor state changes\n * - Debounced with maximum wait time (using lodash)\n * - Deep change detection (skips exports when nothing changed)\n * - Configurable timing parameters\n */\n\nimport { useEffect, useRef, useCallback, useState } from 'react';\nimport { AutoExportManager, AutoExportConfig, AutoExportStats, DEFAULT_AUTO_EXPORT_CONFIG } from '../services/AutoExportManager.js';\nimport { subscribeToImageLoads } from '../core/ImageLoadEvents.js';\nimport { createLogger } from '../utils/logger.js';\nimport type { HybridHistoryManager } from '../core/HybridHistoryManager.js';\nimport type { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { TextElement } from '../core/TextElement.js';\nimport type { ImageElement } from '../core/ImageElement.js';\nimport type { GroupElement } from '../core/GroupElement.js';\nimport type { ShapeElement } from '../core/ShapeElement.js';\nimport type { PathElement } from '../core/PathElement.js';\n\nconst logger = createLogger('useAutoExport');\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\nexport interface UseAutoExportOptions {\n /** Auto-export configuration */\n config?: Partial<AutoExportConfig>;\n\n /** Hybrid history manager to listen to for changes */\n historyManager?: HybridHistoryManager;\n\n /** Current artboards */\n artboards: ArtboardElement[];\n\n /** Current elements */\n elements: EditorElement[];\n\n /** Callback to perform the actual export */\n onExport: () => void | Promise<void>;\n\n /**\n * Callback fired immediately when an export is scheduled (before debounce).\n * Use this to show loading indicators right away instead of waiting for export to complete.\n */\n onExportScheduled?: () => void;\n\n /**\n * Whether the canvas is mounted and ready for export operations.\n * When false, exports are deferred until canvas becomes ready.\n * This prevents \"[useExport] Canvas ref not available\" errors.\n */\n isCanvasReady?: boolean;\n}\n\nexport interface UseAutoExportReturn {\n /** Current auto-export statistics */\n stats: AutoExportStats;\n\n /** Update configuration on the fly */\n updateConfig: (config: Partial<AutoExportConfig>) => void;\n\n /** Force an immediate export (bypassing debounce) */\n forceExport: () => Promise<void>;\n\n /** Reset statistics */\n resetStats: () => void;\n\n /** Reset change detection (next export will always trigger) */\n resetChangeDetection: () => void;\n}\n\n/**\n * Hook to manage automatic export on state changes\n *\n * @example\n * ```tsx\n * const { stats, updateConfig } = useAutoExport({\n * config: {\n * enabled: true,\n * debounceMs: 100,\n * maxWaitMs: 1000,\n * },\n * historyManager,\n * artboards,\n * elements,\n * onExport: async () => {\n * await exportAllArtboards();\n * },\n * });\n * ```\n */\nexport function useAutoExport(options: UseAutoExportOptions): UseAutoExportReturn {\n const { config, historyManager, artboards, elements, onExport, onExportScheduled, isCanvasReady = false } = options;\n\n const managerRef = useRef<AutoExportManager | null>(null);\n // Track if we have a pending export waiting for canvas to become ready\n const pendingExportRef = useRef<boolean>(false);\n // Stats stored in ref to avoid re-rendering host component (e.g. SnowconeCanvas)\n // on every auto-export. Only promoted to state on explicit user actions.\n const statsRef = useRef<AutoExportStats>({\n totalExports: 0,\n skippedExports: 0,\n lastExportTime: null,\n avgExportInterval: 0,\n });\n const [stats, setStats] = useState<AutoExportStats>(statsRef.current);\n\n // Ref for onExport — always reads the latest callback at execution time.\n // This eliminates stale-closure bugs: the manager's onExport callback never\n // goes stale because it always dereferences onExportRef.current.\n const onExportRef = useRef(onExport);\n onExportRef.current = onExport;\n\n // Initialize manager\n useEffect(() => {\n\n const manager = new AutoExportManager({\n ...DEFAULT_AUTO_EXPORT_CONFIG,\n ...config,\n });\n\n // Register export callback — uses ref so it always calls the latest onExport.\n // No need to re-register when onExport changes (the ref handles it).\n manager.onExport(async () => {\n try {\n await onExportRef.current();\n\n // Update ref only — no React state update to avoid re-rendering\n // the host component (SnowconeCanvas) on every auto-export\n statsRef.current = manager.getStats();\n } catch (error) {\n logger.error('[useAutoExport] Export failed:', error);\n }\n });\n\n managerRef.current = manager;\n\n // Cleanup on unmount\n return () => {\n manager.destroy();\n };\n }, []); // Only initialize once\n\n // Trigger pending export when canvas becomes ready\n useEffect(() => {\n if (isCanvasReady && pendingExportRef.current && managerRef.current) {\n pendingExportRef.current = false;\n managerRef.current.forceExport();\n }\n }, [isCanvasReady]);\n\n // Update config when it changes\n useEffect(() => {\n if (managerRef.current && config) {\n managerRef.current.updateConfig(config);\n }\n }, [config?.enabled, config?.debounceMs, config?.maxWaitMs]);\n\n // onExport callback changes are handled via onExportRef (set above).\n // No useEffect re-registration needed — eliminates the race condition\n // where the effect fires after the debounced export has already captured\n // a stale callback reference.\n\n // Update onExportScheduled callback when it changes\n useEffect(() => {\n if (managerRef.current && onExportScheduled) {\n managerRef.current.onExportScheduled(onExportScheduled);\n }\n }, [onExportScheduled]);\n\n // Hook into history manager to listen for changes\n useEffect(() => {\n if (!historyManager || !managerRef.current) {\n return;\n }\n\n\n // Subscribe to command executed events\n const unsubscribe = historyManager.onCommandExecuted(() => {\n\n // Schedule export (will be debounced)\n if (managerRef.current) {\n // Gate on canvas ready to prevent \"[useExport] Canvas ref not available\" errors\n if (!isCanvasReady) {\n pendingExportRef.current = true;\n return;\n }\n managerRef.current.scheduleExport();\n }\n });\n\n // Cleanup\n return () => {\n unsubscribe();\n };\n }, [historyManager, isCanvasReady]);\n\n // Also watch for direct state changes (e.g., during drag operations)\n // This ensures exports trigger even when changes bypass command history\n //\n // PERF: We intentionally avoid JSON.stringify here. During drag operations,\n // elements changes on every mouse move. JSON.stringify of all elements was\n // costing 50-200ms per frame — causing visible stutter. Instead we just call\n // scheduleExport() which is a cheap debounce timer reset. The AutoExportManager\n // does its own change detection (via getCurrentStateSnapshot) before actually\n // exporting, so redundant exports are still prevented.\n useEffect(() => {\n if (!managerRef.current) {\n return;\n }\n\n // Gate on canvas ready to prevent \"[useExport] Canvas ref not available\" errors\n if (!isCanvasReady) {\n pendingExportRef.current = true;\n return;\n }\n\n // Schedule export (debounced + manager does its own change detection)\n managerRef.current.scheduleExport();\n }, [elements, artboards, isCanvasReady]);\n\n // Re-trigger export when an image finishes loading.\n // Adding an image element fires auto-export via the elements dep above,\n // but the image hasn't loaded yet so it gets skipped during serialization.\n // This subscription ensures we re-export once the bitmap is available.\n useEffect(() => {\n const unsubscribe = subscribeToImageLoads((elementId) => {\n if (!managerRef.current || !isCanvasReady) return;\n const hasElement = elements.some(e => e.id === elementId);\n if (hasElement) {\n managerRef.current.scheduleExport();\n }\n });\n return unsubscribe;\n }, [elements, isCanvasReady]);\n\n // Return control functions\n const updateConfig = useCallback((newConfig: Partial<AutoExportConfig>) => {\n if (managerRef.current) {\n managerRef.current.updateConfig(newConfig);\n }\n }, []);\n\n const forceExport = useCallback(async () => {\n if (managerRef.current) {\n await managerRef.current.forceExport();\n statsRef.current = managerRef.current.getStats();\n setStats(statsRef.current);\n }\n }, []);\n\n const resetStats = useCallback(() => {\n if (managerRef.current) {\n managerRef.current.resetStats();\n statsRef.current = managerRef.current.getStats();\n setStats(statsRef.current);\n }\n }, []);\n\n const resetChangeDetection = useCallback(() => {\n if (managerRef.current) {\n managerRef.current.resetChangeDetection();\n }\n }, []);\n\n return {\n stats,\n updateConfig,\n forceExport,\n resetStats,\n resetChangeDetection,\n };\n}\n","/**\n * useContentReady - Tracks when canvas content is truly ready for export.\n *\n * Combines multiple signals:\n * 1. DOM + artboards ready (isCanvasReady)\n * 2. Initial elements deserialized and loaded\n * 3. Fonts used by text elements available in the browser\n * 4. At least one render frame completed after all the above\n *\n * Returns a latching boolean — once true, never goes back to false.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { createLogger } from '../utils/logger.js';\nimport type { TextElement } from '../core/TextElement.js';\nimport type { ImageElement } from '../core/ImageElement.js';\nimport type { GroupElement } from '../core/GroupElement.js';\nimport type { ShapeElement } from '../core/ShapeElement.js';\nimport type { PathElement } from '../core/PathElement.js';\n\nconst logger = createLogger('useContentReady');\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\nexport interface UseContentReadyOptions {\n /** DOM mounted and artboards initialized */\n isCanvasReady: boolean;\n /** Current elements in the editor */\n elements: EditorElement[];\n /** Were initialElements provided to SnowconeCanvas? */\n hasInitialElements: boolean;\n /** Have initialElements been deserialized into the editor? */\n initialElementsLoaded: boolean;\n}\n\n/**\n * Extract unique font families from text elements.\n * Only text-based elements (transformType !== 'image' and !== 'group') have fontFamily.\n */\nfunction extractFontFamilies(elements: EditorElement[]): string[] {\n const fonts = new Set<string>();\n for (const el of elements) {\n if ('fontFamily' in el && typeof el.fontFamily === 'string' && el.fontFamily) {\n fonts.add(el.fontFamily);\n }\n // Also check rich text spans for per-character font families\n if ('getRichText' in el && typeof el.getRichText === 'function') {\n try {\n const richText = el.getRichText();\n if (richText?.spans) {\n for (const span of richText.spans) {\n if (span.style?.fontFamily) {\n fonts.add(span.style.fontFamily);\n }\n }\n }\n } catch {\n // Ignore errors reading rich text\n }\n }\n }\n return Array.from(fonts);\n}\n\n/**\n * Check if all fonts are available, and load any that aren't.\n * Uses the standard FontFaceSet API.\n */\nasync function ensureFontsLoaded(fontFamilies: string[]): Promise<void> {\n if (fontFamilies.length === 0) return;\n if (typeof document === 'undefined' || !document.fonts) return;\n\n // CRITICAL: do NOT gate on `document.fonts.check()`. It returns `true` for\n // any specifier whose fonts are *currently satisfiable* — including custom\n // families with no `@font-face` registered, because the browser falls back\n // to a default font and considers the request fulfilled. So a freshly\n // deserialized design that references \"Bangers\" before the Google Fonts\n // `<link>` has loaded its CSS will pass `check()` (no FontFace exists yet\n // → fallback satisfies it), the auto-export gate releases, the worker\n // exports the placement PNG with a serif fallback, and a moment later the\n // `<link>` finishes loading and the on-screen canvas re-renders correctly\n // — leaving the saved placement permanently wrong.\n //\n // `document.fonts.load()` resolves with `[]` for missing fonts, so calling\n // it unconditionally is cheap and gives us the actual \"is this glyph data\n // available?\" semantics we want.\n const pending: Promise<FontFace[]>[] = [];\n for (const family of fontFamilies) {\n const testString = `16px \"${family}\"`;\n pending.push(document.fonts.load(testString));\n }\n await Promise.all(pending);\n\n // iOS Safari quirk: `document.fonts.load()` can resolve before the font is\n // actually activated for *freshly created* canvas contexts (the offscreen\n // `document.createElement('canvas')` used by the main-thread export path,\n // and `OffscreenCanvas` in workers). The on-screen `<canvas>` paints\n // correctly because Safari activates fonts on first DOM use, but the\n // detached canvas the export builds was never DOM-attached. `document.fonts.ready`\n // resolves *after* all in-flight font activation work has settled — that's\n // the sync point detached-canvas font availability needs on iOS.\n //\n // Race with a 250ms timeout — on Chrome `ready` resolves in <1ms post-load,\n // on iOS this is the activation barrier. The race protects against a\n // wedged FontFaceSet (e.g. a `<link>` that never finishes parsing) holding\n // the export gate open indefinitely.\n if (document.fonts.ready) {\n await Promise.race([\n document.fonts.ready,\n new Promise<void>((resolve) => setTimeout(resolve, 250)),\n ]);\n }\n logger.debug(`Awaited ${pending.length} font(s)`);\n}\n\n/**\n * Wait one animation frame so the canvas render loop draws with loaded content.\n */\nfunction waitOneFrame(): Promise<void> {\n return new Promise((resolve) => requestAnimationFrame(() => resolve()));\n}\n\nexport function useContentReady(options: UseContentReadyOptions): boolean {\n const { isCanvasReady, elements, hasInitialElements, initialElementsLoaded } = options;\n const [isContentReady, setIsContentReady] = useState(false);\n const checkingRef = useRef(false);\n\n useEffect(() => {\n // Already latched — nothing to do\n if (isContentReady) return;\n\n // Gate 1: DOM + artboards must be ready\n if (!isCanvasReady) return;\n\n // Gate 2: If initial elements were provided, they must be loaded\n if (hasInitialElements && !initialElementsLoaded) return;\n\n // Gate 3: If initial elements were provided, the elements array must be non-empty\n if (hasInitialElements && elements.length === 0) return;\n\n // Prevent concurrent checks\n if (checkingRef.current) return;\n checkingRef.current = true;\n\n let cancelled = false;\n\n (async () => {\n try {\n // Gate 4: Ensure all fonts used by text elements are loaded\n const fonts = extractFontFamilies(elements);\n if (fonts.length > 0) {\n await ensureFontsLoaded(fonts);\n if (cancelled) return;\n }\n\n // Gate 5: Wait one render frame so the canvas draws with ready content\n await waitOneFrame();\n if (cancelled) return;\n setIsContentReady(true);\n } catch (err) {\n logger.error('Error during content readiness check:', err);\n // Still mark ready on error to avoid blocking exports forever\n if (!cancelled) {\n setIsContentReady(true);\n }\n } finally {\n checkingRef.current = false;\n }\n })();\n\n return () => {\n cancelled = true;\n checkingRef.current = false;\n };\n }, [isCanvasReady, elements, hasInitialElements, initialElementsLoaded, isContentReady]);\n\n return isContentReady;\n}\n","/**\n * ArtworkPlacement - Utility for calculating artwork position and scale\n *\n * Uses \"cover\" logic (like CSS object-fit: cover) to ensure artwork fills\n * the entire artboard while maintaining aspect ratio.\n */\n\n/**\n * 9-point alignment options for positioning artwork\n */\nexport type Alignment = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br';\n\n/**\n * Scale mode for artwork placement\n * - \"cover\" (default): fills entire area, may crop (like CSS object-fit: cover)\n * - \"contain\": fits entirely inside area, may have empty space (like CSS object-fit: contain)\n */\nexport type ScaleMode = 'cover' | 'contain';\n\n/**\n * Placement configuration for artwork positioning\n */\nexport interface PlacementConfig {\n /** Artboard width in pixels */\n width: number;\n /** Artboard height in pixels */\n height: number;\n /** User scale multiplier (default: 1) */\n scale?: number;\n /** 9-point alignment (default: \"c\" for center) */\n align?: Alignment;\n /** X offset in range -1 to 1 (default: 0) */\n offsetX?: number;\n /** Y offset in range -1 to 1 (default: 0) */\n offsetY?: number;\n /** Scale mode: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: ScaleMode;\n /** Top margin in pixels (reduces available area in contain mode) */\n marginTop?: number;\n /** Right margin in pixels (reduces available area in contain mode) */\n marginRight?: number;\n /** Bottom margin in pixels (reduces available area in contain mode) */\n marginBottom?: number;\n /** Left margin in pixels (reduces available area in contain mode) */\n marginLeft?: number;\n}\n\n/**\n * Result of artwork placement calculation\n */\nexport interface ArtworkPlacementResult {\n /** Final scale to apply to artwork */\n scale: number;\n /** Final artwork dimensions after scaling */\n width: number;\n height: number;\n /** Position to draw artwork (relative to artboard origin) */\n x: number;\n y: number;\n /** Debug information */\n debug: {\n coverScale: number;\n userScale: number;\n overflow: { x: number; y: number };\n alignment: string;\n appliedOffset: { x: number; y: number };\n };\n}\n\n/**\n * Map string alignment values to 9-point grid\n */\nexport const alignmentMap: Record<string, Alignment> = {\n // Vertical alignments (map to top/center/bottom center)\n 'far-top': 't',\n 'top': 't',\n 'center': 'c',\n 'bottom': 'b',\n 'far-bottom': 'b',\n // Horizontal alignments (map to left/center/right middle)\n 'far-left': 'l',\n 'left': 'l',\n 'right': 'r',\n 'far-right': 'r',\n // Direct 9-point values\n 'tl': 'tl',\n 't': 't',\n 'tr': 'tr',\n 'l': 'l',\n 'c': 'c',\n 'r': 'r',\n 'bl': 'bl',\n 'b': 'b',\n 'br': 'br',\n};\n\n/**\n * Get Alignment from string (supports various naming conventions)\n */\nexport function getAlignment(value: string | Alignment | undefined): Alignment {\n if (!value) return 'c';\n return alignmentMap[value] || 'c';\n}\n\n/**\n * Calculates how to size and position artwork on an artboard\n * Uses \"cover\" logic (like CSS object-fit: cover) to ensure artwork fills the entire area\n *\n * @param artwork - The artwork dimensions\n * @param placement - The placement configuration\n * @returns Positioning and scaling information\n *\n * @example\n * const result = calculateArtworkPlacement(\n * { width: 1000, height: 1000 }, // Square artwork\n * { width: 500, height: 800 } // Tall artboard\n * );\n * // Result: artwork scaled to 800x800, positioned at x: -150, y: 0\n */\nexport function calculateArtworkPlacement(\n artwork: { width: number; height: number },\n placement: PlacementConfig\n): ArtworkPlacementResult {\n // Step 1: Calculate base scale based on scale mode\n const mode = placement.scaleMode || 'cover';\n\n // Margins reduce available area (only in contain mode)\n const mTop = (mode === 'contain' ? placement.marginTop : 0) || 0;\n const mRight = (mode === 'contain' ? placement.marginRight : 0) || 0;\n const mBottom = (mode === 'contain' ? placement.marginBottom : 0) || 0;\n const mLeft = (mode === 'contain' ? placement.marginLeft : 0) || 0;\n\n const effectiveWidth = placement.width - mLeft - mRight;\n const effectiveHeight = placement.height - mTop - mBottom;\n\n const scaleX = effectiveWidth / artwork.width;\n const scaleY = effectiveHeight / artwork.height;\n\n // \"cover\": use LARGER scale so artwork fills entire area (may crop)\n // \"contain\": use SMALLER scale so artwork fits entirely inside (may have space)\n const coverScale = mode === 'contain'\n ? Math.min(scaleX, scaleY)\n : Math.max(scaleX, scaleY);\n\n // Step 2: Apply user's custom scale multiplier\n const userScale = placement.scale || 1;\n const finalScale = coverScale * userScale;\n\n // Step 3: Calculate final artwork dimensions after scaling\n const scaledArtworkWidth = artwork.width * finalScale;\n const scaledArtworkHeight = artwork.height * finalScale;\n\n // Step 4: Calculate base position using alignment\n const alignment = placement.align || 'c'; // default to center\n let x = 0;\n let y = 0;\n\n // Calculate the \"overflow\" relative to effective area\n // In contain mode with margins, overflow is relative to the margin-bounded area\n const overflowX = scaledArtworkWidth - effectiveWidth;\n const overflowY = scaledArtworkHeight - effectiveHeight;\n\n // Apply 9-point alignment\n switch (alignment) {\n // Top row\n case 'tl': // Top-Left\n x = 0;\n y = 0;\n break;\n case 't': // Top-Center\n x = -overflowX / 2;\n y = 0;\n break;\n case 'tr': // Top-Right\n x = -overflowX;\n y = 0;\n break;\n\n // Middle row\n case 'l': // Middle-Left\n x = 0;\n y = -overflowY / 2;\n break;\n case 'c': // Center (default)\n default:\n x = -overflowX / 2;\n y = -overflowY / 2;\n break;\n case 'r': // Middle-Right\n x = -overflowX;\n y = -overflowY / 2;\n break;\n\n // Bottom row\n case 'bl': // Bottom-Left\n x = 0;\n y = -overflowY;\n break;\n case 'b': // Bottom-Center\n x = -overflowX / 2;\n y = -overflowY;\n break;\n case 'br': // Bottom-Right\n x = -overflowX;\n y = -overflowY;\n break;\n }\n\n // Step 5: Apply margin offsets (shift positions into margin-bounded area)\n x += mLeft;\n y += mTop;\n\n // Step 6: Apply user offsets\n // Offsets are in range -1 to 1, so convert to pixel values\n const offsetX = placement.offsetX || 0;\n const offsetY = placement.offsetY || 0;\n\n // The \"range\" is how far we can move the artwork\n const xRange = overflowX;\n const yRange = overflowY;\n\n x += offsetX * xRange;\n y += offsetY * yRange;\n\n // Round to whole pixels\n x = Math.round(x);\n y = Math.round(y);\n\n return {\n // Final scale to apply to artwork\n scale: finalScale,\n\n // Final artwork dimensions after scaling\n width: Math.round(scaledArtworkWidth),\n height: Math.round(scaledArtworkHeight),\n\n // Position to draw artwork (relative to artboard origin)\n x,\n y,\n\n // Debug info\n debug: {\n coverScale,\n userScale,\n overflow: { x: overflowX, y: overflowY },\n alignment,\n appliedOffset: { x: offsetX * xRange, y: offsetY * yRange },\n },\n };\n}\n","/**\n * ImageLoader - Utility for loading images from URLs\n * Provides a clean async API with loading states and error handling\n */\n\nimport { ImageElement } from '../core/ImageElement.js';\nimport type { ImageElementConfig } from '../types/index.js';\nimport { calculateArtworkPlacement, getAlignment, type Alignment, type PlacementConfig, type ScaleMode } from './ArtworkPlacement.js';\n\nexport interface ImageLoadResult {\n success: boolean;\n element?: HTMLImageElement;\n error?: Error;\n width?: number;\n height?: number;\n aspectRatio?: number;\n}\n\nexport interface ImageLoaderOptions {\n /** Timeout in milliseconds (default: 30000 - 30 seconds) */\n timeout?: number;\n /** Number of retry attempts on failure (default: 1) */\n retries?: number;\n /** Retry delay in milliseconds (default: 1000) */\n retryDelay?: number;\n /** Whether to validate URL before loading (default: true) */\n validateUrl?: boolean;\n}\n\nexport type ImageLoadingState = 'idle' | 'loading' | 'success' | 'error';\n\nconst DEFAULT_OPTIONS: Required<ImageLoaderOptions> = {\n timeout: 30000,\n retries: 1,\n retryDelay: 1000,\n validateUrl: true,\n};\n\n/**\n * Validate if a string is a valid URL\n */\nexport function isValidImageUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n // Only allow http, https, data, and blob URLs\n return ['http:', 'https:', 'data:', 'blob:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\n/**\n * Load an image from a URL\n * Returns a promise that resolves with the loaded HTMLImageElement\n */\nexport async function loadImageFromURL(\n url: string,\n options: ImageLoaderOptions = {}\n): Promise<ImageLoadResult> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Validate URL if enabled\n if (opts.validateUrl && !isValidImageUrl(url)) {\n return {\n success: false,\n error: new Error(`Invalid image URL: ${url}`),\n };\n }\n\n let lastError: Error | undefined;\n\n // Attempt to load with retries\n for (let attempt = 0; attempt <= opts.retries; attempt++) {\n try {\n const result = await loadImageOnce(url, opts.timeout);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Wait before retrying (unless this was the last attempt)\n if (attempt < opts.retries) {\n await delay(opts.retryDelay);\n }\n }\n }\n\n return {\n success: false,\n error: lastError || new Error('Failed to load image after retries'),\n };\n}\n\n/**\n * Load image once (single attempt)\n */\nfunction loadImageOnce(url: string, timeout: number): Promise<ImageLoadResult> {\n return new Promise((resolve) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let resolved = false;\n\n const cleanup = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n const handleSuccess = () => {\n if (resolved) return;\n resolved = true;\n cleanup();\n\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n resolve({\n success: true,\n element: img,\n width: w,\n height: h,\n aspectRatio: h > 0 ? w / h : 1,\n });\n };\n\n const handleError = (errorMsg: string) => {\n if (resolved) return;\n resolved = true;\n cleanup();\n\n resolve({\n success: false,\n error: new Error(errorMsg),\n });\n };\n\n img.onload = handleSuccess;\n\n img.onerror = () => {\n handleError(`Failed to load image: ${url}`);\n };\n\n // Set timeout\n timeoutId = setTimeout(() => {\n handleError(`Image load timed out after ${timeout}ms: ${url}`);\n }, timeout);\n\n // Start loading\n img.src = url;\n });\n}\n\n/**\n * Preload multiple images in parallel\n * Returns results for all images (some may fail)\n */\nexport async function preloadImages(\n urls: string[],\n options: ImageLoaderOptions = {}\n): Promise<Map<string, ImageLoadResult>> {\n const results = new Map<string, ImageLoadResult>();\n\n const promises = urls.map(async (url) => {\n const result = await loadImageFromURL(url, options);\n results.set(url, result);\n });\n\n await Promise.all(promises);\n return results;\n}\n\n/**\n * Create an ImageElement from a URL with loading state handling\n */\nexport async function createImageElement(\n url: string,\n config: Partial<ImageElementConfig> = {},\n options: ImageLoaderOptions = {}\n): Promise<{\n success: boolean;\n element?: ImageElement;\n error?: Error;\n}> {\n const loadResult = await loadImageFromURL(url, options);\n\n if (!loadResult.success) {\n return {\n success: false,\n error: loadResult.error,\n };\n }\n\n // Create ImageElement with the URL\n // The ImageElement constructor will load the image itself\n const imageElement = new ImageElement({\n ...config,\n imageUrl: url,\n imageAspectRatio: loadResult.aspectRatio,\n transformData: {\n type: 'image',\n width: config.transformData?.width || Math.min(loadResult.width || 400, 400),\n height: config.transformData?.height || Math.min(loadResult.height || 400, 400),\n cropX: config.transformData?.cropX ?? 0,\n cropY: config.transformData?.cropY ?? 0,\n cropWidth: config.transformData?.cropWidth ?? 1,\n cropHeight: config.transformData?.cropHeight ?? 1,\n flipHorizontal: config.transformData?.flipHorizontal ?? false,\n flipVertical: config.transformData?.flipVertical ?? false,\n borderRadius: config.transformData?.borderRadius ?? 0,\n },\n });\n\n return {\n success: true,\n element: imageElement,\n };\n}\n\n/**\n * Configuration for artwork placement on artboard\n */\nexport interface ArtworkPlacementOptions {\n /** Artboard dimensions */\n artboard: {\n width: number;\n height: number;\n /** Artboard position on canvas (for proper element positioning) */\n x: number;\n y: number;\n };\n /** Alignment string (e.g., 'center', 'top', 'bottom-left') */\n alignment?: string | Alignment;\n /** User scale multiplier (default: 1) */\n scale?: number;\n /** X offset in range -1 to 1 */\n offsetX?: number;\n /** Y offset in range -1 to 1 */\n offsetY?: number;\n /** Scale mode: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: ScaleMode;\n /** Top margin in pixels (reduces available area in contain mode) */\n marginTop?: number;\n /** Right margin in pixels (reduces available area in contain mode) */\n marginRight?: number;\n /** Bottom margin in pixels (reduces available area in contain mode) */\n marginBottom?: number;\n /** Left margin in pixels (reduces available area in contain mode) */\n marginLeft?: number;\n}\n\n/**\n * Create an ImageElement with proper \"cover\" sizing and placement on artboard\n *\n * This uses CSS-like object-fit: cover logic to ensure the artwork completely\n * fills the artboard while maintaining aspect ratio, with 9-point alignment support.\n *\n * @example\n * const result = await createImageElementWithPlacement(\n * 'https://example.com/art.png',\n * {\n * artboard: { width: 1200, height: 1500, x: 250, y: 250 },\n * alignment: 'center',\n * scale: 1\n * }\n * );\n */\nexport async function createImageElementWithPlacement(\n url: string,\n placementOptions: ArtworkPlacementOptions,\n config: Partial<ImageElementConfig> = {},\n loaderOptions: ImageLoaderOptions = {}\n): Promise<{\n success: boolean;\n element?: ImageElement;\n error?: Error;\n placement?: {\n artworkSize: { width: number; height: number };\n finalSize: { width: number; height: number };\n position: { x: number; y: number };\n scale: number;\n };\n}> {\n const loadResult = await loadImageFromURL(url, loaderOptions);\n\n if (!loadResult.success || !loadResult.width || !loadResult.height) {\n return {\n success: false,\n error: loadResult.error || new Error('Failed to get image dimensions'),\n };\n }\n\n // Calculate placement using cover/contain algorithm\n const placementConfig: PlacementConfig = {\n width: placementOptions.artboard.width,\n height: placementOptions.artboard.height,\n scale: placementOptions.scale,\n align: getAlignment(placementOptions.alignment),\n offsetX: placementOptions.offsetX,\n offsetY: placementOptions.offsetY,\n scaleMode: placementOptions.scaleMode,\n marginTop: placementOptions.marginTop,\n marginRight: placementOptions.marginRight,\n marginBottom: placementOptions.marginBottom,\n marginLeft: placementOptions.marginLeft,\n };\n\n const placement = calculateArtworkPlacement(\n { width: loadResult.width, height: loadResult.height },\n placementConfig\n );\n\n // placement.x and placement.y are the TOP-LEFT corner position (relative to artboard origin)\n // ImageElement.x and ImageElement.y represent the CENTER of the image\n // So we need to convert from top-left to center position\n\n // Calculate absolute TOP-LEFT position on canvas (artboard position + relative position)\n const topLeftX = placementOptions.artboard.x + placement.x;\n const topLeftY = placementOptions.artboard.y + placement.y;\n\n // Convert to CENTER position (what ImageElement expects)\n const centerX = topLeftX + placement.width / 2;\n const centerY = topLeftY + placement.height / 2;\n\n\n // Create ImageElement with calculated dimensions and CENTER position\n // Set preserveDimensions: true to prevent the image load callback from\n // recalculating the dimensions (which would break our \"cover\" sizing)\n const imageElement = new ImageElement({\n ...config,\n x: centerX,\n y: centerY,\n imageUrl: url,\n imageAspectRatio: loadResult.aspectRatio,\n preserveDimensions: true, // Don't recalculate dimensions on image load\n transformData: {\n type: 'image',\n width: placement.width,\n height: placement.height,\n cropX: config.transformData?.cropX ?? 0,\n cropY: config.transformData?.cropY ?? 0,\n cropWidth: config.transformData?.cropWidth ?? 1,\n cropHeight: config.transformData?.cropHeight ?? 1,\n flipHorizontal: config.transformData?.flipHorizontal ?? false,\n flipVertical: config.transformData?.flipVertical ?? false,\n borderRadius: config.transformData?.borderRadius ?? 0,\n },\n });\n\n return {\n success: true,\n element: imageElement,\n placement: {\n artworkSize: { width: loadResult.width, height: loadResult.height },\n finalSize: { width: placement.width, height: placement.height },\n position: { x: centerX, y: centerY },\n scale: placement.scale,\n },\n };\n}\n\n/**\n * Check if an image URL is accessible (HEAD request)\n * More efficient than fully loading the image\n */\nexport async function checkImageUrl(url: string): Promise<boolean> {\n try {\n // Use fetch with HEAD to check if the URL is accessible\n const response = await fetch(url, {\n method: 'HEAD',\n mode: 'cors',\n });\n\n // Check if response is an image\n const contentType = response.headers.get('content-type');\n return response.ok && (contentType?.startsWith('image/') || false);\n } catch {\n // Fallback: Try to load the image directly\n // Some servers don't support HEAD requests\n const result = await loadImageFromURL(url, { timeout: 5000, retries: 0 });\n return result.success;\n }\n}\n\n/**\n * Get image dimensions without fully loading it (when possible)\n * Falls back to full load if necessary\n */\nexport async function getImageDimensions(\n url: string\n): Promise<{ width: number; height: number } | null> {\n const result = await loadImageFromURL(url, { timeout: 10000, retries: 0 });\n\n if (result.success && result.width && result.height) {\n return { width: result.width, height: result.height };\n }\n\n return null;\n}\n\n// Helper function\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * The embed KERNEL — SnowconeCanvas's non-chrome behaviors as hooks\n * (P1 provider inversion, ADR-0085): artboard init/sync, saved-state restore,\n * initial-image loading, change/selection callbacks, auto-export, readiness.\n *\n * Extracted VERBATIM from `SnowconeCanvasInner` so the provider boundary can\n * move (Editor.Root mounts EditorProvider; `EmbedRoot` mounts it for the\n * legacy standalone path) without dragging two thousand lines of lifecycle\n * along. Every hook runs INSIDE an EditorProvider — they are the write path\n * into the editor document, not state owners themselves.\n *\n * This module imports the paint/export pipeline (CanvasRenderer, useExport),\n * so it must stay OUT of the always-mounted Editor.Root graph: only the\n * SnowconeCanvas chunk (behind `Editor.Canvas`'s lazy boundary) may import it.\n * CI-gated by scripts/check-import-graph.mjs.\n */\n\nimport React, { useEffect, useRef, useCallback, useState, useImperativeHandle } from 'react';\nimport { useEditor } from '../../contexts/EditorContext.js';\nimport { useArtboards } from '../../hooks/useArtboards.js';\nimport { useExport } from '../../hooks/useExport.js';\nimport { useAutoExport } from '../../hooks/useAutoExport.js';\nimport { useContentReady } from '../../hooks/useContentReady.js';\nimport { createImageElementWithPlacement } from '../../utils/ImageLoader.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { CanvasRenderer } from '../../core/CanvasRenderer.js';\nimport { ElementFactory } from '../../core/ElementFactory.js';\nimport type { CanvasError, AnyElementConfig } from '../../types/index.js';\nimport type {\n AlignmentPoint,\n ArtboardConfig,\n CanvasState,\n ExportStatusEvent,\n PieceGuidesConfig,\n SnowconeCanvasHandle,\n} from './SnowconeCanvas.js';\n\nconst log = createLogger('SnowconeCanvas');\n\nexport type ImageLoadingState = 'idle' | 'loading' | 'success' | 'error';\n\n/**\n * Calculate the effective export scale for an artboard.\n * If the scaled dimensions would exceed maxSize, reduces the scale to cap at maxSize.\n *\n * @param width - Artboard width in pixels\n * @param height - Artboard height in pixels\n * @param requestedScale - The requested export scale multiplier\n * @param maxSize - Maximum dimension (largest side) for exported image\n * @returns The effective scale to use (may be lower than requestedScale)\n */\nexport function calculateEffectiveScale(\n width: number,\n height: number,\n requestedScale: number,\n maxSize: number\n): number {\n // If maxSize is 0 or Infinity, don't cap\n if (maxSize <= 0 || !isFinite(maxSize)) {\n return requestedScale;\n }\n\n const scaledWidth = width * requestedScale;\n const scaledHeight = height * requestedScale;\n const largestScaledSide = Math.max(scaledWidth, scaledHeight);\n\n // If already under the limit, use requested scale\n if (largestScaledSide <= maxSize) {\n return requestedScale;\n }\n\n // Calculate the scale that would result in exactly maxSize for the largest side\n const largestSide = Math.max(width, height);\n const cappedScale = maxSize / largestSide;\n\n // Don't scale up if we're capping (scale should be at most 1 if artboard is already >= maxSize)\n return Math.min(cappedScale, requestedScale);\n}\n\n/**\n * Error channel: structured `onError` emission + the CanvasRenderer static\n * render-error wire + the ErrorBoundary handler.\n */\nexport function useCanvasErrorChannel(onError: ((error: CanvasError) => void) | undefined): {\n emitError: (error: CanvasError) => void;\n handleBoundaryError: (error: Error) => void;\n} {\n // Stable ref to avoid re-creating callbacks when onError changes\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n const emitError = useCallback((error: CanvasError) => {\n log.error(`[${error.category}] ${error.message}`, error.originalError);\n onErrorRef.current?.(error);\n }, []);\n\n // Wire CanvasRenderer's static error callback to emitError\n useEffect(() => {\n CanvasRenderer.onRenderError = emitError;\n return () => {\n // Only clear if it's still our callback (avoid removing another instance's callback)\n if (CanvasRenderer.onRenderError === emitError) {\n CanvasRenderer.onRenderError = null;\n }\n };\n }, [emitError]);\n\n // Fires emitError before the ErrorBoundary renders its fallback UI\n const handleBoundaryError = useCallback((error: Error) => {\n emitError({\n category: 'unknown',\n message: error.message,\n originalError: error,\n recoverable: false,\n });\n }, [emitError]);\n\n return { emitError, handleBoundaryError };\n}\n\nexport interface ArtboardKernelOptions {\n artboardConfigs: ArtboardConfig[] | undefined;\n width: number;\n height: number;\n pieceGuides: PieceGuidesConfig | undefined;\n controlledActiveArtboard: string | undefined;\n onArtboardChange: ((name: string) => void) | undefined;\n /**\n * Rebuild the editor document from scratch on mount (clear artboards +\n * elements first). Used by `Editor.Canvas`: with the P1 inversion the\n * editor document lives at `Editor.Root` and OUTLIVES Canvas mounts, so a\n * remount must reflect the live seed instead of accreting onto the previous\n * mount's artboards/elements (the D8 soak cycles exactly this).\n * @internal\n */\n resetDocumentOnMount?: boolean;\n}\n\n/**\n * Artboard init + controlled `activeArtboard` sync + `onArtboardChange`.\n */\nexport function useArtboardKernel(opts: ArtboardKernelOptions): void {\n const {\n artboardConfigs,\n width,\n height,\n pieceGuides,\n controlledActiveArtboard,\n onArtboardChange,\n resetDocumentOnMount,\n } = opts;\n\n const {\n setElements,\n setSelectedId,\n setMultiSelection,\n artboardManager,\n refreshArtboards,\n } = useEditor();\n const { createArtboard, selectArtboard, artboards } = useArtboards();\n\n // Track if initial setup is complete\n const initializedRef = useRef(false);\n\n // Track if initial artboard sync is complete (to suppress premature onArtboardChange calls)\n // During initialization, createArtboard sets each new artboard as active, so the last one\n // ends up active. We only want to fire onArtboardChange after the controlled prop is synced.\n const hasInitialSyncRef = useRef(false);\n\n // === Initialize artboards ===\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n // Clear existing artboards and create new ones from config\n const configs = artboardConfigs || [{ name: 'Design', width, height }];\n\n // ADR-0054: piece-guide products want transparent artboards by default.\n // ArtboardElement constructor seeds `backgroundColor = '#ffffff'` and\n // every paint pass that consumes that field is *supposed* to be gated\n // on `!pieceGuides`, but the safety net here means a missed gate\n // can't leak a white frame past the silhouettes once the spread\n // clip lands in CanvasRenderer. Authors who explicitly set a\n // backgroundColor still win.\n const defaultBg = pieceGuides ? 'transparent' : undefined;\n\n // An explicitly-configured background is part of the design: it must\n // survive into exports and server renders (realtime mockups), not just\n // the editor preview. The constructor's '#ffffff' default stays\n // editor-only (exportBackground false) so apparel placements keep\n // exporting transparent.\n const resolveExportBackground = (c: ArtboardConfig) =>\n c.exportBackground ??\n (!!c.backgroundColor && c.backgroundColor !== 'transparent');\n\n if (resetDocumentOnMount) {\n // Rebuild from the seed: clear the (Root-owned, surviving) document and\n // create every configured artboard fresh. Selection may point at\n // elements that no longer exist — clear it too.\n artboardManager.clear();\n setElements([]);\n setSelectedId(null);\n setMultiSelection([]);\n for (const c of configs) {\n artboardManager.createArtboard({\n name: c.name,\n width: c.width,\n height: c.height,\n backgroundColor: c.backgroundColor ?? defaultBg,\n exportBackground: resolveExportBackground(c),\n clipShape: c.clipShape,\n role: c.role,\n });\n }\n const all = artboardManager.getAllArtboards();\n if (all.length > 0 && !controlledActiveArtboard) {\n artboardManager.setActiveArtboard(all[0].id);\n }\n refreshArtboards();\n return;\n }\n\n // Update the first (default) artboard instead of creating a new one\n const existingArtboards = artboardManager.getAllArtboards();\n if (existingArtboards.length > 0) {\n const firstConfig = configs[0];\n const firstArtboard = existingArtboards[0];\n\n // Update the first artboard\n firstArtboard.name = firstConfig.name;\n firstArtboard.width = firstConfig.width;\n firstArtboard.height = firstConfig.height;\n firstArtboard.clipShape = firstConfig.clipShape;\n firstArtboard.exportBackground = resolveExportBackground(firstConfig);\n firstArtboard.role = firstConfig.role;\n if (firstConfig.backgroundColor) {\n firstArtboard.backgroundColor = firstConfig.backgroundColor;\n } else if (defaultBg) {\n firstArtboard.backgroundColor = defaultBg;\n }\n\n // Create additional artboards\n for (let i = 1; i < configs.length; i++) {\n createArtboard(configs[i].width, configs[i].height, {\n name: configs[i].name,\n backgroundColor: configs[i].backgroundColor ?? defaultBg,\n exportBackground: resolveExportBackground(configs[i]),\n clipShape: configs[i].clipShape,\n role: configs[i].role,\n });\n }\n\n // createArtboard activates each artboard it creates, so after the loop\n // the LAST one is active — while initialElements (and legacy untagged\n // saved elements) land on the FIRST. An uncontrolled multi-artboard\n // editor used to mount showing a blank last artboard (DX F5). Default\n // the active artboard to the first config; a controlled `activeArtboard`\n // prop still wins via the sync effect below.\n if (!controlledActiveArtboard && configs.length > 1) {\n artboardManager.setActiveArtboard(firstArtboard.id);\n }\n }\n\n refreshArtboards();\n }, []);\n\n // === Sync controlled activeArtboard prop ===\n useEffect(() => {\n if (!controlledActiveArtboard) {\n // No controlled prop, but still mark as synced so onArtboardChange can fire\n hasInitialSyncRef.current = true;\n return;\n }\n\n const targetArtboard = artboards.find((ab) => ab.name === controlledActiveArtboard);\n if (targetArtboard) {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (activeArtboard?.name !== controlledActiveArtboard) {\n selectArtboard(targetArtboard.id);\n }\n // Mark as synced - now onArtboardChange can safely fire\n hasInitialSyncRef.current = true;\n }\n }, [controlledActiveArtboard, artboards]);\n\n // === Handle artboard change callback ===\n useEffect(() => {\n if (!onArtboardChange) return;\n\n // Skip until initial sync is complete - during initialization, createArtboard sets\n // each new artboard as active, so the last one ends up active. We wait for the\n // controlled activeArtboard prop to sync before firing this callback.\n if (!hasInitialSyncRef.current) return;\n\n const activeArtboard = artboardManager.getActiveArtboard();\n if (activeArtboard) {\n onArtboardChange(activeArtboard.name);\n }\n }, [artboardManager.getActiveArtboardId()]);\n}\n\nexport interface InitialElementsKernelOptions {\n initialElements: AnyElementConfig[] | undefined;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Restore saved state (`initialElements`) onto the named artboards, then\n * best-effort load the referenced fonts and force one repaint.\n */\nexport function useInitialElementsKernel(opts: InitialElementsKernelOptions): {\n initialElementsLoadedState: boolean;\n} {\n const { initialElements, emitError } = opts;\n const { setElements, artboardManager } = useEditor();\n\n const initialElementsLoadedRef = useRef(false);\n const [initialElementsLoadedState, setInitialElementsLoadedState] = useState(false);\n\n // The font-preload chain below resolves asynchronously (link load events\n // with a 4s safety setTimeout). If the component unmounts first — e.g. a\n // unit test finishing — the trailing setElements would dispatch into a\n // torn-down environment (\"window is not defined\" unhandled rejections in\n // vitest). Guard the late callbacks on unmount instead.\n const aliveRef = useRef(true);\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n // === Load initial elements (restore from saved state) ===\n useEffect(() => {\n if (!initialElements || initialElements.length === 0) return;\n if (initialElementsLoadedRef.current) return;\n\n initialElementsLoadedRef.current = true;\n setInitialElementsLoadedState(true);\n\n try {\n const deserialized = ElementFactory.createManyFromJSON(initialElements);\n\n // Restore each element onto its named artboard (the `artboard` tag that\n // onChange stamps on saved elements), so a multi-artboard design\n // round-trips. Elements without a tag — or with a name that no longer\n // exists — keep the legacy behavior: the first artboard.\n // (createManyFromJSON maps configs 1:1, so indexes align.)\n const allArtboards = artboardManager.getAllArtboards();\n if (allArtboards.length > 0) {\n const byName = new Map(allArtboards.map((ab) => [ab.name, ab]));\n deserialized.forEach((el, i) => {\n const targetName = initialElements[i]?.artboard;\n const target = (targetName && byName.get(targetName)) || allArtboards[0];\n artboardManager.addElementToArtboard(el.id, target.id);\n });\n }\n\n setElements(deserialized as Parameters<typeof setElements>[0]);\n\n // Custom fonts referenced by deserialised text elements need\n // an `@font-face` rule before the browser can paint them. The\n // canvas package itself doesn't inject those rules at startup —\n // only the font browser does, on user interaction. So on\n // refresh, the saved `fontFamily` falls through to a system\n // font; selecting the element opened the font browser, which\n // injected the CSS link, and the eventual re-render then\n // showed the correct face.\n //\n // Fix: for every unique non-system family in the restored\n // elements, inject a Google Fonts CSS link (best-effort —\n // matches `UnifiedFontService.loadGoogleFont`'s URL shape).\n // Once each link's stylesheet loads (which registers the\n // `@font-face` with the document), `document.fonts.load(...)`\n // pulls the actual font file, and a no-op `setElements`\n // identity update forces one more paint. Non-Google families\n // fall back gracefully — link load fails, the render keeps\n // the system fallback.\n const familySet = new Set<string>();\n for (const el of deserialized) {\n const family = (el as { fontFamily?: unknown }).fontFamily;\n if (typeof family === 'string' && family.trim().length > 0) {\n familySet.add(family);\n }\n }\n if (familySet.size > 0 && typeof document !== 'undefined') {\n const fontsApi =\n (document as Document & { fonts?: FontFaceSet }).fonts ?? null;\n const linkPromises = Array.from(familySet).map((family) => {\n const linkId = `font-${family.replace(/\\s+/g, '-').toLowerCase()}`;\n let link = document.getElementById(linkId) as HTMLLinkElement | null;\n if (!link) {\n link = document.createElement('link');\n link.id = linkId;\n link.rel = 'stylesheet';\n link.href = `https://fonts.googleapis.com/css2?family=${family.replace(\n /\\s+/g,\n '+',\n )}:wght@400;700&display=swap`;\n document.head.appendChild(link);\n }\n return new Promise<string>((resolve) => {\n // Already loaded? Resolve immediately.\n if ((link as HTMLLinkElement).sheet) {\n resolve(family);\n return;\n }\n const finish = () => resolve(family);\n link!.addEventListener('load', finish, { once: true });\n link!.addEventListener('error', finish, { once: true });\n // Safety timeout — if neither event fires, don't hang.\n setTimeout(finish, 4000);\n });\n });\n Promise.all(linkPromises)\n .then((families) => {\n if (!aliveRef.current || !fontsApi) return undefined;\n return Promise.all(\n families.flatMap((family) => [\n fontsApi.load(`400 16px \"${family}\"`).catch(() => undefined),\n fontsApi.load(`700 16px \"${family}\"`).catch(() => undefined),\n ]),\n );\n })\n .then(() => {\n // Bail if the component unmounted while fonts were loading —\n // dispatching state into a torn-down test environment throws\n // \"window is not defined\" as an unhandled rejection.\n if (!aliveRef.current) return;\n // New array reference forces dependents (render loop's\n // memoised `elementsByArtboard` keys off `elements`) to\n // re-evaluate, so the canvas re-paints with the now-\n // available font faces.\n setElements((prev) => prev.slice());\n });\n }\n } catch (err) {\n log.error('Failed to load initial elements:', err);\n const importErr = err instanceof Error ? err : new Error(String(err));\n emitError({\n category: 'import',\n message: `Failed to load initial elements: ${importErr.message}`,\n originalError: importErr,\n recoverable: true,\n });\n }\n }, [initialElements, artboardManager, setElements, emitError]);\n\n return { initialElementsLoadedState };\n}\n\nexport interface InitialImageKernelOptions {\n initialImage: string | undefined;\n initialImageAlignment: AlignmentPoint;\n initialImageScale: number;\n initialImageScaleMode: 'cover' | 'contain';\n artboardConfigs: ArtboardConfig[] | undefined;\n onImageLoad: ((src: string) => void) | undefined;\n onImageError: ((src: string, error: Error) => void) | undefined;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Load `initialImage` into every artboard with cover/contain placement.\n */\nexport function useInitialImageKernel(opts: InitialImageKernelOptions): {\n imageLoadingState: ImageLoadingState;\n imageError: Error | null;\n} {\n const {\n initialImage,\n initialImageAlignment,\n initialImageScale,\n initialImageScaleMode,\n artboardConfigs,\n onImageLoad,\n onImageError,\n emitError,\n } = opts;\n\n const { elements, setElements, artboardManager } = useEditor();\n\n // Image loading state\n const [imageLoadingState, setImageLoadingState] = useState<ImageLoadingState>('idle');\n const [imageError, setImageError] = useState<Error | null>(null);\n const prevImageRef = useRef<string | undefined>(undefined);\n\n // === Load initial image into ALL artboards ===\n useEffect(() => {\n if (!initialImage || prevImageRef.current === initialImage) return;\n\n // Skip if elements already exist (viewport switch scenario with externalProvider)\n // When using externalProvider, elements persist across component remounts,\n // so we shouldn't re-add the initial image on each mount\n if (elements.length > 0) {\n // Update ref to prevent future runs, but don't add duplicate images\n prevImageRef.current = initialImage;\n return;\n }\n\n prevImageRef.current = initialImage;\n\n const loadImage = async () => {\n setImageLoadingState('loading');\n setImageError(null);\n\n try {\n // Get ALL artboards to load the image into each one\n const allArtboards = artboardManager.getAllArtboards();\n if (allArtboards.length === 0) {\n throw new Error('No artboards available');\n }\n\n // Load image into EACH artboard\n const newElements: typeof elements = [];\n\n for (const artboard of allArtboards) {\n // Find the matching artboard config for per-artboard scaleMode\n const artboardConfig = artboardConfigs?.find(c => c.name === artboard.name);\n const effectiveScaleMode = artboardConfig?.scaleMode || initialImageScaleMode;\n\n // Use the placement function that calculates cover/contain sizing\n const result = await createImageElementWithPlacement(\n initialImage,\n {\n artboard: {\n width: artboard.width,\n height: artboard.height,\n x: artboard.x,\n y: artboard.y,\n },\n alignment: artboardConfig?.fitAlign || initialImageAlignment,\n scale: initialImageScale,\n scaleMode: effectiveScaleMode,\n marginTop: artboardConfig?.fitMarginTop,\n marginRight: artboardConfig?.fitMarginRight,\n marginBottom: artboardConfig?.fitMarginBottom,\n marginLeft: artboardConfig?.fitMarginLeft,\n }\n );\n\n if (result.success && result.element) {\n // Add element to this artboard\n artboardManager.addElementToArtboard(result.element.id, artboard.id);\n newElements.push(result.element);\n } else {\n log.error('Failed to load image for artboard:', artboard.name, result.error);\n }\n }\n\n if (newElements.length > 0) {\n setElements((prev) => [...prev, ...newElements]);\n setImageLoadingState('success');\n onImageLoad?.(initialImage);\n } else {\n const loadErr = new Error('Failed to load image into any artboard');\n setImageLoadingState('error');\n setImageError(loadErr);\n onImageError?.(initialImage, loadErr);\n emitError({\n category: 'image',\n message: loadErr.message,\n originalError: loadErr,\n recoverable: true,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n setImageLoadingState('error');\n setImageError(err);\n onImageError?.(initialImage, err);\n emitError({\n category: 'image',\n message: err.message,\n originalError: err,\n recoverable: true,\n });\n }\n };\n\n loadImage();\n }, [initialImage, initialImageAlignment, initialImageScale, initialImageScaleMode]);\n\n return { imageLoadingState, imageError };\n}\n\nexport interface ChangeCallbacksKernelOptions {\n onChange: ((state: CanvasState) => void) | undefined;\n onSelectionChange: ((elementId: string | null) => void) | undefined;\n}\n\n/**\n * `onChange` / `onSelectionChange` emission. Both fire through latest-refs so\n * an inline callback (a fresh closure identity on every host render) can't\n * re-trigger the effects — the component must not require consumers to\n * memoize their callbacks. Depending on the callback identity turns \"callback\n * updates host state\" into an infinite render loop; depending only on the\n * DATA fires exactly when the canvas actually changes. Pinned by\n * SnowconeCanvas.onChange-loop.test.tsx.\n */\nexport function useChangeCallbacksKernel(opts: ChangeCallbacksKernelOptions): void {\n const { onChange, onSelectionChange } = opts;\n const { elements, selectedId, artboardManager } = useEditor();\n const { artboards } = useArtboards();\n\n // === Handle selection change callback ===\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedId);\n }, [selectedId]);\n\n // === Handle onChange callback ===\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n useEffect(() => {\n const cb = onChangeRef.current;\n if (!cb) return;\n\n const activeArtboard = artboardManager.getActiveArtboard();\n const state: CanvasState = {\n // Stamp each element with the NAME of the artboard it lives on (the\n // manager is the membership source of truth). Without this the public\n // state is flat and a multi-placement design cannot round-trip: restore\n // collapses everything onto the first artboard, and per-placement render\n // states can't be derived from onChange. Pinned by\n // SnowconeCanvas.artboard-membership.test.tsx.\n elements: elements.map((el) => {\n const json = el.toJSON() as AnyElementConfig;\n const ab = artboardManager.getArtboardForElement(el.id);\n return ab ? { ...json, artboard: ab.name } : json;\n }),\n artboards: artboards.map((ab) => ({\n name: ab.name,\n width: ab.width,\n height: ab.height,\n clipShape: ab.clipShape,\n backgroundColor: ab.backgroundColor,\n // Carried so serializeStateForServer / saved states keep an\n // explicitly-set background renderable (mockup matches editor).\n exportBackground: ab.exportBackground,\n // Pacing role for multi-page templates (ADR-0086) — emitted only when\n // set so non-template states stay byte-identical.\n ...(ab.role ? { role: ab.role } : {}),\n })),\n activeArtboard: activeArtboard?.name || 'Design',\n };\n\n cb(state);\n }, [elements, artboards]);\n}\n\nexport interface ExportKernelOptions {\n autoExportInterval: number | undefined;\n autoExportConfig: { enabled?: boolean; debounceMs?: number; maxWaitMs?: number } | undefined;\n autoExportFormat: 'dataUrl' | 'blob';\n autoExportAll: boolean;\n exportScale: number;\n maxExportSize: number;\n exportImageFormat: 'png' | 'jpg' | 'jpeg' | 'webp';\n exportImageQuality: number;\n onExport: ((exports: Record<string, string | Blob>) => void) | undefined;\n onExportScheduled: (() => void) | undefined;\n onExportStatus: ((event: ExportStatusEvent) => void) | undefined;\n onExportReady: ((exportFn: () => Promise<Record<string, string | Blob>>) => void) | undefined;\n onReady: (() => void) | undefined;\n hasInitialElements: boolean;\n initialElementsLoaded: boolean;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Export kernel: the imperative `exportArtboards` handle, `onExportReady`,\n * smart auto-export (debounced, change-detected), content readiness, the\n * `onReady` signal, and the canvas-ready gate.\n */\nexport function useExportKernel(\n opts: ExportKernelOptions,\n ref: React.ForwardedRef<SnowconeCanvasHandle>,\n): void {\n const {\n autoExportInterval,\n autoExportConfig,\n autoExportFormat,\n autoExportAll,\n exportScale,\n maxExportSize,\n exportImageFormat,\n exportImageQuality,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onReady,\n hasInitialElements,\n initialElementsLoaded,\n emitError,\n } = opts;\n\n const {\n elements,\n artboardManager,\n historyManager,\n isCanvasReady,\n setCanvasReady,\n } = useEditor();\n const { artboards } = useArtboards();\n const { exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs } = useExport();\n\n // Export tracking\n const lastExportRef = useRef<number>(0);\n const exportCounterRef = useRef<number>(0);\n\n // Expose imperative handle for programmatic export (ref-based API)\n useImperativeHandle(ref, () => ({\n exportArtboards: async (options = {}) => {\n const format = options.format || autoExportFormat;\n const requestedScale = options.scale || exportScale;\n const all = options.all ?? false;\n\n // Calculate effective scale (capped to maxExportSize)\n let effectiveScale = requestedScale;\n if (maxExportSize > 0 && isFinite(maxExportSize)) {\n for (const ab of artboards) {\n const abEffectiveScale = calculateEffectiveScale(ab.width, ab.height, requestedScale, maxExportSize);\n effectiveScale = Math.min(effectiveScale, abEffectiveScale);\n }\n }\n\n const exportOptions = { scale: effectiveScale, format: exportImageFormat as 'png' | 'jpg' | 'jpeg' | 'webp', quality: exportImageQuality };\n\n if (all) {\n // Export all artboards (results keyed by artboard ID)\n const resultsById = format === 'blob'\n ? await exportAllArtboardsAsBlobs(exportOptions)\n : await exportAllArtboards(exportOptions);\n\n // Transform keys from artboard ID to artboard name\n const resultsByName: Record<string, string | Blob> = {};\n for (const [artboardId, result] of Object.entries(resultsById)) {\n const artboard = artboards.find(ab => ab.id === artboardId);\n if (artboard) {\n resultsByName[artboard.name] = result;\n }\n }\n return resultsByName;\n } else {\n // Export active artboard only\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) {\n throw new Error('[SnowconeCanvas] No active artboard found');\n }\n\n if (format === 'blob') {\n const blob = await exportArtboardAsBlob(activeArtboard.id, exportOptions);\n return { [activeArtboard.name]: blob };\n } else {\n const dataUrl = await exportArtboard(activeArtboard.id, exportOptions);\n return { [activeArtboard.name]: dataUrl };\n }\n }\n },\n }), [exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs, autoExportFormat, exportScale, maxExportSize, exportImageFormat, exportImageQuality, artboardManager, artboards]);\n\n // Provide export function via callback (modern React pattern, works with dynamic imports)\n useEffect(() => {\n if (onExportReady) {\n const exportFn = async () => {\n // Get the active artboard\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) {\n throw new Error('[SnowconeCanvas] No active artboard found');\n }\n\n // Calculate effective scale (capped to maxExportSize)\n const effectiveScale = calculateEffectiveScale(\n activeArtboard.width,\n activeArtboard.height,\n exportScale,\n maxExportSize\n );\n\n // Export as single artboard (returns string or blob)\n const result = autoExportFormat === 'blob'\n ? await exportArtboardAsBlob(activeArtboard.id, { scale: effectiveScale })\n : await exportArtboard(activeArtboard.id, { scale: effectiveScale });\n\n // Return as Record keyed by artboard name (to match expected interface)\n return { [activeArtboard.name]: result };\n };\n onExportReady(exportFn);\n }\n }, [onExportReady, exportArtboard, exportArtboardAsBlob, artboardManager, autoExportFormat, exportScale, maxExportSize]);\n\n // === Auto export ===\n const triggerExport = useCallback(async () => {\n if (!onExport && !onExportStatus) return;\n\n const exportNumber = ++exportCounterRef.current;\n const startTime = Date.now();\n\n // Calculate effective scale for each artboard (capped to maxExportSize)\n // Use the most restrictive scale to ensure all artboards fit within maxExportSize\n let effectiveScale = exportScale;\n if (maxExportSize > 0 && isFinite(maxExportSize)) {\n for (const ab of artboards) {\n const abEffectiveScale = calculateEffectiveScale(ab.width, ab.height, exportScale, maxExportSize);\n effectiveScale = Math.min(effectiveScale, abEffectiveScale);\n }\n }\n\n // Determine the artboard ID for status events\n const activeAb = artboardManager.getActiveArtboard();\n const statusArtboardId = activeAb?.id ?? 'unknown';\n\n // Fire 'rendering' status event\n onExportStatus?.({ status: 'rendering', artboardId: statusArtboardId });\n\n lastExportRef.current = startTime;\n\n try {\n let results: Record<string, string | Blob>;\n\n const exportOptions = {\n format: exportImageFormat as 'png' | 'jpg' | 'jpeg' | 'webp',\n scale: effectiveScale,\n quality: exportImageQuality,\n };\n\n if (autoExportFormat === 'blob') {\n // Export as Blob (more efficient for API uploads)\n if (autoExportAll) {\n const allResults = await exportAllArtboardsAsBlobs(exportOptions);\n results = {};\n for (const [artboardId, data] of Object.entries(allResults)) {\n const artboard = artboards.find((ab) => ab.id === artboardId);\n if (artboard) {\n results[artboard.name] = data;\n }\n }\n } else {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) return;\n const blob = await exportArtboardAsBlob(activeArtboard.id, exportOptions);\n results = { [activeArtboard.name]: blob };\n }\n } else {\n // Export as dataUrl (default)\n if (autoExportAll) {\n const allResults = await exportAllArtboards(exportOptions);\n results = {};\n for (const [artboardId, data] of Object.entries(allResults)) {\n const artboard = artboards.find((ab) => ab.id === artboardId);\n if (artboard) {\n results[artboard.name] = data;\n }\n }\n } else {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) return;\n const dataUrl = await exportArtboard(activeArtboard.id, exportOptions);\n results = { [activeArtboard.name]: dataUrl };\n }\n }\n\n // Fire legacy callback (backwards compat)\n onExport?.(results);\n\n // Fire unified status callback\n onExportStatus?.({ status: 'complete', artboardId: statusArtboardId, result: results });\n } catch (error) {\n // Silently skip if canvas ref not available (canvas still mounting)\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage.includes('Canvas ref not available')) {\n // Canvas not ready yet, will retry on next interval\n return;\n }\n const duration = Date.now() - startTime;\n log.error('Export failed (export #' + exportNumber + '):', {\n duration: `${duration}ms`,\n error: errorMessage,\n });\n\n // Fire unified error status callback\n const err = error instanceof Error ? error : new Error(errorMessage);\n onExportStatus?.({ status: 'error', artboardId: statusArtboardId, error: err });\n\n // Fire general onError callback\n emitError({\n category: 'export',\n message: err.message,\n originalError: err,\n artboardId: statusArtboardId,\n recoverable: true,\n });\n }\n }, [onExport, onExportStatus, autoExportAll, autoExportFormat, exportScale, maxExportSize, exportImageFormat, exportImageQuality, exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs, artboards, artboardManager, elements, autoExportConfig, emitError]);\n\n // === Deprecation warning for autoExportInterval ===\n useEffect(() => {\n if (autoExportInterval !== undefined && autoExportInterval > 0) {\n log.warn(\n 'autoExportInterval is deprecated and ignored. ' +\n 'Use autoExportConfig={{ enabled: true, debounceMs: 100, maxWaitMs: 1000 }} instead.'\n );\n }\n }, []);\n\n // === Combined scheduled callback (fires both legacy + unified) ===\n const handleExportScheduled = useCallback(() => {\n // Fire legacy callback (backwards compat)\n onExportScheduled?.();\n\n // Fire unified status callback with 'scheduled' event\n if (onExportStatus) {\n const activeAb = artboardManager.getActiveArtboard();\n onExportStatus({ status: 'scheduled', artboardId: activeAb?.id ?? 'unknown' });\n }\n }, [onExportScheduled, onExportStatus, artboardManager]);\n\n // === Content readiness (gates auto-export on full content readiness) ===\n const isContentReady = useContentReady({\n isCanvasReady,\n elements,\n hasInitialElements,\n initialElementsLoaded,\n });\n\n // === Fire onReady callback once content is ready ===\n const hasCalledOnReadyRef = useRef(false);\n useEffect(() => {\n if (isContentReady && !hasCalledOnReadyRef.current) {\n hasCalledOnReadyRef.current = true;\n onReady?.();\n }\n }, [isContentReady, onReady]);\n\n // === Smart auto-export with debouncing and change detection ===\n useAutoExport({\n config: autoExportConfig ? {\n enabled: autoExportConfig.enabled ?? true,\n debounceMs: autoExportConfig.debounceMs ?? 100,\n maxWaitMs: autoExportConfig.maxWaitMs ?? 1000,\n } : {\n enabled: false, // Only use smart export if autoExportConfig is provided\n },\n historyManager,\n artboards: artboards,\n elements,\n onExport: triggerExport,\n onExportScheduled: handleExportScheduled,\n isCanvasReady: isContentReady, // Gate exports until content is fully ready (fonts loaded, elements deserialized)\n });\n\n // === Signal canvas ready when artboards are initialized ===\n // This gates auto-export to prevent \"[useExport] Canvas ref not available\" errors\n useEffect(() => {\n if (artboards.length > 0 && !isCanvasReady) {\n // Small delay to ensure canvas DOM is mounted after artboards are ready\n const timeoutId = requestAnimationFrame(() => {\n setCanvasReady(true);\n });\n return () => cancelAnimationFrame(timeoutId);\n }\n return undefined;\n }, [artboards.length, isCanvasReady, setCanvasReady]);\n\n // === Cleanup canvas ready on unmount ===\n useEffect(() => {\n return () => {\n setCanvasReady(false);\n };\n }, [setCanvasReady]);\n}\n","/**\n * SnowconeCanvas - Main embeddable canvas component for e-commerce integration\n *\n * A self-contained canvas editor that wraps EditorProvider and handles:\n * - Artboard initialization and synchronization\n * - Image loading from URL with loading/error states\n * - Auto-export on interval or artboard change\n * - State change callbacks\n *\n * @example\n * ```tsx\n * import { SnowconeCanvas } from '@snowcone-app/canvas';\n *\n * function ProductCustomizer() {\n * return (\n * <SnowconeCanvas\n * artboards={[\n * { name: 'Front', width: 1200, height: 1200, clipShape: 'circle' },\n * { name: 'Back', width: 1200, height: 1200 },\n * ]}\n * initialImage=\"https://example.com/product.png\"\n * autoExportInterval={500}\n * onExport={(exports) => {\n * // exports = { 'Front': dataUrl, 'Back': dataUrl }\n * console.log('Design updated:', exports);\n * }}\n * />\n * );\n * }\n * ```\n */\n\nimport React, { useEffect, useMemo, forwardRef } from 'react';\nimport { KitProvider } from '../../contexts/KitContext.js';\nimport { type CanvasServices } from '../../contexts/CanvasServicesContext.js';\nimport { Canvas } from './Canvas.js';\nimport { KitLayout } from './KitLayout.js';\nimport { ErrorBoundary } from './ErrorBoundary.js';\nimport { EmbedRoot } from './EmbedRoot.js';\nimport {\n useArtboardKernel,\n useCanvasErrorChannel,\n useChangeCallbacksKernel,\n useExportKernel,\n useInitialElementsKernel,\n useInitialImageKernel,\n} from './kernel.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { CanvasError } from '../../types/index.js';\n\nconst log = createLogger('SnowconeCanvas');\nimport { Spinner } from './LoadingStates.js';\nimport { resolveKit } from '../../kits/registry.js';\nimport { validateKit } from '../../kits/validation.js';\nimport type { KitPresetId, KitDefinition } from '../../kits/types.js';\nimport type { ArtboardRole, ClipShape, AnyElementConfig } from '../../types/index.js';\nimport type {\n PieceGuide,\n PieceGuideRendererPiece,\n PieceGuideRendererStyle,\n PieceGuideVisibility,\n} from '../../rendering/PieceGuideRenderer.js';\n\n// Re-export ClipShape type for convenience\nexport type { ClipShape, ArtboardRole } from '../../types/index.js';\n\n/** 9-point alignment grid values */\nexport type AlignmentPoint = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br' | 'center' | 'top' | 'bottom' | 'left' | 'right';\n\n/** Events emitted during the export lifecycle */\nexport type ExportStatusEvent =\n | { status: 'scheduled'; artboardId: string }\n | { status: 'rendering'; artboardId: string }\n | { status: 'complete'; artboardId: string; result: Record<string, string | Blob> }\n | { status: 'error'; artboardId: string; error: Error };\n\nexport interface ArtboardConfig {\n /** Display name for the artboard (used as key in export results) */\n name: string;\n /** Width in pixels */\n width: number;\n /** Height in pixels */\n height: number;\n /** Optional clip shape for content masking */\n clipShape?: ClipShape;\n /** Optional background color */\n backgroundColor?: string;\n /**\n * Whether the background color is part of the design — painted in exports\n * and server renders (realtime mockups), not just in the editor. Defaults to\n * `true` when `backgroundColor` is explicitly set (and not 'transparent'),\n * `false` otherwise: an author who sets a background expects the mockup to\n * match the editor.\n */\n exportBackground?: boolean;\n /**\n * Pacing role in a multi-page template (ADR-0086 albums):\n * `'cover' | 'front-matter' | 'repeating' | 'back-matter'`. Template\n * metadata only — auto-fill clones `repeating` spreads as the page count\n * grows. Round-trips through saved states and `onChange`; never sent on\n * the server render wire (`serializeStateForServer` strips it).\n */\n role?: ArtboardRole;\n /** Scale mode for initial image: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: 'cover' | 'contain';\n /** Top margin in pixels for contain mode (reduces available area) */\n fitMarginTop?: number;\n /** Right margin in pixels for contain mode (reduces available area) */\n fitMarginRight?: number;\n /** Bottom margin in pixels for contain mode (reduces available area) */\n fitMarginBottom?: number;\n /** Left margin in pixels for contain mode (reduces available area) */\n fitMarginLeft?: number;\n /** Alignment override for contain mode (9-point grid: 'tl','t','tr','l','c','r','bl','b','br') */\n fitAlign?: AlignmentPoint;\n}\n\nexport interface CanvasState {\n /**\n * Serialized element configurations for persistence. Each element carries an\n * `artboard` field — the NAME of the artboard it lives on — so a\n * multi-artboard design round-trips through save→`initialElements` and\n * `serializeStateForServer` can emit every artboard with its own elements.\n */\n elements: AnyElementConfig[];\n /** Artboard configurations */\n artboards: ArtboardConfig[];\n /** Currently active artboard name */\n activeArtboard: string;\n}\n\n/**\n * Imperative handle for programmatic control of SnowconeCanvas\n */\nexport interface SnowconeCanvasHandle {\n /**\n * Trigger an export of artboards programmatically.\n * Returns a promise that resolves with the exported artboards.\n *\n * @param options Export options\n * @returns Promise resolving to exported artboards keyed by name\n *\n * @example\n * const canvasRef = useRef<SnowconeCanvasHandle>(null);\n * const handleExport = async () => {\n * const exports = await canvasRef.current?.exportArtboards();\n * console.log('Exported:', exports);\n * };\n */\n exportArtboards: (options?: {\n /** Export format */\n format?: 'dataUrl' | 'blob';\n /** Resolution multiplier */\n scale?: number;\n /** Export all artboards or just active */\n all?: boolean;\n }) => Promise<Record<string, string | Blob>>;\n}\n\n/**\n * Configuration for export behavior (scale, format, auto-export).\n *\n * Group these props together instead of passing them individually:\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * exportConfig={{\n * autoExportConfig: { enabled: true, debounceMs: 100, maxWaitMs: 1000 },\n * format: 'blob',\n * exportAll: true,\n * scale: 2,\n * maxSize: 4000,\n * imageFormat: 'webp',\n * imageQuality: 0.85,\n * }}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n */\nexport interface ExportConfig {\n /**\n * Smart auto-export configuration with debouncing and change detection.\n * Replaces the timer-based `autoExportInterval` with an event-driven approach.\n */\n autoExportConfig?: {\n /** Enable/disable auto-export */\n enabled?: boolean;\n /** Debounce delay - wait this long after last change before exporting (ms) */\n debounceMs?: number;\n /** Maximum wait time - force export after this duration even if changes keep coming (ms) */\n maxWaitMs?: number;\n };\n\n /**\n * Export format: 'dataUrl' (base64 string) or 'blob' (binary).\n * Blob is more efficient for API uploads.\n */\n format?: 'dataUrl' | 'blob';\n\n /**\n * If true, exports all artboards on each interval.\n * If false, only exports the active artboard.\n */\n exportAll?: boolean;\n\n /**\n * Resolution multiplier for exports.\n * Default: 2 (recommended for print quality).\n *\n * Note: This scale is applied intelligently - if the resulting image would\n * exceed `maxSize`, the scale is reduced to cap the largest dimension.\n */\n scale?: number;\n\n /**\n * Maximum export dimension (largest side) in pixels.\n * Default: 4000 (4K).\n *\n * When the artboard dimensions multiplied by scale would exceed this,\n * the effective scale is reduced to cap the output at this size.\n * Set to 0 or Infinity to disable this capping behavior.\n */\n maxSize?: number;\n\n /**\n * Image format for auto-exports.\n * Default: 'png'.\n * Use 'webp' for smaller file sizes with transparency support (good for realtime preview).\n */\n imageFormat?: 'png' | 'jpg' | 'jpeg' | 'webp';\n\n /**\n * Image quality for auto-exports (0-1).\n * Only applies to JPEG and WebP formats. PNG ignores this.\n * Default: 0.92.\n */\n imageQuality?: number;\n}\n\n/**\n * Configuration for the initial image loaded into the canvas.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * imageConfig={{\n * src: 'https://example.com/artwork.png',\n * alignment: 'center',\n * scale: 1.2,\n * scaleMode: 'contain',\n * }}\n * />\n * ```\n */\nexport interface ImageConfig {\n /**\n * URL of initial image to load into the canvas.\n * Shows loading state while fetching, error state on failure.\n */\n src?: string;\n\n /**\n * Alignment for the initial image on the artboard.\n * Uses a 9-point grid: 'tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br'\n * Or semantic values: 'center', 'top', 'bottom', 'left', 'right'\n * Default: 'center'\n */\n alignment?: AlignmentPoint;\n\n /**\n * Scale multiplier for the initial image.\n * Default: 1 (image covers artboard exactly)\n * Values > 1 make the image larger, < 1 make it smaller.\n */\n scale?: number;\n\n /**\n * Scale mode for initial image placement.\n * - \"cover\" (default): image fills artboard completely, may crop edges\n * - \"contain\": image fits entirely inside artboard, may have empty space\n */\n scaleMode?: 'cover' | 'contain';\n}\n\n/**\n * Configuration for layout and visual appearance of the canvas.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * layoutConfig={{\n * width: 1200,\n * height: 1200,\n * viewPadding: 0.85,\n * artboardBorderRadius: 12,\n * showToolbar: false,\n * showLayers: false,\n * }}\n * />\n * ```\n */\nexport interface LayoutConfig {\n /**\n * Artboard width (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n */\n width?: number;\n\n /**\n * Artboard height (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n */\n height?: number;\n\n /**\n * Controls how much of the container the artboard fills.\n * Value between 0 and 1, where 1 means the artboard fills 100% of the container.\n * Default is 0.9 (90%).\n */\n viewPadding?: number;\n\n /**\n * Border radius for artboard corners in pixels.\n * This is a visual-only effect - exports will still have the full artboard.\n * @default 0\n */\n artboardBorderRadius?: number;\n\n /**\n * Fixed screen-space margin in pixels.\n * When set, the artboard will have this exact left/top margin regardless of zoom level.\n * Overrides viewPadding for positioning.\n */\n fixedMargin?: number;\n\n /**\n * Maximum HTML height in pixels.\n * When the artboard (with vertical fixedMargin padding) would exceed this height,\n * horizontal padding is increased to shrink the artboard until it fits.\n */\n maxHeight?: number;\n\n /**\n * Show toolbar UI.\n * Default: false (canvas-only mode).\n */\n showToolbar?: boolean;\n\n /**\n * Show layers panel.\n * Default: false (canvas-only mode).\n */\n showLayers?: boolean;\n\n /**\n * Whether to show the rotation handle on the canvas.\n * Set to false for embedded mode where rotation is handled via toolbar panel.\n * @default true\n */\n showRotationHandle?: boolean;\n\n /**\n * When true, hides the internal Canvas component.\n * Use this when rendering a separate Canvas (e.g., in fullscreen mode)\n * that shares the same EditorProvider context.\n */\n hideCanvas?: boolean;\n\n /**\n * CSS class name for the canvas wrapper element.\n */\n canvasWrapperClassName?: string;\n\n /**\n * Inline styles for the canvas wrapper element.\n */\n canvasWrapperStyle?: React.CSSProperties;\n\n /**\n * ADR-0054 Phase 4: cutout shapes (in artboard-space px) that get\n * punched through the `<canvas>` element as truly transparent holes,\n * revealing whatever sits behind SnowconeCanvas in the DOM. Internally\n * translated to a CSS `clip-path: path(evenodd, ...)` that accounts\n * for the canvas's padding margin and zoom. Each shape is a rect,\n * rounded rect, or SVG path — the same `SvgPath` shape used by the\n * `VisualGuideOverlay`.\n */\n canvasCutouts?: CanvasCutoutShape[];\n}\n\n/**\n * ADR-0054: piece-guide config passed to `SnowconeCanvas`. The render\n * loop calls `renderPieceGuides` with these values after drawing the\n * artboard and its elements, so boundary/zone/label coordinates are in\n * the artboard's local pixel frame and compose cleanly with the canvas\n * viewport transform.\n */\nexport interface PieceGuidesConfig {\n /** Pieces (artboard-space rects) the guides attach to. */\n pieces: PieceGuideRendererPiece[];\n /** Authored guides keyed by piece id. Pieces without a guide fall back to the piece rect. */\n guides?: Record<string, PieceGuide>;\n /** Per-layer visibility toggles. All default to true. */\n show?: PieceGuideVisibility;\n /** Optional style overrides. */\n style?: PieceGuideRendererStyle;\n}\n\n/** ADR-0054 Phase 4: shape union used for `canvasCutouts`. Mirrors the\n * `SvgPath` shape in `VisualGuideOverlay`. Coordinates are in artboard\n * pixel units (the canvas's intrinsic/native size). */\nexport type CanvasCutoutShape =\n | { kind: \"rect\"; x: number; y: number; width: number; height: number }\n | {\n kind: \"roundedRect\";\n x: number;\n y: number;\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n }\n | { kind: \"path\"; d: string };\n\n/**\n * Props for the SnowconeCanvas component.\n *\n * Props can be passed individually (flat) or grouped using config objects.\n * When both are provided, individual props take precedence over config values.\n *\n * @example Grouped config pattern (recommended for new code)\n * ```tsx\n * <SnowconeCanvas\n * artboards={[{ name: 'Front', width: 1200, height: 1200 }]}\n * imageConfig={{\n * src: 'https://example.com/artwork.png',\n * alignment: 'center',\n * scaleMode: 'contain',\n * }}\n * exportConfig={{\n * autoExportConfig: { enabled: true, debounceMs: 100 },\n * format: 'blob',\n * scale: 2,\n * imageFormat: 'webp',\n * }}\n * layoutConfig={{\n * viewPadding: 0.85,\n * artboardBorderRadius: 12,\n * showRotationHandle: false,\n * }}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n *\n * @example Flat props (backwards compatible, still fully supported)\n * ```tsx\n * <SnowconeCanvas\n * artboards={[{ name: 'Front', width: 1200, height: 1200 }]}\n * initialImage=\"https://example.com/artwork.png\"\n * autoExportConfig={{ enabled: true, debounceMs: 100 }}\n * autoExportFormat=\"blob\"\n * exportScale={2}\n * viewPadding={0.85}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n */\nexport interface SnowconeCanvasProps {\n // === Config object props (grouped) ===\n\n /**\n * Grouped export configuration. See {@link ExportConfig}.\n * Individual export props (autoExportConfig, autoExportFormat, etc.) take precedence if also provided.\n */\n exportConfig?: ExportConfig;\n\n /**\n * Grouped initial image configuration. See {@link ImageConfig}.\n * Individual image props (initialImage, initialImageAlignment, etc.) take precedence if also provided.\n */\n imageConfig?: ImageConfig;\n\n /**\n * Grouped layout/appearance configuration. See {@link LayoutConfig}.\n * Individual layout props (width, height, viewPadding, etc.) take precedence if also provided.\n */\n layoutConfig?: LayoutConfig;\n\n // === Kit configuration ===\n\n /**\n * Editor kit configuration. Use a preset name ('compact-customizer', 'pro-studio', 'embed-only')\n * or a custom KitDefinition for full control over layout, capabilities, and behavior.\n * @default 'pro-studio'\n */\n kit?: KitPresetId | KitDefinition;\n\n // === Privileged services (Image Browser search / AI generation) ===\n\n /**\n * Privileged-service wiring for the editor chrome (the Image Browser's\n * stock-image search and AI generation tabs). Host these URLs on your\n * server; your vendor keys live there — the canvas never sees a key and\n * never calls a vendor directly.\n *\n * Tabs without a configured service do not render at all. With no\n * `services` prop, the Image Browser shows only the URL/upload tab.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * services={{\n * imageSearchUrl: '/api/image-search', // your same-origin proxy route\n * generateUrl: '/api/generate-image', // your same-origin proxy route\n * }}\n * />\n * ```\n */\n services?: CanvasServices;\n\n // === Artboards (placements) ===\n\n /**\n * Array of artboard configurations.\n * Each artboard represents a placement area (e.g., 'Front', 'Back').\n */\n artboards?: ArtboardConfig[];\n\n /**\n * Currently active artboard by name.\n * Used for controlled artboard selection.\n */\n activeArtboard?: string;\n\n /**\n * Callback when user switches artboards.\n * Receives the name of the newly active artboard.\n */\n onArtboardChange?: (name: string) => void;\n\n // === Single artboard shorthand ===\n\n /**\n * Artboard width (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n * @deprecated Use `layoutConfig.width` instead.\n */\n width?: number;\n\n /**\n * Artboard height (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n * @deprecated Use `layoutConfig.height` instead.\n */\n height?: number;\n\n // === Initial content ===\n\n /**\n * URL of initial image to load into the canvas.\n * Shows loading state while fetching, error state on failure.\n *\n * The image will be sized to \"cover\" the artboard (like CSS object-fit: cover)\n * and positioned according to `initialImageAlignment`.\n * @deprecated Use `imageConfig.src` instead.\n */\n initialImage?: string;\n\n /**\n * Alignment for the initial image on the artboard.\n * Uses a 9-point grid: 'tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br'\n * Or semantic values: 'center', 'top', 'bottom', 'left', 'right'\n *\n * Default: 'center' (places image centered on artboard)\n * @deprecated Use `imageConfig.alignment` instead.\n */\n initialImageAlignment?: AlignmentPoint;\n\n /**\n * Scale multiplier for the initial image.\n * Default: 1 (image covers artboard exactly)\n * Values > 1 make the image larger, < 1 make it smaller.\n * @deprecated Use `imageConfig.scale` instead.\n */\n initialImageScale?: number;\n\n /**\n * Scale mode for initial image placement.\n * - \"cover\" (default): image fills artboard completely, may crop edges\n * - \"contain\": image fits entirely inside artboard, may have empty space\n * @deprecated Use `imageConfig.scaleMode` instead.\n */\n initialImageScaleMode?: 'cover' | 'contain';\n\n /**\n * Serialized element configurations for restoring saved state.\n * Use this to restore a previously saved design.\n *\n * Elements carrying an `artboard` field (the artboard NAME, stamped by\n * `onChange`) are restored onto that artboard; elements without one land on\n * the first artboard, so pre-existing saved states keep working.\n */\n initialElements?: AnyElementConfig[];\n\n // === State callbacks ===\n\n /**\n * Called when canvas content changes (elements added, modified, or removed).\n * Use this to persist the design state.\n */\n onChange?: (state: CanvasState) => void;\n\n /**\n * Called when selection changes.\n * Receives null when nothing is selected.\n */\n onSelectionChange?: (elementId: string | null) => void;\n\n // === Auto export ===\n\n /**\n * Interval in milliseconds for automatic exports.\n * Set to 0 or undefined to disable auto-export.\n * Recommended: 500ms for responsive preview updates.\n *\n * @deprecated Use `autoExportConfig` or `exportConfig.autoExportConfig` instead, which provides smart debouncing and change detection rather than fixed-interval polling.\n */\n autoExportInterval?: number;\n\n /**\n * Smart auto-export configuration with debouncing and change detection.\n * Replaces the timer-based `autoExportInterval` with an event-driven approach.\n * @deprecated Use `exportConfig.autoExportConfig` instead.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * autoExportConfig={{\n * enabled: true,\n * debounceMs: 100, // Wait 100ms after last change\n * maxWaitMs: 1000, // Force export after 1s of continuous changes\n * }}\n * onExport={(exports) => console.log('Exported:', exports)}\n * />\n * ```\n */\n autoExportConfig?: {\n /** Enable/disable auto-export */\n enabled?: boolean;\n /** Debounce delay - wait this long after last change before exporting (ms) */\n debounceMs?: number;\n /** Maximum wait time - force export after this duration even if changes keep coming (ms) */\n maxWaitMs?: number;\n };\n\n /**\n * Export format: 'dataUrl' (base64 string) or 'blob' (binary).\n * Blob is more efficient for API uploads.\n * @deprecated Use `exportConfig.format` instead.\n */\n autoExportFormat?: 'dataUrl' | 'blob';\n\n /**\n * If true, exports all artboards on each interval.\n * If false, only exports the active artboard.\n * @deprecated Use `exportConfig.exportAll` instead.\n */\n autoExportAll?: boolean;\n\n /**\n * Resolution multiplier for exports.\n * Default: 2 (recommended for print quality).\n *\n * Note: This scale is applied intelligently - if the resulting image would\n * exceed `maxExportSize`, the scale is reduced to cap the largest dimension.\n * @deprecated Use `exportConfig.scale` instead.\n */\n exportScale?: number;\n\n /**\n * Maximum export dimension (largest side) in pixels.\n * Default: 4000 (4K).\n *\n * When the artboard dimensions multiplied by exportScale would exceed this,\n * the effective scale is reduced to cap the output at this size.\n *\n * For example:\n * - Artboard 3951x4800 with exportScale=2 would normally be 7902x9600\n * - With maxExportSize=4000, it stays at 3951x4800 (scale effectively 1)\n * - Artboard 1200x1200 with exportScale=2 becomes 2400x2400 (under limit)\n *\n * Set to 0 or Infinity to disable this capping behavior.\n * @deprecated Use `exportConfig.maxSize` instead.\n */\n maxExportSize?: number;\n\n /**\n * Image format for auto-exports.\n * Default: 'png'.\n * Use 'webp' for smaller file sizes with transparency support (good for realtime preview).\n * @deprecated Use `exportConfig.imageFormat` instead.\n */\n exportImageFormat?: 'png' | 'jpg' | 'jpeg' | 'webp';\n\n /**\n * Image quality for auto-exports (0-1).\n * Only applies to JPEG and WebP formats. PNG ignores this.\n * Default: 0.92.\n * @deprecated Use `exportConfig.imageQuality` instead.\n */\n exportImageQuality?: number;\n\n /**\n * Called with export results.\n * Results are keyed by artboard name.\n *\n * @deprecated Use `onExportStatus` for a unified export lifecycle callback, or `onExportReady` for on-demand export control.\n */\n onExport?: (exports: Record<string, string | Blob>) => void;\n\n /**\n * Called immediately when an export is scheduled (before debounce).\n * Use this to show loading indicators right away instead of waiting for export to complete.\n * This fires as soon as a change is detected, giving consumers early warning.\n *\n * @deprecated Use `onExportStatus` instead, which provides `{ status: 'scheduled' }` events along with the full export lifecycle.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onExportScheduled={() => {\n * // Show shimmer/loading immediately\n * setIsExporting(true);\n * }}\n * onExport={(exports) => {\n * // Hide shimmer after export completes\n * setIsExporting(false);\n * }}\n * />\n * ```\n */\n onExportScheduled?: () => void;\n\n /**\n * Unified export status callback. Prefer this over individual `onExport`/`onExportScheduled` callbacks.\n *\n * Receives events for each phase of the export lifecycle:\n * - `scheduled` - Export was requested (before debounce completes)\n * - `rendering` - Export rendering has started\n * - `complete` - Export finished successfully with results\n * - `error` - Export failed with an error\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onExportStatus={(event) => {\n * switch (event.status) {\n * case 'scheduled':\n * setIsExporting(true);\n * break;\n * case 'complete':\n * setIsExporting(false);\n * uploadResults(event.result);\n * break;\n * case 'error':\n * setIsExporting(false);\n * showError(event.error);\n * break;\n * }\n * }}\n * />\n * ```\n */\n onExportStatus?: (event: ExportStatusEvent) => void;\n\n /**\n * Callback that provides an export function.\n * Called once when the canvas is ready.\n * Use this for manual/on-demand exports triggered by UI buttons.\n *\n * @example\n * ```tsx\n * const [exportFn, setExportFn] = useState<(() => Promise<Record<string, string | Blob>>) | null>(null);\n *\n * <SnowconeCanvas\n * onExportReady={(fn) => setExportFn(() => fn)}\n * ...\n * />\n *\n * <button onClick={() => exportFn?.()}>Export</button>\n * ```\n */\n onExportReady?: (exportFn: () => Promise<Record<string, string | Blob>>) => void;\n\n // === Image loading callbacks ===\n\n /**\n * Called when initialImage loads successfully.\n */\n onImageLoad?: (src: string) => void;\n\n /**\n * Called when initialImage fails to load.\n */\n onImageError?: (src: string, error: Error) => void;\n\n // === Error handling ===\n\n /**\n * General error callback for recoverable and non-recoverable canvas errors.\n * Receives a structured CanvasError with category, message, and context.\n *\n * This fires for:\n * - Image loading failures (category: 'image')\n * - Export errors (category: 'export')\n * - Element import/deserialization errors (category: 'import')\n * - Unexpected React crashes caught by ErrorBoundary (category: 'unknown')\n *\n * Existing `onExportStatus` and `onImageError` callbacks continue to work.\n * `onError` provides a unified stream of all errors for logging/analytics.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onError={(error) => {\n * console.error(`[${error.category}] ${error.message}`);\n * if (!error.recoverable) {\n * analytics.track('canvas_crash', { category: error.category });\n * }\n * }}\n * />\n * ```\n */\n onError?: (error: CanvasError) => void;\n\n // === Appearance ===\n\n /**\n * Theme name (reserved for future use).\n */\n theme?: string;\n\n /**\n * When true, skips the internal ThemeProvider and inherits theme from parent.\n * Use this when embedding SnowconeCanvas in an app that already manages themes.\n * This prevents the canvas from overriding the parent app's theme on document.documentElement.\n *\n * @default false\n */\n inheritTheme?: boolean;\n\n /**\n * When true, assumes EditorProvider already exists in a parent component.\n * Skips creating the internal EditorProvider so multiple SnowconeCanvas instances\n * can share the same editor state (e.g., when conditionally rendering mobile/desktop layouts).\n *\n * @default false\n */\n externalProvider?: boolean;\n\n /**\n * Rebuild the editor document from scratch on mount: clear all artboards\n * and elements before applying `artboards`/`initialElements`. Used by\n * `Editor.Canvas` (P1 inversion): the editor document lives at\n * `Editor.Root` and OUTLIVES Canvas mounts, so a remount must reflect the\n * live seed instead of accreting onto the previous mount's document. The\n * legacy `externalProvider` persistence semantics (mobile/desktop layout\n * switch) are unchanged when this is false.\n *\n * @internal\n * @default false\n */\n resetDocumentOnMount?: boolean;\n\n /**\n * CSS class name for the container.\n */\n className?: string;\n\n /**\n * Inline styles for the container.\n */\n style?: React.CSSProperties;\n\n /**\n * CSS class name for the canvas wrapper element.\n * Use this to apply CSS masks, clips, or other effects to just the canvas\n * without affecting the overlay elements.\n * @deprecated Use `layoutConfig.canvasWrapperClassName` instead.\n */\n canvasWrapperClassName?: string;\n\n /**\n * Inline styles for the canvas wrapper element.\n * Use this to apply CSS masks, clips, or other effects to just the canvas\n * without affecting the overlay elements.\n * @deprecated Use `layoutConfig.canvasWrapperStyle` instead.\n */\n canvasWrapperStyle?: React.CSSProperties;\n\n /**\n * ADR-0054 Phase 4: shapes punched through the `<canvas>` as truly\n * transparent holes. See matching field on LayoutConfig for details.\n * Takes precedence over `layoutConfig.canvasCutouts`.\n */\n canvasCutouts?: CanvasCutoutShape[];\n\n /**\n * ADR-0054: piece boundary / safe-area / zone / label overlays drawn\n * directly into the canvas render loop (no DOM SVG overlay). Pieces\n * are in artboard-space coordinates (same frame as placements and\n * `canvasCutouts`). `guides` is keyed by piece id. Supply `undefined`\n * or omit to skip piece-guide rendering entirely.\n *\n * Replaces the old `VisualGuideOverlay` sibling-overlay pattern —\n * drawing directly in the ctx means no fixedMargin-unit / aspect-\n * ratio-letterbox / canvas-wrapper-negative-margin shenanigans.\n */\n pieceGuides?: PieceGuidesConfig;\n\n /**\n * ADR-0060: viewport focus mode for multi-piece spread layouts.\n *\n * - Omit / `'spread'` — show the entire artboard with all pieces\n * visible (historical behaviour). The toggle is a no-op for\n * single-piece layouts.\n * - `{ pieceId: 'front' }` — fit the viewport to that piece's\n * rect plus a peek margin so neighbour pieces remain visible\n * at the edges (dimmed) for overflow feedback. The piece id\n * must match an entry in `pieceGuides.pieces[]`; unrecognised\n * ids fall back to spread mode.\n *\n * Element data is unaffected — elements always live in spread\n * (artboard) coordinates regardless of focus. Switching focus is\n * purely an editor viewport / chrome decision.\n */\n pieceFocus?: 'spread' | { pieceId: string };\n\n // === Feature flags ===\n\n /**\n * Show toolbar UI (reserved for future use).\n * Default: false (canvas-only mode).\n * @deprecated Use `layoutConfig.showToolbar` instead.\n */\n showToolbar?: boolean;\n\n /**\n * Show layers panel (reserved for future use).\n * Default: false (canvas-only mode).\n * @deprecated Use `layoutConfig.showLayers` instead.\n */\n showLayers?: boolean;\n\n /**\n * Enable keyboard shortcuts.\n * Default: true.\n */\n enableShortcuts?: boolean;\n\n // === Overlay ===\n\n /**\n * Render custom controls on top of the canvas.\n * Useful for adding menu buttons, toolbars, etc.\n * The overlay is rendered inside the EditorProvider context.\n */\n overlay?: React.ReactNode;\n\n /**\n * When true, hides the internal Canvas component.\n * Use this when rendering a separate Canvas (e.g., in fullscreen mode)\n * that shares the same EditorProvider context.\n * This prevents two Canvas components from fighting over the same refs.\n * @deprecated Use `layoutConfig.hideCanvas` instead.\n */\n hideCanvas?: boolean;\n\n // === View configuration ===\n\n /**\n * Controls how much of the container the artboard fills.\n * Value between 0 and 1, where 1 means the artboard fills 100% of the container.\n * Default is 0.9 (90%), leaving 5% padding on each side.\n * Use smaller values (e.g., 0.8) to show more canvas area around the artboard,\n * which is useful for seeing selection handles that extend beyond the artboard.\n * @deprecated Use `layoutConfig.viewPadding` instead.\n */\n viewPadding?: number;\n\n /**\n * Border radius for artboard corners in pixels.\n * Use this to give the artboard rounded corners in the editor.\n * This is a visual-only effect - exports will still have the full artboard.\n * @default 0\n * @deprecated Use `layoutConfig.artboardBorderRadius` instead.\n */\n artboardBorderRadius?: number;\n\n /**\n * Fixed screen-space margin in pixels.\n * When set, the artboard will have this exact left/top margin regardless of zoom level.\n * This is useful for aligning the artboard with fixed-position UI elements like headers.\n * Overrides viewPadding for positioning (viewPadding still affects zoom calculation).\n * @deprecated Use `layoutConfig.fixedMargin` instead.\n */\n fixedMargin?: number;\n\n /**\n * Maximum HTML height in pixels.\n * When the artboard (with vertical fixedMargin padding) would exceed this height,\n * horizontal padding is increased to shrink the artboard until it fits within\n * this constraint while still respecting vertical fixedMargin.\n * @deprecated Use `layoutConfig.maxHeight` instead.\n */\n maxHeight?: number;\n\n /**\n * Whether to show the rotation handle on the canvas.\n * Set to false for embedded mode where rotation is handled via toolbar panel.\n * @default true\n * @deprecated Use `layoutConfig.showRotationHandle` instead.\n */\n showRotationHandle?: boolean;\n\n /**\n * Called once when the canvas is fully ready for interaction and export.\n * Fires after: artboards initialized, initial elements loaded, fonts ready,\n * and at least one render frame completed.\n */\n onReady?: () => void;\n}\n\n/**\n * Inner component with access to EditorContext\n */\nconst SnowconeCanvasInner = forwardRef<SnowconeCanvasHandle, SnowconeCanvasProps>((props, ref) => {\n // === Merge config objects with individual props (individual props take precedence) ===\n const {\n exportConfig: _exportConfig,\n imageConfig: _imageConfig,\n layoutConfig: _layoutConfig,\n kit,\n artboards: artboardConfigs,\n activeArtboard: controlledActiveArtboard,\n onArtboardChange,\n initialElements,\n onChange,\n onSelectionChange,\n autoExportInterval: autoExportInterval_prop,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onImageLoad,\n onImageError,\n onError,\n onReady,\n className,\n style,\n enableShortcuts: enableShortcuts_prop,\n overlay,\n resetDocumentOnMount,\n } = props;\n\n // Export config: individual props override exportConfig values\n const autoExportInterval = autoExportInterval_prop;\n const autoExportConfig = props.autoExportConfig ?? _exportConfig?.autoExportConfig;\n const autoExportFormat = props.autoExportFormat ?? _exportConfig?.format ?? 'dataUrl';\n const autoExportAll = props.autoExportAll ?? _exportConfig?.exportAll ?? false;\n const exportScale = props.exportScale ?? _exportConfig?.scale ?? 2;\n const maxExportSize = props.maxExportSize ?? _exportConfig?.maxSize ?? 4000;\n const exportImageFormat = props.exportImageFormat ?? _exportConfig?.imageFormat ?? 'png';\n const exportImageQuality = props.exportImageQuality ?? _exportConfig?.imageQuality ?? 0.92;\n\n // Image config: individual props override imageConfig values\n const initialImage = props.initialImage ?? _imageConfig?.src;\n const initialImageAlignment = props.initialImageAlignment ?? _imageConfig?.alignment ?? 'center';\n const initialImageScale = props.initialImageScale ?? _imageConfig?.scale ?? 1;\n const initialImageScaleMode = props.initialImageScaleMode ?? _imageConfig?.scaleMode ?? 'cover';\n\n // Layout config: individual props override layoutConfig values\n const width = props.width ?? _layoutConfig?.width ?? 1200;\n const height = props.height ?? _layoutConfig?.height ?? 1200;\n const viewPadding = props.viewPadding ?? _layoutConfig?.viewPadding ?? 0.9;\n const artboardBorderRadius = props.artboardBorderRadius ?? _layoutConfig?.artboardBorderRadius ?? 0;\n const fixedMargin = props.fixedMargin ?? _layoutConfig?.fixedMargin;\n const maxHeight = props.maxHeight ?? _layoutConfig?.maxHeight;\n const showRotationHandle = props.showRotationHandle ?? _layoutConfig?.showRotationHandle ?? true;\n const hideCanvas = props.hideCanvas ?? _layoutConfig?.hideCanvas ?? false;\n const canvasWrapperClassName = props.canvasWrapperClassName ?? _layoutConfig?.canvasWrapperClassName;\n const canvasWrapperStyle = props.canvasWrapperStyle ?? _layoutConfig?.canvasWrapperStyle;\n const canvasCutouts = props.canvasCutouts ?? _layoutConfig?.canvasCutouts;\n const pieceGuides = props.pieceGuides;\n const pieceFocus = props.pieceFocus;\n const enableShortcuts = enableShortcuts_prop ?? true;\n\n // === Resolve kit configuration ===\n const resolvedKit = useMemo(() => resolveKit(kit ?? 'pro-studio'), [kit]);\n\n // === Validate kit in development ===\n useEffect(() => {\n // Guarded: `process` is undefined in browser bundlers that don't shim it —\n // an unguarded deref white-screens the consumer app (ReferenceError: process\n // is not defined). See the packaging browser-mount gate that pins this.\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development' && resolvedKit) {\n const result = validateKit(resolvedKit);\n if (!result.valid) {\n log.warn('Kit validation warnings:', result.errors);\n }\n if (result.warnings.length > 0) {\n log.warn('Kit validation warnings:', result.warnings);\n }\n }\n }, [resolvedKit]);\n\n // === The kernel (P1: behaviors as hooks — see ./kernel.tsx) ===\n const { emitError, handleBoundaryError } = useCanvasErrorChannel(onError);\n\n useArtboardKernel({\n artboardConfigs,\n width,\n height,\n pieceGuides,\n controlledActiveArtboard,\n onArtboardChange,\n resetDocumentOnMount,\n });\n\n const { initialElementsLoadedState } = useInitialElementsKernel({\n initialElements,\n emitError,\n });\n\n const { imageLoadingState, imageError } = useInitialImageKernel({\n initialImage,\n initialImageAlignment,\n initialImageScale,\n initialImageScaleMode,\n artboardConfigs,\n onImageLoad,\n onImageError,\n emitError,\n });\n\n useChangeCallbacksKernel({ onChange, onSelectionChange });\n\n useExportKernel(\n {\n autoExportInterval,\n autoExportConfig,\n autoExportFormat,\n autoExportAll,\n exportScale,\n maxExportSize,\n exportImageFormat,\n exportImageQuality,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onReady,\n hasInitialElements: !!initialElements && initialElements.length > 0,\n initialElementsLoaded: initialElementsLoadedState,\n emitError,\n },\n ref,\n );\n\n // === Render loading state ===\n if (imageLoadingState === 'loading') {\n return (\n <div\n className={className}\n style={{\n ...style,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'var(--surface, #f5f5f5)',\n }}\n >\n <Spinner size={48} />\n </div>\n );\n }\n\n // === Render error state ===\n if (imageLoadingState === 'error' && imageError) {\n return (\n <div\n className={className}\n style={{\n ...style,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'var(--surface, #f5f5f5)',\n color: 'var(--danger, #dc2626)',\n padding: '1rem',\n }}\n >\n <div className=\"sc:text-center\">\n <div className=\"sc:font-medium\">Failed to load image</div>\n <div className=\"sc:text-sm sc:mt-1 sc:opacity-70\">{imageError.message}</div>\n </div>\n </div>\n );\n }\n\n // === Render canvas ===\n // Note: 'sc-root' class is required for toolbar CSS to work (see src/App.css)\n // Canvas-only kits (e.g. `embed-only`) are headless embedding: the host owns\n // the surface the editor sits on, so the editor-surface fill\n // (--color-canvas-bg, painted by the <canvas> outside the artboard) must not\n // cover it with an opaque block — neutralize it to transparent. Kits that\n // render their own chrome keep the fill so the design canvas still reads as\n // a distinct surface inside that chrome. Declared FIRST in the style object\n // so a host-supplied `style` var can still override it.\n const kitIsCanvasOnly = Object.entries(resolvedKit.layout?.slots ?? {}).every(\n ([slotId, sections]) => slotId === 'canvas' || !sections || sections.length === 0\n );\n const rootSurfaceVars = kitIsCanvasOnly\n ? ({ '--color-canvas-bg': 'transparent' } as React.CSSProperties)\n : undefined;\n return (\n <KitProvider kit={resolvedKit}>\n <div className={`sc-root ${className || ''}`} style={{ ...rootSurfaceVars, ...style, position: 'relative' }}>\n <ErrorBoundary\n onError={handleBoundaryError}\n fallback={(error, reset) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '24px',\n minHeight: '200px',\n backgroundColor: 'var(--surface, #f5f5f5)',\n color: 'var(--foreground, #333)',\n fontFamily: 'system-ui, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontSize: '18px', fontWeight: 600, marginBottom: '8px' }}>\n Something went wrong\n </div>\n <div style={{ fontSize: '14px', opacity: 0.7, marginBottom: '16px', maxWidth: '400px' }}>\n {error.message}\n </div>\n <button\n onClick={reset}\n style={{\n padding: '8px 20px',\n backgroundColor: 'var(--primary, #333)',\n color: 'var(--primary-foreground, #fff)',\n border: 'none',\n borderRadius: '6px',\n fontSize: '14px',\n fontWeight: 500,\n cursor: 'pointer',\n }}\n >\n Try again\n </button>\n </div>\n )}\n >\n {!hideCanvas && (() => {\n // The kit's `layout.slots` decide what chrome (toolbar / panels)\n // renders around the canvas. KitLayout resolves each slot's section\n // ids to components; a canvas-only kit (e.g. `embed-only`) collapses\n // to just the bare canvas, preserving headless embedding.\n const canvasEl = (\n <Canvas\n style={{ width: '100%' }}\n fitPadding={viewPadding}\n artboardBorderRadius={artboardBorderRadius}\n fixedMargin={fixedMargin}\n maxHeight={maxHeight}\n showRotationHandle={showRotationHandle}\n enableShortcuts={enableShortcuts}\n canvasCutouts={canvasCutouts}\n pieceGuides={pieceGuides}\n pieceFocus={pieceFocus}\n />\n );\n const wrapped =\n canvasWrapperClassName || canvasWrapperStyle ? (\n <div className={canvasWrapperClassName} style={canvasWrapperStyle}>\n {canvasEl}\n </div>\n ) : (\n canvasEl\n );\n return <KitLayout kit={resolvedKit} canvas={wrapped} />;\n })()}\n {overlay}\n </ErrorBoundary>\n </div>\n </KitProvider>\n );\n});\n\nSnowconeCanvasInner.displayName = 'SnowconeCanvasInner';\n\n/**\n * SnowconeCanvas - Self-contained embeddable canvas editor\n * Wraps with ThemeProvider and EditorProvider for full functionality\n *\n * @example With imperative export\n * ```tsx\n * const canvasRef = useRef<SnowconeCanvasHandle>(null);\n *\n * const handleExport = async () => {\n * const exports = await canvasRef.current?.exportArtboards();\n * console.log('Exported:', exports);\n * };\n *\n * return <SnowconeCanvas ref={canvasRef} ... />;\n * ```\n *\n * @example Embedded in app with existing theme\n * ```tsx\n * // When your app already manages themes, use inheritTheme to prevent conflicts\n * <SnowconeCanvas inheritTheme ... />\n * ```\n */\nexport const SnowconeCanvas = forwardRef<SnowconeCanvasHandle, SnowconeCanvasProps>((props, ref) => {\n const { inheritTheme, externalProvider, services } = props;\n\n // Resolve viewPadding from either flat prop or layoutConfig (flat takes precedence)\n const resolvedViewPadding = props.viewPadding ?? props.layoutConfig?.viewPadding;\n\n return (\n <EmbedRoot\n inheritTheme={inheritTheme}\n externalProvider={externalProvider}\n services={services}\n viewPadding={resolvedViewPadding}\n >\n <SnowconeCanvasInner ref={ref} {...props} />\n </EmbedRoot>\n );\n});\n\nSnowconeCanvas.displayName = 'SnowconeCanvas';\n\nexport default SnowconeCanvas;\n"],"names":["log","createLogger","CANVAS_SECTION_ID","MIN_CHROME_WIDTH","registered","ensureSectionsRegistered","registerBuiltinSectionsSync","EffectsPanel","ExportPanel","ImagePanel","GlyphPanel","ArtboardTabs","ContextualToolbars","ZoomControls","CropPanel","KitSectionHost","children","internalsRef","useRef","registry","EditorPartsRegistry","DesignStateStore","CommerceStateStore","makeInvariantReporter","error","EditorRootInternalsContext","SlotSections","ids","jsx","id","section","getSection","process","Component","KitLayout","kit","canvas","slots","hasTopbar","_a","hasLeft","_b","hasRight","_c","hasBottombar","_d","isCanvasOnly","useMemo","rootRef","hasChrome","warnedRef","useEffect","el","check","width","observer","entries","entry","jsxs","logger","ErrorBoundary","props","errorInfo","canvasError","EmbedRoot","inheritTheme","externalProvider","services","viewPadding","tree","ThemeProvider","EditorProvider","CanvasServicesProvider","useAutoExport","options","config","historyManager","artboards","elements","onExport","onExportScheduled","isCanvasReady","managerRef","pendingExportRef","statsRef","stats","setStats","useState","onExportRef","manager","AutoExportManager","DEFAULT_AUTO_EXPORT_CONFIG","unsubscribe","subscribeToImageLoads","elementId","e","updateConfig","useCallback","newConfig","forceExport","resetStats","resetChangeDetection","extractFontFamilies","fonts","richText","span","ensureFontsLoaded","fontFamilies","pending","family","testString","resolve","waitOneFrame","useContentReady","hasInitialElements","initialElementsLoaded","isContentReady","setIsContentReady","checkingRef","cancelled","err","alignmentMap","getAlignment","value","calculateArtworkPlacement","artwork","placement","mode","mTop","mRight","mBottom","mLeft","effectiveWidth","effectiveHeight","scaleX","scaleY","coverScale","userScale","finalScale","scaledArtworkWidth","scaledArtworkHeight","alignment","x","y","overflowX","overflowY","offsetX","offsetY","xRange","yRange","DEFAULT_OPTIONS","isValidImageUrl","url","parsed","loadImageFromURL","opts","lastError","attempt","loadImageOnce","delay","timeout","img","timeoutId","resolved","cleanup","handleSuccess","w","h","handleError","errorMsg","createImageElementWithPlacement","placementOptions","loaderOptions","loadResult","placementConfig","topLeftX","topLeftY","centerX","centerY","ImageElement","_e","_f","_g","ms","calculateEffectiveScale","height","requestedScale","maxSize","scaledWidth","scaledHeight","largestSide","cappedScale","useCanvasErrorChannel","onError","onErrorRef","emitError","CanvasRenderer","handleBoundaryError","useArtboardKernel","artboardConfigs","pieceGuides","controlledActiveArtboard","onArtboardChange","resetDocumentOnMount","setElements","setSelectedId","setMultiSelection","artboardManager","refreshArtboards","useEditor","createArtboard","selectArtboard","useArtboards","initializedRef","hasInitialSyncRef","configs","defaultBg","resolveExportBackground","c","all","existingArtboards","firstConfig","firstArtboard","i","targetArtboard","ab","activeArtboard","useInitialElementsKernel","initialElements","initialElementsLoadedRef","initialElementsLoadedState","setInitialElementsLoadedState","aliveRef","deserialized","ElementFactory","allArtboards","byName","targetName","target","familySet","fontsApi","linkPromises","linkId","link","finish","families","prev","importErr","useInitialImageKernel","initialImage","initialImageAlignment","initialImageScale","initialImageScaleMode","onImageLoad","onImageError","imageLoadingState","setImageLoadingState","imageError","setImageError","prevImageRef","newElements","artboard","artboardConfig","effectiveScaleMode","result","loadErr","useChangeCallbacksKernel","onChange","onSelectionChange","selectedId","onSelectionChangeRef","onChangeRef","cb","state","json","useExportKernel","ref","autoExportInterval","autoExportConfig","autoExportFormat","autoExportAll","exportScale","maxExportSize","exportImageFormat","exportImageQuality","onExportStatus","onExportReady","onReady","setCanvasReady","exportArtboard","exportAllArtboards","exportArtboardAsBlob","exportAllArtboardsAsBlobs","useExport","lastExportRef","exportCounterRef","useImperativeHandle","format","effectiveScale","abEffectiveScale","exportOptions","resultsById","resultsByName","artboardId","blob","dataUrl","triggerExport","exportNumber","startTime","activeAb","statusArtboardId","results","allResults","data","errorMessage","duration","handleExportScheduled","hasCalledOnReadyRef","SnowconeCanvasInner","forwardRef","_exportConfig","_imageConfig","_layoutConfig","autoExportInterval_prop","className","style","enableShortcuts_prop","overlay","artboardBorderRadius","fixedMargin","maxHeight","showRotationHandle","hideCanvas","canvasWrapperClassName","canvasWrapperStyle","canvasCutouts","pieceFocus","enableShortcuts","resolvedKit","resolveKit","validateKit","Spinner","rootSurfaceVars","slotId","sections","KitProvider","reset","canvasEl","Canvas","SnowconeCanvas","resolvedViewPadding"],"mappings":"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"}
1
+ {"version":3,"file":"SnowconeCanvas-4ef4a61J.js","sources":["../src/components/embed/KitLayout.tsx","../src/components/embed/ErrorBoundary.tsx","../src/components/embed/EmbedRoot.tsx","../src/hooks/useAutoExport.ts","../src/hooks/useContentReady.ts","../src/utils/ArtworkPlacement.ts","../src/utils/ImageLoader.ts","../src/components/embed/kernel.tsx","../src/components/embed/SnowconeCanvas.tsx"],"sourcesContent":["/**\n * KitLayout — renders a resolved {@link KitDefinition}'s layout slots.\n *\n * The kit system defines named slots (`topbar`, `left`, `canvas`, `right`,\n * `bottombar`) that map to section IDs. Each section ID resolves, via the\n * section registry, to a React component that reads its state from the editor\n * contexts. This component is the missing renderer: it walks the kit's slots,\n * resolves each section, and lays them out around the canvas.\n *\n * Before this existed, `SnowconeCanvas` ignored `kit.layout.slots` entirely and\n * rendered only the bare `<Canvas>` — so the documented default kit\n * (`pro-studio`, advertised as \"full panels, all features\") produced an editor\n * with ZERO toolbar / panels / buttons. Mounting it and querying for any\n * interactive control returned nothing. KitLayout fixes that.\n *\n * The special section id `canvas` renders the caller-supplied canvas element\n * (which needs layout-specific props), not a registry component.\n */\n\nimport React, { useMemo, useEffect, useRef, type ReactNode } from 'react';\nimport type { KitDefinition } from '../../kits/types.js';\nimport {\n getSection,\n registerBuiltinSectionsSync,\n} from '../../kits/sections.js';\nimport { createLogger } from '../../utils/logger.js';\n\nimport { EffectsPanel } from './EffectsPanel.js';\nimport { ExportPanel } from './ExportPanel.js';\nimport { ImagePanel } from './ImagePanel.js';\nimport { GlyphPanel } from './GlyphPanel.js';\nimport { ArtboardTabs } from './ArtboardTabs.js';\nimport { ContextualToolbars } from '../ContextualToolbars.js';\nimport { ZoomControls } from './ZoomControls.js';\nimport { CropPanel } from '../CropPanel.js';\nimport {\n EditorPartsRegistry,\n EditorRootInternalsContext,\n makeInvariantReporter,\n type EditorRootInternals,\n} from '../editor/RootInternals.js';\nimport { DesignStateStore } from '../editor/designStateStore.js';\nimport { CommerceStateStore } from '../editor/commerceStore.js';\n\nconst log = createLogger('KitLayout');\n\n/** Section id used to mark where the live canvas should be rendered. */\nconst CANVAS_SECTION_ID = 'canvas';\n\n/**\n * Minimum container width (px) for a kit's chrome (toolbar / side panels /\n * menus) to actually be usable. Below this, side-panel slots (`flexShrink: 0`)\n * starve the canvas while the floating contextual toolbar — which portals to\n * `position: fixed` and clamps itself to `window.innerWidth` — renders off to\n * the side of, or visually detached from, a too-small editor. The net effect a\n * developer sees is a bare canvas with no discoverable controls. 320px is the\n * canonical smallest usable editor width (one panel + a visible canvas column).\n */\nconst MIN_CHROME_WIDTH = 320;\n\nlet registered = false;\n\n/**\n * Idempotently register the built-in sections that kit presets reference.\n *\n * Uses the synchronous registration path with direct component references so\n * the first render already has every section available (the async lazy-import\n * variant would leave the first paint empty). The part-backed entries\n * (`layers-panel` / `menu-button` / `undo-redo-controls` / `add-element-menu`)\n * are registered by the registry itself from the `Editor.*` references —\n * D6: one catalog, two access styles.\n */\nfunction ensureSectionsRegistered(): void {\n if (registered) return;\n registered = true;\n\n registerBuiltinSectionsSync({\n EffectsPanel: EffectsPanel as React.ComponentType<{}>,\n ExportPanel: ExportPanel as React.ComponentType<{}>,\n ImagePanel: ImagePanel as React.ComponentType<{}>,\n GlyphPanel: GlyphPanel as React.ComponentType<{}>,\n ArtboardTabs: ArtboardTabs as React.ComponentType<{}>,\n ContextualToolbars: ContextualToolbars as React.ComponentType<{}>,\n ZoomControls: ZoomControls as React.ComponentType<{}>,\n CropPanel: CropPanel as React.ComponentType<{}>,\n });\n}\n\n/**\n * Host context for part-backed sections in a kit layout (D6).\n *\n * The re-pointed catalog entries are real `Editor.*` parts, which read the\n * Root internals (part registry, stores) and throw `E_OUTSIDE_ROOT` when\n * mounted bare. Kit slot sections are ALWAYS kit-scoped: this provides a\n * standalone internals value around each slot's sections —\n *\n * - the registry is created with the kit's canvas pre-registered (a kit\n * layout always mounts one), so `Editor.Layers` renders the live document\n * panel and requires-canvas parts are satisfied;\n * - the stores are empty placeholders — no part-backed section reads design\n * or commerce state (Buy/TextField are not catalog sections), they exist\n * to satisfy the internals shape;\n * - invariant reports log loud instead of routing to a Root error card\n * (there is no Root boundary here; the legacy editor keeps its behavior).\n *\n * Deliberately UNCONDITIONAL (it shadows any outer `<Editor.Root>` for the\n * slot's sections): a legacy `<RealtimeEmbed>` is itself an `EditorRoot`,\n * and a `<RealtimeCanvas kit=\"pro-studio\">` inside it never registers an\n * `Editor.Canvas` with that Root — sections registering requires-canvas\n * into the REAL registry would falsely throw `E_REQUIRES_CANVAS`. Scoping\n * is per-slot and never wraps the canvas slot, so the canvas overlay (the\n * Root's `CanvasBindingBridge`) keeps seeing the real internals.\n */\nfunction KitSectionHost({ children }: { children: ReactNode }): React.ReactElement {\n const internalsRef = useRef<EditorRootInternals | null>(null);\n if (internalsRef.current === null) {\n const registry = new EditorPartsRegistry();\n registry.registerCanvas('legacy-kit-canvas');\n internalsRef.current = {\n store: new DesignStateStore({ elements: [], artboards: [], activeArtboard: '' }),\n commerce: new CommerceStateStore({ productId: '', mockupIds: [] }),\n registry,\n ...makeInvariantReporter((error) => log.error('[kit section]', error)),\n };\n }\n return (\n <EditorRootInternalsContext.Provider value={internalsRef.current}>\n {children}\n </EditorRootInternalsContext.Provider>\n );\n}\n\n/** Render the section components for one slot, skipping `canvas` + unknowns.\n * Sections render inside a {@link KitSectionHost} so the part-backed catalog\n * entries (D6) have the internals they need. */\nfunction SlotSections({ ids }: { ids: string[] | undefined }) {\n if (!ids || ids.length === 0) return null;\n return (\n <KitSectionHost>\n {ids.map((id) => {\n if (id === CANVAS_SECTION_ID) return null;\n const section = getSection(id);\n if (!section) {\n // Guarded: `process` is undefined in browser bundlers (Vite/Rollup/esbuild)\n // that don't shim it. An unguarded deref white-screens the consumer app\n // (ReferenceError: process is not defined). See packaging browser-mount gate.\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n log.warn(\n `Kit layout references unknown section \"${id}\" — skipping. ` +\n `Register it with registerSection(\"${id}\", { ... }).`,\n );\n }\n return null;\n }\n const Component = section.component;\n return <Component key={id} />;\n })}\n </KitSectionHost>\n );\n}\n\nexport interface KitLayoutProps {\n /** The resolved kit whose slots should be rendered. */\n kit: KitDefinition;\n /** The live canvas element to drop into the `canvas` slot. */\n canvas: React.ReactNode;\n}\n\n/**\n * Lay out a kit's slots around the canvas.\n *\n * Layout shape:\n * ```\n * ┌───────────────── topbar ─────────────────┐\n * │ left │ canvas │ right │\n * └───────────────── bottombar ───────────────┘\n * ```\n * Empty slots collapse (render nothing), so `embed-only` (canvas-only) renders\n * exactly the bare canvas with no chrome — preserving headless embedding.\n */\nexport function KitLayout({ kit, canvas }: KitLayoutProps) {\n ensureSectionsRegistered();\n\n const slots = kit.layout.slots;\n const hasTopbar = !!slots.topbar?.some((id) => id !== CANVAS_SECTION_ID);\n const hasLeft = !!slots.left?.some((id) => id !== CANVAS_SECTION_ID);\n const hasRight = !!slots.right?.some((id) => id !== CANVAS_SECTION_ID);\n const hasBottombar = !!slots.bottombar?.some((id) => id !== CANVAS_SECTION_ID);\n\n // Fast path: canvas-only kit (e.g. embed-only) renders just the canvas with\n // no wrapper chrome — identical to the pre-kit behaviour for headless use.\n const isCanvasOnly = useMemo(\n () => !hasTopbar && !hasLeft && !hasRight && !hasBottombar,\n [hasTopbar, hasLeft, hasRight, hasBottombar],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n\n // Dev-only guard: a kit declares chrome (toolbar / panels / menus) but the\n // measured container is too narrow for that chrome to actually render\n // usably, so the developer gets a bare canvas with no discoverable\n // controls — silently. Warn with the kit name, measured width, and the fix.\n // Skipped for canvas-only kits (e.g. `embed-only`), where a chrome-less\n // canvas is intended: `hasChrome` is false, and the `isCanvasOnly` early\n // return below never renders `rootRef`, so the observer never attaches.\n const hasChrome = hasTopbar || hasLeft || hasRight || hasBottombar;\n const warnedRef = useRef(false);\n useEffect(() => {\n if (typeof process === 'undefined' || process.env?.NODE_ENV !== 'development') return;\n if (!hasChrome) return;\n const el = rootRef.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n\n const check = (width: number) => {\n if (width > 0 && width < MIN_CHROME_WIDTH && !warnedRef.current) {\n warnedRef.current = true;\n log.warn(\n `Kit \"${kit.name}\" declares chrome (toolbar / panels) but its container ` +\n `is only ${Math.round(width)}px wide — below the ~${MIN_CHROME_WIDTH}px ` +\n `needed to render it, so the toolbar / panels are suppressed and the ` +\n `editor looks like a bare canvas. Give the editor more container width, ` +\n `or pass kit=\"embed-only\" if a chrome-less canvas is intended.`,\n );\n } else if (width >= MIN_CHROME_WIDTH) {\n // Width recovered (e.g. container grew) — re-arm so a later shrink warns again.\n warnedRef.current = false;\n }\n };\n\n check(el.getBoundingClientRect().width);\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) check(entry.contentRect.width);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, [hasChrome, kit.name]);\n\n if (isCanvasOnly) {\n return <>{canvas}</>;\n }\n\n return (\n <div ref={rootRef} className=\"sc-kit-layout\" style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>\n {hasTopbar && (\n <div\n className=\"sc-kit-topbar sc-toolbar\"\n style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', flexShrink: 0 }}\n >\n <SlotSections ids={slots.topbar} />\n </div>\n )}\n <div className=\"sc-kit-body\" style={{ display: 'flex', flex: 1, minHeight: 0 }}>\n {hasLeft && (\n <div className=\"sc-kit-left\" style={{ flexShrink: 0, overflow: 'auto' }}>\n <SlotSections ids={slots.left} />\n </div>\n )}\n <div className=\"sc-kit-canvas\" style={{ flex: 1, minWidth: 0, position: 'relative' }}>\n {canvas}\n </div>\n {hasRight && (\n <div className=\"sc-kit-right\" style={{ flexShrink: 0, overflow: 'auto' }}>\n <SlotSections ids={slots.right} />\n </div>\n )}\n </div>\n {hasBottombar && (\n <div\n className=\"sc-kit-bottombar sc-toolbar\"\n style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}\n >\n <SlotSections ids={slots.bottombar} />\n </div>\n )}\n </div>\n );\n}\n\nexport default KitLayout;\n","/**\n * ErrorBoundary - Error boundary component for graceful error handling\n * Catches JavaScript errors in component tree and displays fallback UI\n *\n * @example\n * ```tsx\n * import { ErrorBoundary } from '@snowcone-app/canvas/embed';\n *\n * function App() {\n * return (\n * <ErrorBoundary\n * fallback={(error, reset) => (\n * <div>\n * <h2>Something went wrong</h2>\n * <pre>{error.message}</pre>\n * <button onClick={reset}>Try again</button>\n * </div>\n * )}\n * onError={(error) => {\n * console.error('Editor error:', error);\n * analytics.track('Editor Error', { message: error.message });\n * }}\n * >\n * <EditorProvider>\n * <Canvas />\n * </EditorProvider>\n * </ErrorBoundary>\n * );\n * }\n * ```\n */\n\nimport { Component, ReactNode, ErrorInfo } from 'react';\nimport { createLogger } from '../../utils/logger.js';\nimport type { CanvasError } from '../../types/index.js';\n\nconst logger = createLogger('ErrorBoundary');\n\nexport interface ErrorBoundaryProps {\n children: ReactNode;\n /** Fallback UI to render when an error is caught. Can be a static ReactNode or a render function. */\n fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode);\n /**\n * Render function for custom error UI.\n * Takes the error and a reset function, returns a ReactNode.\n * When both `fallback` and `renderError` are provided, `renderError` takes precedence.\n */\n renderError?: (error: Error, reset: () => void) => ReactNode;\n /** Callback fired when an error is caught. Receives the raw Error and React ErrorInfo. */\n onError?: (error: Error, errorInfo: ErrorInfo) => void;\n /** Callback fired when an error is caught, receiving a structured CanvasError. */\n onCanvasError?: (error: CanvasError) => void;\n}\n\ninterface ErrorBoundaryState {\n hasError: boolean;\n error: Error | null;\n}\n\n/**\n * Error boundary component for graceful error handling.\n * Catches React rendering errors in the component tree and displays fallback UI.\n * Supports both plain Error and structured CanvasError callbacks.\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n };\n }\n\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n hasError: true,\n error,\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo): void {\n // Log error to console\n logger.error('Caught error:', error, errorInfo);\n\n // Call custom error handler if provided (plain Error)\n if (this.props.onError) {\n this.props.onError(error, errorInfo);\n }\n\n // Call structured CanvasError handler if provided\n if (this.props.onCanvasError) {\n const canvasError: CanvasError = {\n category: 'unknown',\n message: error.message,\n originalError: error,\n recoverable: false,\n };\n this.props.onCanvasError(canvasError);\n }\n }\n\n resetError = (): void => {\n this.setState({\n hasError: false,\n error: null,\n });\n };\n\n render(): ReactNode {\n if (this.state.hasError && this.state.error) {\n // renderError takes precedence over fallback\n if (this.props.renderError) {\n return this.props.renderError(this.state.error, this.resetError);\n }\n\n // Custom fallback (legacy pattern, still supported)\n if (this.props.fallback) {\n if (typeof this.props.fallback === 'function') {\n return this.props.fallback(this.state.error, this.resetError);\n }\n return this.props.fallback;\n }\n\n // Default fallback UI\n return (\n <div\n style={{\n padding: '24px',\n border: '2px solid var(--danger)',\n borderRadius: '8px',\n backgroundColor: 'color-mix(in oklch, var(--danger) 10%, var(--background))',\n color: 'var(--danger)',\n fontFamily: 'system-ui, sans-serif',\n }}\n >\n <h2 style={{ margin: '0 0 16px 0', fontSize: '18px', fontWeight: 600 }}>\n Something went wrong\n </h2>\n <pre\n style={{\n margin: '0 0 16px 0',\n padding: '12px',\n backgroundColor: 'color-mix(in oklch, var(--danger) 15%, var(--background))',\n borderRadius: '4px',\n fontSize: '14px',\n overflow: 'auto',\n maxHeight: '200px',\n }}\n >\n {this.state.error.message}\n </pre>\n <button\n onClick={this.resetError}\n style={{\n padding: '8px 16px',\n backgroundColor: 'var(--danger)',\n color: 'var(--danger-foreground)',\n border: 'none',\n borderRadius: '4px',\n fontSize: '14px',\n fontWeight: 500,\n cursor: 'pointer',\n }}\n >\n Try again\n </button>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n","/**\n * `EmbedRoot` — the embed's provider shell, extracted from `SnowconeCanvas`\n * (P1 provider inversion, ADR-0085).\n *\n * Owns the provider boundaries the standalone embed needs: theme, the editor\n * document (`EditorProvider`), and optional privileged services. Under an\n * `<Editor.Root>` the editor document is mounted by the ROOT instead (D9:\n * state always, renderer lazy) — `Editor.Canvas` passes\n * `externalProvider` so this shell skips its own `EditorProvider` and the\n * kernel writes into the Root-owned document.\n */\n\nimport React, { type ReactNode } from 'react';\nimport { EditorProvider } from '../../contexts/EditorContext.js';\nimport { ThemeProvider } from '../../contexts/ThemeContext.js';\nimport { CanvasServicesProvider, type CanvasServices } from '../../contexts/CanvasServicesContext.js';\n\nexport interface EmbedRootProps {\n /** Inherit the host app's theme instead of managing document-level theme. */\n inheritTheme?: boolean;\n /**\n * Skip mounting an `EditorProvider` — an ancestor (an `<Editor.Root>`, or a\n * host-managed provider for shared mobile/desktop layouts) already provides\n * the editor document.\n */\n externalProvider?: boolean;\n /** Privileged-service wiring (Image Browser search / AI generation). */\n services?: CanvasServices;\n /** Forwarded to `EditorProvider` (fitToArtboard fill fraction). */\n viewPadding?: number;\n children: ReactNode;\n}\n\n/**\n * Provider shell for the embeddable canvas. `SnowconeCanvas` is\n * `<EmbedRoot><KitLayout/></EmbedRoot>` plus the kernel hooks; custom layouts\n * can reuse the same shell.\n */\nexport function EmbedRoot({\n inheritTheme,\n externalProvider,\n services,\n viewPadding,\n children,\n}: EmbedRootProps): React.ReactElement {\n // When externalProvider is true, skip creating EditorProvider\n // (assumes an ancestor already provides one)\n const tree = externalProvider ? (\n <ThemeProvider defaultTheme=\"light\" passive={inheritTheme}>\n {children}\n </ThemeProvider>\n ) : (\n <ThemeProvider defaultTheme=\"light\" passive={inheritTheme}>\n <EditorProvider viewPadding={viewPadding}>{children}</EditorProvider>\n </ThemeProvider>\n );\n\n // Only mount a services provider when the prop is given — otherwise inherit\n // any provider above us (e.g. Editor.Root's), instead of shadowing it\n // with an empty one.\n if (services) {\n return <CanvasServicesProvider services={services}>{tree}</CanvasServicesProvider>;\n }\n return tree;\n}\n","/**\n * useAutoExport - React hook for automatic export on state changes\n *\n * Features:\n * - Automatically exports when editor state changes\n * - Debounced with maximum wait time (using lodash)\n * - Deep change detection (skips exports when nothing changed)\n * - Configurable timing parameters\n */\n\nimport { useEffect, useRef, useCallback, useState } from 'react';\nimport { AutoExportManager, AutoExportConfig, AutoExportStats, DEFAULT_AUTO_EXPORT_CONFIG } from '../services/AutoExportManager.js';\nimport { subscribeToImageLoads } from '../core/ImageLoadEvents.js';\nimport { createLogger } from '../utils/logger.js';\nimport type { HybridHistoryManager } from '../core/HybridHistoryManager.js';\nimport type { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { TextElement } from '../core/TextElement.js';\nimport type { ImageElement } from '../core/ImageElement.js';\nimport type { GroupElement } from '../core/GroupElement.js';\nimport type { ShapeElement } from '../core/ShapeElement.js';\nimport type { PathElement } from '../core/PathElement.js';\n\nconst logger = createLogger('useAutoExport');\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\nexport interface UseAutoExportOptions {\n /** Auto-export configuration */\n config?: Partial<AutoExportConfig>;\n\n /** Hybrid history manager to listen to for changes */\n historyManager?: HybridHistoryManager;\n\n /** Current artboards */\n artboards: ArtboardElement[];\n\n /** Current elements */\n elements: EditorElement[];\n\n /** Callback to perform the actual export */\n onExport: () => void | Promise<void>;\n\n /**\n * Callback fired immediately when an export is scheduled (before debounce).\n * Use this to show loading indicators right away instead of waiting for export to complete.\n */\n onExportScheduled?: () => void;\n\n /**\n * Whether the canvas is mounted and ready for export operations.\n * When false, exports are deferred until canvas becomes ready.\n * This prevents \"[useExport] Canvas ref not available\" errors.\n */\n isCanvasReady?: boolean;\n}\n\nexport interface UseAutoExportReturn {\n /** Current auto-export statistics */\n stats: AutoExportStats;\n\n /** Update configuration on the fly */\n updateConfig: (config: Partial<AutoExportConfig>) => void;\n\n /** Force an immediate export (bypassing debounce) */\n forceExport: () => Promise<void>;\n\n /** Reset statistics */\n resetStats: () => void;\n\n /** Reset change detection (next export will always trigger) */\n resetChangeDetection: () => void;\n}\n\n/**\n * Hook to manage automatic export on state changes\n *\n * @example\n * ```tsx\n * const { stats, updateConfig } = useAutoExport({\n * config: {\n * enabled: true,\n * debounceMs: 100,\n * maxWaitMs: 1000,\n * },\n * historyManager,\n * artboards,\n * elements,\n * onExport: async () => {\n * await exportAllArtboards();\n * },\n * });\n * ```\n */\nexport function useAutoExport(options: UseAutoExportOptions): UseAutoExportReturn {\n const { config, historyManager, artboards, elements, onExport, onExportScheduled, isCanvasReady = false } = options;\n\n const managerRef = useRef<AutoExportManager | null>(null);\n // Track if we have a pending export waiting for canvas to become ready\n const pendingExportRef = useRef<boolean>(false);\n // Stats stored in ref to avoid re-rendering host component (e.g. SnowconeCanvas)\n // on every auto-export. Only promoted to state on explicit user actions.\n const statsRef = useRef<AutoExportStats>({\n totalExports: 0,\n skippedExports: 0,\n lastExportTime: null,\n avgExportInterval: 0,\n });\n const [stats, setStats] = useState<AutoExportStats>(statsRef.current);\n\n // Ref for onExport — always reads the latest callback at execution time.\n // This eliminates stale-closure bugs: the manager's onExport callback never\n // goes stale because it always dereferences onExportRef.current.\n const onExportRef = useRef(onExport);\n onExportRef.current = onExport;\n\n // Initialize manager\n useEffect(() => {\n\n const manager = new AutoExportManager({\n ...DEFAULT_AUTO_EXPORT_CONFIG,\n ...config,\n });\n\n // Register export callback — uses ref so it always calls the latest onExport.\n // No need to re-register when onExport changes (the ref handles it).\n manager.onExport(async () => {\n try {\n await onExportRef.current();\n\n // Update ref only — no React state update to avoid re-rendering\n // the host component (SnowconeCanvas) on every auto-export\n statsRef.current = manager.getStats();\n } catch (error) {\n logger.error('[useAutoExport] Export failed:', error);\n }\n });\n\n managerRef.current = manager;\n\n // Cleanup on unmount\n return () => {\n manager.destroy();\n };\n }, []); // Only initialize once\n\n // Trigger pending export when canvas becomes ready\n useEffect(() => {\n if (isCanvasReady && pendingExportRef.current && managerRef.current) {\n pendingExportRef.current = false;\n managerRef.current.forceExport();\n }\n }, [isCanvasReady]);\n\n // Update config when it changes\n useEffect(() => {\n if (managerRef.current && config) {\n managerRef.current.updateConfig(config);\n }\n }, [config?.enabled, config?.debounceMs, config?.maxWaitMs]);\n\n // onExport callback changes are handled via onExportRef (set above).\n // No useEffect re-registration needed — eliminates the race condition\n // where the effect fires after the debounced export has already captured\n // a stale callback reference.\n\n // Update onExportScheduled callback when it changes\n useEffect(() => {\n if (managerRef.current && onExportScheduled) {\n managerRef.current.onExportScheduled(onExportScheduled);\n }\n }, [onExportScheduled]);\n\n // Hook into history manager to listen for changes\n useEffect(() => {\n if (!historyManager || !managerRef.current) {\n return;\n }\n\n\n // Subscribe to command executed events\n const unsubscribe = historyManager.onCommandExecuted(() => {\n\n // Schedule export (will be debounced)\n if (managerRef.current) {\n // Gate on canvas ready to prevent \"[useExport] Canvas ref not available\" errors\n if (!isCanvasReady) {\n pendingExportRef.current = true;\n return;\n }\n managerRef.current.scheduleExport();\n }\n });\n\n // Cleanup\n return () => {\n unsubscribe();\n };\n }, [historyManager, isCanvasReady]);\n\n // Also watch for direct state changes (e.g., during drag operations)\n // This ensures exports trigger even when changes bypass command history\n //\n // PERF: We intentionally avoid JSON.stringify here. During drag operations,\n // elements changes on every mouse move. JSON.stringify of all elements was\n // costing 50-200ms per frame — causing visible stutter. Instead we just call\n // scheduleExport() which is a cheap debounce timer reset. The AutoExportManager\n // does its own change detection (via getCurrentStateSnapshot) before actually\n // exporting, so redundant exports are still prevented.\n useEffect(() => {\n if (!managerRef.current) {\n return;\n }\n\n // Gate on canvas ready to prevent \"[useExport] Canvas ref not available\" errors\n if (!isCanvasReady) {\n pendingExportRef.current = true;\n return;\n }\n\n // Schedule export (debounced + manager does its own change detection)\n managerRef.current.scheduleExport();\n }, [elements, artboards, isCanvasReady]);\n\n // Re-trigger export when an image finishes loading.\n // Adding an image element fires auto-export via the elements dep above,\n // but the image hasn't loaded yet so it gets skipped during serialization.\n // This subscription ensures we re-export once the bitmap is available.\n useEffect(() => {\n const unsubscribe = subscribeToImageLoads((elementId) => {\n if (!managerRef.current || !isCanvasReady) return;\n const hasElement = elements.some(e => e.id === elementId);\n if (hasElement) {\n managerRef.current.scheduleExport();\n }\n });\n return unsubscribe;\n }, [elements, isCanvasReady]);\n\n // Return control functions\n const updateConfig = useCallback((newConfig: Partial<AutoExportConfig>) => {\n if (managerRef.current) {\n managerRef.current.updateConfig(newConfig);\n }\n }, []);\n\n const forceExport = useCallback(async () => {\n if (managerRef.current) {\n await managerRef.current.forceExport();\n statsRef.current = managerRef.current.getStats();\n setStats(statsRef.current);\n }\n }, []);\n\n const resetStats = useCallback(() => {\n if (managerRef.current) {\n managerRef.current.resetStats();\n statsRef.current = managerRef.current.getStats();\n setStats(statsRef.current);\n }\n }, []);\n\n const resetChangeDetection = useCallback(() => {\n if (managerRef.current) {\n managerRef.current.resetChangeDetection();\n }\n }, []);\n\n return {\n stats,\n updateConfig,\n forceExport,\n resetStats,\n resetChangeDetection,\n };\n}\n","/**\n * useContentReady - Tracks when canvas content is truly ready for export.\n *\n * Combines multiple signals:\n * 1. DOM + artboards ready (isCanvasReady)\n * 2. Initial elements deserialized and loaded\n * 3. Fonts used by text elements available in the browser\n * 4. At least one render frame completed after all the above\n *\n * Returns a latching boolean — once true, never goes back to false.\n */\n\nimport { useState, useEffect, useRef } from 'react';\nimport { createLogger } from '../utils/logger.js';\nimport type { TextElement } from '../core/TextElement.js';\nimport type { ImageElement } from '../core/ImageElement.js';\nimport type { GroupElement } from '../core/GroupElement.js';\nimport type { ShapeElement } from '../core/ShapeElement.js';\nimport type { PathElement } from '../core/PathElement.js';\n\nconst logger = createLogger('useContentReady');\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\nexport interface UseContentReadyOptions {\n /** DOM mounted and artboards initialized */\n isCanvasReady: boolean;\n /** Current elements in the editor */\n elements: EditorElement[];\n /** Were initialElements provided to SnowconeCanvas? */\n hasInitialElements: boolean;\n /** Have initialElements been deserialized into the editor? */\n initialElementsLoaded: boolean;\n}\n\n/**\n * Extract unique font families from text elements.\n * Only text-based elements (transformType !== 'image' and !== 'group') have fontFamily.\n */\nfunction extractFontFamilies(elements: EditorElement[]): string[] {\n const fonts = new Set<string>();\n for (const el of elements) {\n if ('fontFamily' in el && typeof el.fontFamily === 'string' && el.fontFamily) {\n fonts.add(el.fontFamily);\n }\n // Also check rich text spans for per-character font families\n if ('getRichText' in el && typeof el.getRichText === 'function') {\n try {\n const richText = el.getRichText();\n if (richText?.spans) {\n for (const span of richText.spans) {\n if (span.style?.fontFamily) {\n fonts.add(span.style.fontFamily);\n }\n }\n }\n } catch {\n // Ignore errors reading rich text\n }\n }\n }\n return Array.from(fonts);\n}\n\n/**\n * Check if all fonts are available, and load any that aren't.\n * Uses the standard FontFaceSet API.\n */\nasync function ensureFontsLoaded(fontFamilies: string[]): Promise<void> {\n if (fontFamilies.length === 0) return;\n if (typeof document === 'undefined' || !document.fonts) return;\n\n // CRITICAL: do NOT gate on `document.fonts.check()`. It returns `true` for\n // any specifier whose fonts are *currently satisfiable* — including custom\n // families with no `@font-face` registered, because the browser falls back\n // to a default font and considers the request fulfilled. So a freshly\n // deserialized design that references \"Bangers\" before the Google Fonts\n // `<link>` has loaded its CSS will pass `check()` (no FontFace exists yet\n // → fallback satisfies it), the auto-export gate releases, the worker\n // exports the placement PNG with a serif fallback, and a moment later the\n // `<link>` finishes loading and the on-screen canvas re-renders correctly\n // — leaving the saved placement permanently wrong.\n //\n // `document.fonts.load()` resolves with `[]` for missing fonts, so calling\n // it unconditionally is cheap and gives us the actual \"is this glyph data\n // available?\" semantics we want.\n const pending: Promise<FontFace[]>[] = [];\n for (const family of fontFamilies) {\n const testString = `16px \"${family}\"`;\n pending.push(document.fonts.load(testString));\n }\n await Promise.all(pending);\n\n // iOS Safari quirk: `document.fonts.load()` can resolve before the font is\n // actually activated for *freshly created* canvas contexts (the offscreen\n // `document.createElement('canvas')` used by the main-thread export path,\n // and `OffscreenCanvas` in workers). The on-screen `<canvas>` paints\n // correctly because Safari activates fonts on first DOM use, but the\n // detached canvas the export builds was never DOM-attached. `document.fonts.ready`\n // resolves *after* all in-flight font activation work has settled — that's\n // the sync point detached-canvas font availability needs on iOS.\n //\n // Race with a 250ms timeout — on Chrome `ready` resolves in <1ms post-load,\n // on iOS this is the activation barrier. The race protects against a\n // wedged FontFaceSet (e.g. a `<link>` that never finishes parsing) holding\n // the export gate open indefinitely.\n if (document.fonts.ready) {\n await Promise.race([\n document.fonts.ready,\n new Promise<void>((resolve) => setTimeout(resolve, 250)),\n ]);\n }\n logger.debug(`Awaited ${pending.length} font(s)`);\n}\n\n/**\n * Wait one animation frame so the canvas render loop draws with loaded content.\n */\nfunction waitOneFrame(): Promise<void> {\n return new Promise((resolve) => requestAnimationFrame(() => resolve()));\n}\n\nexport function useContentReady(options: UseContentReadyOptions): boolean {\n const { isCanvasReady, elements, hasInitialElements, initialElementsLoaded } = options;\n const [isContentReady, setIsContentReady] = useState(false);\n const checkingRef = useRef(false);\n\n useEffect(() => {\n // Already latched — nothing to do\n if (isContentReady) return;\n\n // Gate 1: DOM + artboards must be ready\n if (!isCanvasReady) return;\n\n // Gate 2: If initial elements were provided, they must be loaded\n if (hasInitialElements && !initialElementsLoaded) return;\n\n // Gate 3: If initial elements were provided, the elements array must be non-empty\n if (hasInitialElements && elements.length === 0) return;\n\n // Prevent concurrent checks\n if (checkingRef.current) return;\n checkingRef.current = true;\n\n let cancelled = false;\n\n (async () => {\n try {\n // Gate 4: Ensure all fonts used by text elements are loaded\n const fonts = extractFontFamilies(elements);\n if (fonts.length > 0) {\n await ensureFontsLoaded(fonts);\n if (cancelled) return;\n }\n\n // Gate 5: Wait one render frame so the canvas draws with ready content\n await waitOneFrame();\n if (cancelled) return;\n setIsContentReady(true);\n } catch (err) {\n logger.error('Error during content readiness check:', err);\n // Still mark ready on error to avoid blocking exports forever\n if (!cancelled) {\n setIsContentReady(true);\n }\n } finally {\n checkingRef.current = false;\n }\n })();\n\n return () => {\n cancelled = true;\n checkingRef.current = false;\n };\n }, [isCanvasReady, elements, hasInitialElements, initialElementsLoaded, isContentReady]);\n\n return isContentReady;\n}\n","/**\n * ArtworkPlacement - Utility for calculating artwork position and scale\n *\n * Uses \"cover\" logic (like CSS object-fit: cover) to ensure artwork fills\n * the entire artboard while maintaining aspect ratio.\n */\n\n/**\n * 9-point alignment options for positioning artwork\n */\nexport type Alignment = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br';\n\n/**\n * Scale mode for artwork placement\n * - \"cover\" (default): fills entire area, may crop (like CSS object-fit: cover)\n * - \"contain\": fits entirely inside area, may have empty space (like CSS object-fit: contain)\n */\nexport type ScaleMode = 'cover' | 'contain';\n\n/**\n * Placement configuration for artwork positioning\n */\nexport interface PlacementConfig {\n /** Artboard width in pixels */\n width: number;\n /** Artboard height in pixels */\n height: number;\n /** User scale multiplier (default: 1) */\n scale?: number;\n /** 9-point alignment (default: \"c\" for center) */\n align?: Alignment;\n /** X offset in range -1 to 1 (default: 0) */\n offsetX?: number;\n /** Y offset in range -1 to 1 (default: 0) */\n offsetY?: number;\n /** Scale mode: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: ScaleMode;\n /** Top margin in pixels (reduces available area in contain mode) */\n marginTop?: number;\n /** Right margin in pixels (reduces available area in contain mode) */\n marginRight?: number;\n /** Bottom margin in pixels (reduces available area in contain mode) */\n marginBottom?: number;\n /** Left margin in pixels (reduces available area in contain mode) */\n marginLeft?: number;\n}\n\n/**\n * Result of artwork placement calculation\n */\nexport interface ArtworkPlacementResult {\n /** Final scale to apply to artwork */\n scale: number;\n /** Final artwork dimensions after scaling */\n width: number;\n height: number;\n /** Position to draw artwork (relative to artboard origin) */\n x: number;\n y: number;\n /** Debug information */\n debug: {\n coverScale: number;\n userScale: number;\n overflow: { x: number; y: number };\n alignment: string;\n appliedOffset: { x: number; y: number };\n };\n}\n\n/**\n * Map string alignment values to 9-point grid\n */\nexport const alignmentMap: Record<string, Alignment> = {\n // Vertical alignments (map to top/center/bottom center)\n 'far-top': 't',\n 'top': 't',\n 'center': 'c',\n 'bottom': 'b',\n 'far-bottom': 'b',\n // Horizontal alignments (map to left/center/right middle)\n 'far-left': 'l',\n 'left': 'l',\n 'right': 'r',\n 'far-right': 'r',\n // Direct 9-point values\n 'tl': 'tl',\n 't': 't',\n 'tr': 'tr',\n 'l': 'l',\n 'c': 'c',\n 'r': 'r',\n 'bl': 'bl',\n 'b': 'b',\n 'br': 'br',\n};\n\n/**\n * Get Alignment from string (supports various naming conventions)\n */\nexport function getAlignment(value: string | Alignment | undefined): Alignment {\n if (!value) return 'c';\n return alignmentMap[value] || 'c';\n}\n\n/**\n * Calculates how to size and position artwork on an artboard\n * Uses \"cover\" logic (like CSS object-fit: cover) to ensure artwork fills the entire area\n *\n * @param artwork - The artwork dimensions\n * @param placement - The placement configuration\n * @returns Positioning and scaling information\n *\n * @example\n * const result = calculateArtworkPlacement(\n * { width: 1000, height: 1000 }, // Square artwork\n * { width: 500, height: 800 } // Tall artboard\n * );\n * // Result: artwork scaled to 800x800, positioned at x: -150, y: 0\n */\nexport function calculateArtworkPlacement(\n artwork: { width: number; height: number },\n placement: PlacementConfig\n): ArtworkPlacementResult {\n // Step 1: Calculate base scale based on scale mode\n const mode = placement.scaleMode || 'cover';\n\n // Margins reduce available area (only in contain mode)\n const mTop = (mode === 'contain' ? placement.marginTop : 0) || 0;\n const mRight = (mode === 'contain' ? placement.marginRight : 0) || 0;\n const mBottom = (mode === 'contain' ? placement.marginBottom : 0) || 0;\n const mLeft = (mode === 'contain' ? placement.marginLeft : 0) || 0;\n\n const effectiveWidth = placement.width - mLeft - mRight;\n const effectiveHeight = placement.height - mTop - mBottom;\n\n const scaleX = effectiveWidth / artwork.width;\n const scaleY = effectiveHeight / artwork.height;\n\n // \"cover\": use LARGER scale so artwork fills entire area (may crop)\n // \"contain\": use SMALLER scale so artwork fits entirely inside (may have space)\n const coverScale = mode === 'contain'\n ? Math.min(scaleX, scaleY)\n : Math.max(scaleX, scaleY);\n\n // Step 2: Apply user's custom scale multiplier\n const userScale = placement.scale || 1;\n const finalScale = coverScale * userScale;\n\n // Step 3: Calculate final artwork dimensions after scaling\n const scaledArtworkWidth = artwork.width * finalScale;\n const scaledArtworkHeight = artwork.height * finalScale;\n\n // Step 4: Calculate base position using alignment\n const alignment = placement.align || 'c'; // default to center\n let x = 0;\n let y = 0;\n\n // Calculate the \"overflow\" relative to effective area\n // In contain mode with margins, overflow is relative to the margin-bounded area\n const overflowX = scaledArtworkWidth - effectiveWidth;\n const overflowY = scaledArtworkHeight - effectiveHeight;\n\n // Apply 9-point alignment\n switch (alignment) {\n // Top row\n case 'tl': // Top-Left\n x = 0;\n y = 0;\n break;\n case 't': // Top-Center\n x = -overflowX / 2;\n y = 0;\n break;\n case 'tr': // Top-Right\n x = -overflowX;\n y = 0;\n break;\n\n // Middle row\n case 'l': // Middle-Left\n x = 0;\n y = -overflowY / 2;\n break;\n case 'c': // Center (default)\n default:\n x = -overflowX / 2;\n y = -overflowY / 2;\n break;\n case 'r': // Middle-Right\n x = -overflowX;\n y = -overflowY / 2;\n break;\n\n // Bottom row\n case 'bl': // Bottom-Left\n x = 0;\n y = -overflowY;\n break;\n case 'b': // Bottom-Center\n x = -overflowX / 2;\n y = -overflowY;\n break;\n case 'br': // Bottom-Right\n x = -overflowX;\n y = -overflowY;\n break;\n }\n\n // Step 5: Apply margin offsets (shift positions into margin-bounded area)\n x += mLeft;\n y += mTop;\n\n // Step 6: Apply user offsets\n // Offsets are in range -1 to 1, so convert to pixel values\n const offsetX = placement.offsetX || 0;\n const offsetY = placement.offsetY || 0;\n\n // The \"range\" is how far we can move the artwork\n const xRange = overflowX;\n const yRange = overflowY;\n\n x += offsetX * xRange;\n y += offsetY * yRange;\n\n // Round to whole pixels\n x = Math.round(x);\n y = Math.round(y);\n\n return {\n // Final scale to apply to artwork\n scale: finalScale,\n\n // Final artwork dimensions after scaling\n width: Math.round(scaledArtworkWidth),\n height: Math.round(scaledArtworkHeight),\n\n // Position to draw artwork (relative to artboard origin)\n x,\n y,\n\n // Debug info\n debug: {\n coverScale,\n userScale,\n overflow: { x: overflowX, y: overflowY },\n alignment,\n appliedOffset: { x: offsetX * xRange, y: offsetY * yRange },\n },\n };\n}\n","/**\n * ImageLoader - Utility for loading images from URLs\n * Provides a clean async API with loading states and error handling\n */\n\nimport { ImageElement } from '../core/ImageElement.js';\nimport type { ImageElementConfig } from '../types/index.js';\nimport { calculateArtworkPlacement, getAlignment, type Alignment, type PlacementConfig, type ScaleMode } from './ArtworkPlacement.js';\n\nexport interface ImageLoadResult {\n success: boolean;\n element?: HTMLImageElement;\n error?: Error;\n width?: number;\n height?: number;\n aspectRatio?: number;\n}\n\nexport interface ImageLoaderOptions {\n /** Timeout in milliseconds (default: 30000 - 30 seconds) */\n timeout?: number;\n /** Number of retry attempts on failure (default: 1) */\n retries?: number;\n /** Retry delay in milliseconds (default: 1000) */\n retryDelay?: number;\n /** Whether to validate URL before loading (default: true) */\n validateUrl?: boolean;\n}\n\nexport type ImageLoadingState = 'idle' | 'loading' | 'success' | 'error';\n\nconst DEFAULT_OPTIONS: Required<ImageLoaderOptions> = {\n timeout: 30000,\n retries: 1,\n retryDelay: 1000,\n validateUrl: true,\n};\n\n/**\n * Validate if a string is a valid URL\n */\nexport function isValidImageUrl(url: string): boolean {\n try {\n const parsed = new URL(url);\n // Only allow http, https, data, and blob URLs\n return ['http:', 'https:', 'data:', 'blob:'].includes(parsed.protocol);\n } catch {\n return false;\n }\n}\n\n/**\n * Load an image from a URL\n * Returns a promise that resolves with the loaded HTMLImageElement\n */\nexport async function loadImageFromURL(\n url: string,\n options: ImageLoaderOptions = {}\n): Promise<ImageLoadResult> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n\n // Validate URL if enabled\n if (opts.validateUrl && !isValidImageUrl(url)) {\n return {\n success: false,\n error: new Error(`Invalid image URL: ${url}`),\n };\n }\n\n let lastError: Error | undefined;\n\n // Attempt to load with retries\n for (let attempt = 0; attempt <= opts.retries; attempt++) {\n try {\n const result = await loadImageOnce(url, opts.timeout);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Wait before retrying (unless this was the last attempt)\n if (attempt < opts.retries) {\n await delay(opts.retryDelay);\n }\n }\n }\n\n return {\n success: false,\n error: lastError || new Error('Failed to load image after retries'),\n };\n}\n\n/**\n * Load image once (single attempt)\n */\nfunction loadImageOnce(url: string, timeout: number): Promise<ImageLoadResult> {\n return new Promise((resolve) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let resolved = false;\n\n const cleanup = () => {\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n };\n\n const handleSuccess = () => {\n if (resolved) return;\n resolved = true;\n cleanup();\n\n const w = img.naturalWidth || img.width;\n const h = img.naturalHeight || img.height;\n resolve({\n success: true,\n element: img,\n width: w,\n height: h,\n aspectRatio: h > 0 ? w / h : 1,\n });\n };\n\n const handleError = (errorMsg: string) => {\n if (resolved) return;\n resolved = true;\n cleanup();\n\n resolve({\n success: false,\n error: new Error(errorMsg),\n });\n };\n\n img.onload = handleSuccess;\n\n img.onerror = () => {\n handleError(`Failed to load image: ${url}`);\n };\n\n // Set timeout\n timeoutId = setTimeout(() => {\n handleError(`Image load timed out after ${timeout}ms: ${url}`);\n }, timeout);\n\n // Start loading\n img.src = url;\n });\n}\n\n/**\n * Preload multiple images in parallel\n * Returns results for all images (some may fail)\n */\nexport async function preloadImages(\n urls: string[],\n options: ImageLoaderOptions = {}\n): Promise<Map<string, ImageLoadResult>> {\n const results = new Map<string, ImageLoadResult>();\n\n const promises = urls.map(async (url) => {\n const result = await loadImageFromURL(url, options);\n results.set(url, result);\n });\n\n await Promise.all(promises);\n return results;\n}\n\n/**\n * Create an ImageElement from a URL with loading state handling\n */\nexport async function createImageElement(\n url: string,\n config: Partial<ImageElementConfig> = {},\n options: ImageLoaderOptions = {}\n): Promise<{\n success: boolean;\n element?: ImageElement;\n error?: Error;\n}> {\n const loadResult = await loadImageFromURL(url, options);\n\n if (!loadResult.success) {\n return {\n success: false,\n error: loadResult.error,\n };\n }\n\n // Create ImageElement with the URL\n // The ImageElement constructor will load the image itself\n const imageElement = new ImageElement({\n ...config,\n imageUrl: url,\n imageAspectRatio: loadResult.aspectRatio,\n transformData: {\n type: 'image',\n width: config.transformData?.width || Math.min(loadResult.width || 400, 400),\n height: config.transformData?.height || Math.min(loadResult.height || 400, 400),\n cropX: config.transformData?.cropX ?? 0,\n cropY: config.transformData?.cropY ?? 0,\n cropWidth: config.transformData?.cropWidth ?? 1,\n cropHeight: config.transformData?.cropHeight ?? 1,\n flipHorizontal: config.transformData?.flipHorizontal ?? false,\n flipVertical: config.transformData?.flipVertical ?? false,\n borderRadius: config.transformData?.borderRadius ?? 0,\n },\n });\n\n return {\n success: true,\n element: imageElement,\n };\n}\n\n/**\n * Configuration for artwork placement on artboard\n */\nexport interface ArtworkPlacementOptions {\n /** Artboard dimensions */\n artboard: {\n width: number;\n height: number;\n /** Artboard position on canvas (for proper element positioning) */\n x: number;\n y: number;\n };\n /** Alignment string (e.g., 'center', 'top', 'bottom-left') */\n alignment?: string | Alignment;\n /** User scale multiplier (default: 1) */\n scale?: number;\n /** X offset in range -1 to 1 */\n offsetX?: number;\n /** Y offset in range -1 to 1 */\n offsetY?: number;\n /** Scale mode: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: ScaleMode;\n /** Top margin in pixels (reduces available area in contain mode) */\n marginTop?: number;\n /** Right margin in pixels (reduces available area in contain mode) */\n marginRight?: number;\n /** Bottom margin in pixels (reduces available area in contain mode) */\n marginBottom?: number;\n /** Left margin in pixels (reduces available area in contain mode) */\n marginLeft?: number;\n}\n\n/**\n * Create an ImageElement with proper \"cover\" sizing and placement on artboard\n *\n * This uses CSS-like object-fit: cover logic to ensure the artwork completely\n * fills the artboard while maintaining aspect ratio, with 9-point alignment support.\n *\n * @example\n * const result = await createImageElementWithPlacement(\n * 'https://example.com/art.png',\n * {\n * artboard: { width: 1200, height: 1500, x: 250, y: 250 },\n * alignment: 'center',\n * scale: 1\n * }\n * );\n */\nexport async function createImageElementWithPlacement(\n url: string,\n placementOptions: ArtworkPlacementOptions,\n config: Partial<ImageElementConfig> = {},\n loaderOptions: ImageLoaderOptions = {}\n): Promise<{\n success: boolean;\n element?: ImageElement;\n error?: Error;\n placement?: {\n artworkSize: { width: number; height: number };\n finalSize: { width: number; height: number };\n position: { x: number; y: number };\n scale: number;\n };\n}> {\n const loadResult = await loadImageFromURL(url, loaderOptions);\n\n if (!loadResult.success || !loadResult.width || !loadResult.height) {\n return {\n success: false,\n error: loadResult.error || new Error('Failed to get image dimensions'),\n };\n }\n\n // Calculate placement using cover/contain algorithm\n const placementConfig: PlacementConfig = {\n width: placementOptions.artboard.width,\n height: placementOptions.artboard.height,\n scale: placementOptions.scale,\n align: getAlignment(placementOptions.alignment),\n offsetX: placementOptions.offsetX,\n offsetY: placementOptions.offsetY,\n scaleMode: placementOptions.scaleMode,\n marginTop: placementOptions.marginTop,\n marginRight: placementOptions.marginRight,\n marginBottom: placementOptions.marginBottom,\n marginLeft: placementOptions.marginLeft,\n };\n\n const placement = calculateArtworkPlacement(\n { width: loadResult.width, height: loadResult.height },\n placementConfig\n );\n\n // placement.x and placement.y are the TOP-LEFT corner position (relative to artboard origin)\n // ImageElement.x and ImageElement.y represent the CENTER of the image\n // So we need to convert from top-left to center position\n\n // Calculate absolute TOP-LEFT position on canvas (artboard position + relative position)\n const topLeftX = placementOptions.artboard.x + placement.x;\n const topLeftY = placementOptions.artboard.y + placement.y;\n\n // Convert to CENTER position (what ImageElement expects)\n const centerX = topLeftX + placement.width / 2;\n const centerY = topLeftY + placement.height / 2;\n\n\n // Create ImageElement with calculated dimensions and CENTER position\n // Set preserveDimensions: true to prevent the image load callback from\n // recalculating the dimensions (which would break our \"cover\" sizing)\n const imageElement = new ImageElement({\n ...config,\n x: centerX,\n y: centerY,\n imageUrl: url,\n imageAspectRatio: loadResult.aspectRatio,\n preserveDimensions: true, // Don't recalculate dimensions on image load\n transformData: {\n type: 'image',\n width: placement.width,\n height: placement.height,\n cropX: config.transformData?.cropX ?? 0,\n cropY: config.transformData?.cropY ?? 0,\n cropWidth: config.transformData?.cropWidth ?? 1,\n cropHeight: config.transformData?.cropHeight ?? 1,\n flipHorizontal: config.transformData?.flipHorizontal ?? false,\n flipVertical: config.transformData?.flipVertical ?? false,\n borderRadius: config.transformData?.borderRadius ?? 0,\n },\n });\n\n return {\n success: true,\n element: imageElement,\n placement: {\n artworkSize: { width: loadResult.width, height: loadResult.height },\n finalSize: { width: placement.width, height: placement.height },\n position: { x: centerX, y: centerY },\n scale: placement.scale,\n },\n };\n}\n\n/**\n * Check if an image URL is accessible (HEAD request)\n * More efficient than fully loading the image\n */\nexport async function checkImageUrl(url: string): Promise<boolean> {\n try {\n // Use fetch with HEAD to check if the URL is accessible\n const response = await fetch(url, {\n method: 'HEAD',\n mode: 'cors',\n });\n\n // Check if response is an image\n const contentType = response.headers.get('content-type');\n return response.ok && (contentType?.startsWith('image/') || false);\n } catch {\n // Fallback: Try to load the image directly\n // Some servers don't support HEAD requests\n const result = await loadImageFromURL(url, { timeout: 5000, retries: 0 });\n return result.success;\n }\n}\n\n/**\n * Get image dimensions without fully loading it (when possible)\n * Falls back to full load if necessary\n */\nexport async function getImageDimensions(\n url: string\n): Promise<{ width: number; height: number } | null> {\n const result = await loadImageFromURL(url, { timeout: 10000, retries: 0 });\n\n if (result.success && result.width && result.height) {\n return { width: result.width, height: result.height };\n }\n\n return null;\n}\n\n// Helper function\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * The embed KERNEL — SnowconeCanvas's non-chrome behaviors as hooks\n * (P1 provider inversion, ADR-0085): artboard init/sync, saved-state restore,\n * initial-image loading, change/selection callbacks, auto-export, readiness.\n *\n * Extracted VERBATIM from `SnowconeCanvasInner` so the provider boundary can\n * move (Editor.Root mounts EditorProvider; `EmbedRoot` mounts it for the\n * legacy standalone path) without dragging two thousand lines of lifecycle\n * along. Every hook runs INSIDE an EditorProvider — they are the write path\n * into the editor document, not state owners themselves.\n *\n * This module imports the paint/export pipeline (CanvasRenderer, useExport),\n * so it must stay OUT of the always-mounted Editor.Root graph: only the\n * SnowconeCanvas chunk (behind `Editor.Canvas`'s lazy boundary) may import it.\n * CI-gated by scripts/check-import-graph.mjs.\n */\n\nimport React, { useEffect, useRef, useCallback, useState, useImperativeHandle } from 'react';\nimport { useEditor } from '../../contexts/EditorContext.js';\nimport { useArtboards } from '../../hooks/useArtboards.js';\nimport { useExport } from '../../hooks/useExport.js';\nimport { useAutoExport } from '../../hooks/useAutoExport.js';\nimport { useContentReady } from '../../hooks/useContentReady.js';\nimport { createImageElementWithPlacement } from '../../utils/ImageLoader.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { CanvasRenderer } from '../../core/CanvasRenderer.js';\nimport { ElementFactory } from '../../core/ElementFactory.js';\nimport type { CanvasError, AnyElementConfig } from '../../types/index.js';\nimport type {\n AlignmentPoint,\n ArtboardConfig,\n CanvasState,\n ExportStatusEvent,\n PieceGuidesConfig,\n SnowconeCanvasHandle,\n} from './SnowconeCanvas.js';\n\nconst log = createLogger('SnowconeCanvas');\n\nexport type ImageLoadingState = 'idle' | 'loading' | 'success' | 'error';\n\n/**\n * Calculate the effective export scale for an artboard.\n * If the scaled dimensions would exceed maxSize, reduces the scale to cap at maxSize.\n *\n * @param width - Artboard width in pixels\n * @param height - Artboard height in pixels\n * @param requestedScale - The requested export scale multiplier\n * @param maxSize - Maximum dimension (largest side) for exported image\n * @returns The effective scale to use (may be lower than requestedScale)\n */\nexport function calculateEffectiveScale(\n width: number,\n height: number,\n requestedScale: number,\n maxSize: number\n): number {\n // If maxSize is 0 or Infinity, don't cap\n if (maxSize <= 0 || !isFinite(maxSize)) {\n return requestedScale;\n }\n\n const scaledWidth = width * requestedScale;\n const scaledHeight = height * requestedScale;\n const largestScaledSide = Math.max(scaledWidth, scaledHeight);\n\n // If already under the limit, use requested scale\n if (largestScaledSide <= maxSize) {\n return requestedScale;\n }\n\n // Calculate the scale that would result in exactly maxSize for the largest side\n const largestSide = Math.max(width, height);\n const cappedScale = maxSize / largestSide;\n\n // Don't scale up if we're capping (scale should be at most 1 if artboard is already >= maxSize)\n return Math.min(cappedScale, requestedScale);\n}\n\n/**\n * Error channel: structured `onError` emission + the CanvasRenderer static\n * render-error wire + the ErrorBoundary handler.\n */\nexport function useCanvasErrorChannel(onError: ((error: CanvasError) => void) | undefined): {\n emitError: (error: CanvasError) => void;\n handleBoundaryError: (error: Error) => void;\n} {\n // Stable ref to avoid re-creating callbacks when onError changes\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n const emitError = useCallback((error: CanvasError) => {\n log.error(`[${error.category}] ${error.message}`, error.originalError);\n onErrorRef.current?.(error);\n }, []);\n\n // Wire CanvasRenderer's static error callback to emitError\n useEffect(() => {\n CanvasRenderer.onRenderError = emitError;\n return () => {\n // Only clear if it's still our callback (avoid removing another instance's callback)\n if (CanvasRenderer.onRenderError === emitError) {\n CanvasRenderer.onRenderError = null;\n }\n };\n }, [emitError]);\n\n // Fires emitError before the ErrorBoundary renders its fallback UI\n const handleBoundaryError = useCallback((error: Error) => {\n emitError({\n category: 'unknown',\n message: error.message,\n originalError: error,\n recoverable: false,\n });\n }, [emitError]);\n\n return { emitError, handleBoundaryError };\n}\n\nexport interface ArtboardKernelOptions {\n artboardConfigs: ArtboardConfig[] | undefined;\n width: number;\n height: number;\n pieceGuides: PieceGuidesConfig | undefined;\n controlledActiveArtboard: string | undefined;\n onArtboardChange: ((name: string) => void) | undefined;\n /**\n * Rebuild the editor document from scratch on mount (clear artboards +\n * elements first). Used by `Editor.Canvas`: with the P1 inversion the\n * editor document lives at `Editor.Root` and OUTLIVES Canvas mounts, so a\n * remount must reflect the live seed instead of accreting onto the previous\n * mount's artboards/elements (the D8 soak cycles exactly this).\n * @internal\n */\n resetDocumentOnMount?: boolean;\n}\n\n/**\n * Artboard init + controlled `activeArtboard` sync + `onArtboardChange`.\n */\nexport function useArtboardKernel(opts: ArtboardKernelOptions): void {\n const {\n artboardConfigs,\n width,\n height,\n pieceGuides,\n controlledActiveArtboard,\n onArtboardChange,\n resetDocumentOnMount,\n } = opts;\n\n const {\n setElements,\n setSelectedId,\n setMultiSelection,\n artboardManager,\n refreshArtboards,\n } = useEditor();\n const { createArtboard, selectArtboard, artboards } = useArtboards();\n\n // Track if initial setup is complete\n const initializedRef = useRef(false);\n\n // Track if initial artboard sync is complete (to suppress premature onArtboardChange calls)\n // During initialization, createArtboard sets each new artboard as active, so the last one\n // ends up active. We only want to fire onArtboardChange after the controlled prop is synced.\n const hasInitialSyncRef = useRef(false);\n\n // === Initialize artboards ===\n useEffect(() => {\n if (initializedRef.current) return;\n initializedRef.current = true;\n\n // Clear existing artboards and create new ones from config\n const configs = artboardConfigs || [{ name: 'Design', width, height }];\n\n // ADR-0054: piece-guide products want transparent artboards by default.\n // ArtboardElement constructor seeds `backgroundColor = '#ffffff'` and\n // every paint pass that consumes that field is *supposed* to be gated\n // on `!pieceGuides`, but the safety net here means a missed gate\n // can't leak a white frame past the silhouettes once the spread\n // clip lands in CanvasRenderer. Authors who explicitly set a\n // backgroundColor still win.\n const defaultBg = pieceGuides ? 'transparent' : undefined;\n\n // An explicitly-configured background is part of the design: it must\n // survive into exports and server renders (realtime mockups), not just\n // the editor preview. The constructor's '#ffffff' default stays\n // editor-only (exportBackground false) so apparel placements keep\n // exporting transparent.\n const resolveExportBackground = (c: ArtboardConfig) =>\n c.exportBackground ??\n (!!c.backgroundColor && c.backgroundColor !== 'transparent');\n\n if (resetDocumentOnMount) {\n // Rebuild from the seed: clear the (Root-owned, surviving) document and\n // create every configured artboard fresh. Selection may point at\n // elements that no longer exist — clear it too.\n artboardManager.clear();\n setElements([]);\n setSelectedId(null);\n setMultiSelection([]);\n for (const c of configs) {\n artboardManager.createArtboard({\n name: c.name,\n width: c.width,\n height: c.height,\n backgroundColor: c.backgroundColor ?? defaultBg,\n exportBackground: resolveExportBackground(c),\n clipShape: c.clipShape,\n role: c.role,\n });\n }\n const all = artboardManager.getAllArtboards();\n if (all.length > 0 && !controlledActiveArtboard) {\n artboardManager.setActiveArtboard(all[0].id);\n }\n refreshArtboards();\n return;\n }\n\n // Update the first (default) artboard instead of creating a new one\n const existingArtboards = artboardManager.getAllArtboards();\n if (existingArtboards.length > 0) {\n const firstConfig = configs[0];\n const firstArtboard = existingArtboards[0];\n\n // Update the first artboard\n firstArtboard.name = firstConfig.name;\n firstArtboard.width = firstConfig.width;\n firstArtboard.height = firstConfig.height;\n firstArtboard.clipShape = firstConfig.clipShape;\n firstArtboard.exportBackground = resolveExportBackground(firstConfig);\n firstArtboard.role = firstConfig.role;\n if (firstConfig.backgroundColor) {\n firstArtboard.backgroundColor = firstConfig.backgroundColor;\n } else if (defaultBg) {\n firstArtboard.backgroundColor = defaultBg;\n }\n\n // Create additional artboards\n for (let i = 1; i < configs.length; i++) {\n createArtboard(configs[i].width, configs[i].height, {\n name: configs[i].name,\n backgroundColor: configs[i].backgroundColor ?? defaultBg,\n exportBackground: resolveExportBackground(configs[i]),\n clipShape: configs[i].clipShape,\n role: configs[i].role,\n });\n }\n\n // createArtboard activates each artboard it creates, so after the loop\n // the LAST one is active — while initialElements (and legacy untagged\n // saved elements) land on the FIRST. An uncontrolled multi-artboard\n // editor used to mount showing a blank last artboard (DX F5). Default\n // the active artboard to the first config; a controlled `activeArtboard`\n // prop still wins via the sync effect below.\n if (!controlledActiveArtboard && configs.length > 1) {\n artboardManager.setActiveArtboard(firstArtboard.id);\n }\n }\n\n refreshArtboards();\n }, []);\n\n // === Sync controlled activeArtboard prop ===\n useEffect(() => {\n if (!controlledActiveArtboard) {\n // No controlled prop, but still mark as synced so onArtboardChange can fire\n hasInitialSyncRef.current = true;\n return;\n }\n\n const targetArtboard = artboards.find((ab) => ab.name === controlledActiveArtboard);\n if (targetArtboard) {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (activeArtboard?.name !== controlledActiveArtboard) {\n selectArtboard(targetArtboard.id);\n }\n // Mark as synced - now onArtboardChange can safely fire\n hasInitialSyncRef.current = true;\n }\n }, [controlledActiveArtboard, artboards]);\n\n // === Handle artboard change callback ===\n useEffect(() => {\n if (!onArtboardChange) return;\n\n // Skip until initial sync is complete - during initialization, createArtboard sets\n // each new artboard as active, so the last one ends up active. We wait for the\n // controlled activeArtboard prop to sync before firing this callback.\n if (!hasInitialSyncRef.current) return;\n\n const activeArtboard = artboardManager.getActiveArtboard();\n if (activeArtboard) {\n onArtboardChange(activeArtboard.name);\n }\n }, [artboardManager.getActiveArtboardId()]);\n}\n\nexport interface InitialElementsKernelOptions {\n initialElements: AnyElementConfig[] | undefined;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Restore saved state (`initialElements`) onto the named artboards, then\n * best-effort load the referenced fonts and force one repaint.\n */\nexport function useInitialElementsKernel(opts: InitialElementsKernelOptions): {\n initialElementsLoadedState: boolean;\n} {\n const { initialElements, emitError } = opts;\n const { setElements, artboardManager } = useEditor();\n\n const initialElementsLoadedRef = useRef(false);\n const [initialElementsLoadedState, setInitialElementsLoadedState] = useState(false);\n\n // The font-preload chain below resolves asynchronously (link load events\n // with a 4s safety setTimeout). If the component unmounts first — e.g. a\n // unit test finishing — the trailing setElements would dispatch into a\n // torn-down environment (\"window is not defined\" unhandled rejections in\n // vitest). Guard the late callbacks on unmount instead.\n const aliveRef = useRef(true);\n useEffect(() => {\n aliveRef.current = true;\n return () => {\n aliveRef.current = false;\n };\n }, []);\n\n // === Load initial elements (restore from saved state) ===\n useEffect(() => {\n if (!initialElements || initialElements.length === 0) return;\n if (initialElementsLoadedRef.current) return;\n\n initialElementsLoadedRef.current = true;\n setInitialElementsLoadedState(true);\n\n try {\n const deserialized = ElementFactory.createManyFromJSON(initialElements);\n\n // Restore each element onto its named artboard (the `artboard` tag that\n // onChange stamps on saved elements), so a multi-artboard design\n // round-trips. Elements without a tag — or with a name that no longer\n // exists — keep the legacy behavior: the first artboard.\n // (createManyFromJSON maps configs 1:1, so indexes align.)\n const allArtboards = artboardManager.getAllArtboards();\n if (allArtboards.length > 0) {\n const byName = new Map(allArtboards.map((ab) => [ab.name, ab]));\n deserialized.forEach((el, i) => {\n const targetName = initialElements[i]?.artboard;\n const target = (targetName && byName.get(targetName)) || allArtboards[0];\n artboardManager.addElementToArtboard(el.id, target.id);\n });\n }\n\n setElements(deserialized as Parameters<typeof setElements>[0]);\n\n // Custom fonts referenced by deserialised text elements need\n // an `@font-face` rule before the browser can paint them. The\n // canvas package itself doesn't inject those rules at startup —\n // only the font browser does, on user interaction. So on\n // refresh, the saved `fontFamily` falls through to a system\n // font; selecting the element opened the font browser, which\n // injected the CSS link, and the eventual re-render then\n // showed the correct face.\n //\n // Fix: for every unique non-system family in the restored\n // elements, inject a Google Fonts CSS link (best-effort —\n // matches `UnifiedFontService.loadGoogleFont`'s URL shape).\n // Once each link's stylesheet loads (which registers the\n // `@font-face` with the document), `document.fonts.load(...)`\n // pulls the actual font file, and a no-op `setElements`\n // identity update forces one more paint. Non-Google families\n // fall back gracefully — link load fails, the render keeps\n // the system fallback.\n const familySet = new Set<string>();\n for (const el of deserialized) {\n const family = (el as { fontFamily?: unknown }).fontFamily;\n if (typeof family === 'string' && family.trim().length > 0) {\n familySet.add(family);\n }\n }\n if (familySet.size > 0 && typeof document !== 'undefined') {\n const fontsApi =\n (document as Document & { fonts?: FontFaceSet }).fonts ?? null;\n const linkPromises = Array.from(familySet).map((family) => {\n const linkId = `font-${family.replace(/\\s+/g, '-').toLowerCase()}`;\n let link = document.getElementById(linkId) as HTMLLinkElement | null;\n if (!link) {\n link = document.createElement('link');\n link.id = linkId;\n link.rel = 'stylesheet';\n link.href = `https://fonts.googleapis.com/css2?family=${family.replace(\n /\\s+/g,\n '+',\n )}:wght@400;700&display=swap`;\n document.head.appendChild(link);\n }\n return new Promise<string>((resolve) => {\n // Already loaded? Resolve immediately.\n if ((link as HTMLLinkElement).sheet) {\n resolve(family);\n return;\n }\n const finish = () => resolve(family);\n link!.addEventListener('load', finish, { once: true });\n link!.addEventListener('error', finish, { once: true });\n // Safety timeout — if neither event fires, don't hang.\n setTimeout(finish, 4000);\n });\n });\n Promise.all(linkPromises)\n .then((families) => {\n if (!aliveRef.current || !fontsApi) return undefined;\n return Promise.all(\n families.flatMap((family) => [\n fontsApi.load(`400 16px \"${family}\"`).catch(() => undefined),\n fontsApi.load(`700 16px \"${family}\"`).catch(() => undefined),\n ]),\n );\n })\n .then(() => {\n // Bail if the component unmounted while fonts were loading —\n // dispatching state into a torn-down test environment throws\n // \"window is not defined\" as an unhandled rejection.\n if (!aliveRef.current) return;\n // New array reference forces dependents (render loop's\n // memoised `elementsByArtboard` keys off `elements`) to\n // re-evaluate, so the canvas re-paints with the now-\n // available font faces.\n setElements((prev) => prev.slice());\n });\n }\n } catch (err) {\n log.error('Failed to load initial elements:', err);\n const importErr = err instanceof Error ? err : new Error(String(err));\n emitError({\n category: 'import',\n message: `Failed to load initial elements: ${importErr.message}`,\n originalError: importErr,\n recoverable: true,\n });\n }\n }, [initialElements, artboardManager, setElements, emitError]);\n\n return { initialElementsLoadedState };\n}\n\nexport interface InitialImageKernelOptions {\n initialImage: string | undefined;\n initialImageAlignment: AlignmentPoint;\n initialImageScale: number;\n initialImageScaleMode: 'cover' | 'contain';\n artboardConfigs: ArtboardConfig[] | undefined;\n onImageLoad: ((src: string) => void) | undefined;\n onImageError: ((src: string, error: Error) => void) | undefined;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Load `initialImage` into every artboard with cover/contain placement.\n */\nexport function useInitialImageKernel(opts: InitialImageKernelOptions): {\n imageLoadingState: ImageLoadingState;\n imageError: Error | null;\n} {\n const {\n initialImage,\n initialImageAlignment,\n initialImageScale,\n initialImageScaleMode,\n artboardConfigs,\n onImageLoad,\n onImageError,\n emitError,\n } = opts;\n\n const { elements, setElements, artboardManager } = useEditor();\n\n // Image loading state\n const [imageLoadingState, setImageLoadingState] = useState<ImageLoadingState>('idle');\n const [imageError, setImageError] = useState<Error | null>(null);\n const prevImageRef = useRef<string | undefined>(undefined);\n\n // === Load initial image into ALL artboards ===\n useEffect(() => {\n if (!initialImage || prevImageRef.current === initialImage) return;\n\n // Skip if elements already exist (viewport switch scenario with externalProvider)\n // When using externalProvider, elements persist across component remounts,\n // so we shouldn't re-add the initial image on each mount\n if (elements.length > 0) {\n // Update ref to prevent future runs, but don't add duplicate images\n prevImageRef.current = initialImage;\n return;\n }\n\n prevImageRef.current = initialImage;\n\n const loadImage = async () => {\n setImageLoadingState('loading');\n setImageError(null);\n\n try {\n // Get ALL artboards to load the image into each one\n const allArtboards = artboardManager.getAllArtboards();\n if (allArtboards.length === 0) {\n throw new Error('No artboards available');\n }\n\n // Load image into EACH artboard\n const newElements: typeof elements = [];\n\n for (const artboard of allArtboards) {\n // Find the matching artboard config for per-artboard scaleMode\n const artboardConfig = artboardConfigs?.find(c => c.name === artboard.name);\n const effectiveScaleMode = artboardConfig?.scaleMode || initialImageScaleMode;\n\n // Use the placement function that calculates cover/contain sizing\n const result = await createImageElementWithPlacement(\n initialImage,\n {\n artboard: {\n width: artboard.width,\n height: artboard.height,\n x: artboard.x,\n y: artboard.y,\n },\n alignment: artboardConfig?.fitAlign || initialImageAlignment,\n scale: initialImageScale,\n scaleMode: effectiveScaleMode,\n marginTop: artboardConfig?.fitMarginTop,\n marginRight: artboardConfig?.fitMarginRight,\n marginBottom: artboardConfig?.fitMarginBottom,\n marginLeft: artboardConfig?.fitMarginLeft,\n }\n );\n\n if (result.success && result.element) {\n // Add element to this artboard\n artboardManager.addElementToArtboard(result.element.id, artboard.id);\n newElements.push(result.element);\n } else {\n log.error('Failed to load image for artboard:', artboard.name, result.error);\n }\n }\n\n if (newElements.length > 0) {\n setElements((prev) => [...prev, ...newElements]);\n setImageLoadingState('success');\n onImageLoad?.(initialImage);\n } else {\n const loadErr = new Error('Failed to load image into any artboard');\n setImageLoadingState('error');\n setImageError(loadErr);\n onImageError?.(initialImage, loadErr);\n emitError({\n category: 'image',\n message: loadErr.message,\n originalError: loadErr,\n recoverable: true,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n setImageLoadingState('error');\n setImageError(err);\n onImageError?.(initialImage, err);\n emitError({\n category: 'image',\n message: err.message,\n originalError: err,\n recoverable: true,\n });\n }\n };\n\n loadImage();\n }, [initialImage, initialImageAlignment, initialImageScale, initialImageScaleMode]);\n\n return { imageLoadingState, imageError };\n}\n\nexport interface ChangeCallbacksKernelOptions {\n onChange: ((state: CanvasState) => void) | undefined;\n onSelectionChange: ((elementId: string | null) => void) | undefined;\n}\n\n/**\n * `onChange` / `onSelectionChange` emission. Both fire through latest-refs so\n * an inline callback (a fresh closure identity on every host render) can't\n * re-trigger the effects — the component must not require consumers to\n * memoize their callbacks. Depending on the callback identity turns \"callback\n * updates host state\" into an infinite render loop; depending only on the\n * DATA fires exactly when the canvas actually changes. Pinned by\n * SnowconeCanvas.onChange-loop.test.tsx.\n */\nexport function useChangeCallbacksKernel(opts: ChangeCallbacksKernelOptions): void {\n const { onChange, onSelectionChange } = opts;\n const { elements, selectedId, artboardManager } = useEditor();\n const { artboards } = useArtboards();\n\n // === Handle selection change callback ===\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedId);\n }, [selectedId]);\n\n // === Handle onChange callback ===\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n useEffect(() => {\n const cb = onChangeRef.current;\n if (!cb) return;\n\n const activeArtboard = artboardManager.getActiveArtboard();\n const state: CanvasState = {\n // Stamp each element with the NAME of the artboard it lives on (the\n // manager is the membership source of truth). Without this the public\n // state is flat and a multi-placement design cannot round-trip: restore\n // collapses everything onto the first artboard, and per-placement render\n // states can't be derived from onChange. Pinned by\n // SnowconeCanvas.artboard-membership.test.tsx.\n elements: elements.map((el) => {\n const json = el.toJSON() as AnyElementConfig;\n const ab = artboardManager.getArtboardForElement(el.id);\n return ab ? { ...json, artboard: ab.name } : json;\n }),\n artboards: artboards.map((ab) => ({\n name: ab.name,\n width: ab.width,\n height: ab.height,\n clipShape: ab.clipShape,\n backgroundColor: ab.backgroundColor,\n // Carried so serializeStateForServer / saved states keep an\n // explicitly-set background renderable (mockup matches editor).\n exportBackground: ab.exportBackground,\n // Pacing role for multi-page templates (ADR-0086) — emitted only when\n // set so non-template states stay byte-identical.\n ...(ab.role ? { role: ab.role } : {}),\n })),\n activeArtboard: activeArtboard?.name || 'Design',\n };\n\n cb(state);\n }, [elements, artboards]);\n}\n\nexport interface ExportKernelOptions {\n autoExportInterval: number | undefined;\n autoExportConfig: { enabled?: boolean; debounceMs?: number; maxWaitMs?: number } | undefined;\n autoExportFormat: 'dataUrl' | 'blob';\n autoExportAll: boolean;\n exportScale: number;\n maxExportSize: number;\n exportImageFormat: 'png' | 'jpg' | 'jpeg' | 'webp';\n exportImageQuality: number;\n onExport: ((exports: Record<string, string | Blob>) => void) | undefined;\n onExportScheduled: (() => void) | undefined;\n onExportStatus: ((event: ExportStatusEvent) => void) | undefined;\n onExportReady: ((exportFn: () => Promise<Record<string, string | Blob>>) => void) | undefined;\n onReady: (() => void) | undefined;\n hasInitialElements: boolean;\n initialElementsLoaded: boolean;\n emitError: (error: CanvasError) => void;\n}\n\n/**\n * Export kernel: the imperative `exportArtboards` handle, `onExportReady`,\n * smart auto-export (debounced, change-detected), content readiness, the\n * `onReady` signal, and the canvas-ready gate.\n */\nexport function useExportKernel(\n opts: ExportKernelOptions,\n ref: React.ForwardedRef<SnowconeCanvasHandle>,\n): void {\n const {\n autoExportInterval,\n autoExportConfig,\n autoExportFormat,\n autoExportAll,\n exportScale,\n maxExportSize,\n exportImageFormat,\n exportImageQuality,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onReady,\n hasInitialElements,\n initialElementsLoaded,\n emitError,\n } = opts;\n\n const {\n elements,\n artboardManager,\n historyManager,\n isCanvasReady,\n setCanvasReady,\n } = useEditor();\n const { artboards } = useArtboards();\n const { exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs } = useExport();\n\n // Export tracking\n const lastExportRef = useRef<number>(0);\n const exportCounterRef = useRef<number>(0);\n\n // Expose imperative handle for programmatic export (ref-based API)\n useImperativeHandle(ref, () => ({\n exportArtboards: async (options = {}) => {\n const format = options.format || autoExportFormat;\n const requestedScale = options.scale || exportScale;\n const all = options.all ?? false;\n\n // Calculate effective scale (capped to maxExportSize)\n let effectiveScale = requestedScale;\n if (maxExportSize > 0 && isFinite(maxExportSize)) {\n for (const ab of artboards) {\n const abEffectiveScale = calculateEffectiveScale(ab.width, ab.height, requestedScale, maxExportSize);\n effectiveScale = Math.min(effectiveScale, abEffectiveScale);\n }\n }\n\n const exportOptions = { scale: effectiveScale, format: exportImageFormat as 'png' | 'jpg' | 'jpeg' | 'webp', quality: exportImageQuality };\n\n if (all) {\n // Export all artboards (results keyed by artboard ID)\n const resultsById = format === 'blob'\n ? await exportAllArtboardsAsBlobs(exportOptions)\n : await exportAllArtboards(exportOptions);\n\n // Transform keys from artboard ID to artboard name\n const resultsByName: Record<string, string | Blob> = {};\n for (const [artboardId, result] of Object.entries(resultsById)) {\n const artboard = artboards.find(ab => ab.id === artboardId);\n if (artboard) {\n resultsByName[artboard.name] = result;\n }\n }\n return resultsByName;\n } else {\n // Export active artboard only\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) {\n throw new Error('[SnowconeCanvas] No active artboard found');\n }\n\n if (format === 'blob') {\n const blob = await exportArtboardAsBlob(activeArtboard.id, exportOptions);\n return { [activeArtboard.name]: blob };\n } else {\n const dataUrl = await exportArtboard(activeArtboard.id, exportOptions);\n return { [activeArtboard.name]: dataUrl };\n }\n }\n },\n }), [exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs, autoExportFormat, exportScale, maxExportSize, exportImageFormat, exportImageQuality, artboardManager, artboards]);\n\n // Provide export function via callback (modern React pattern, works with dynamic imports)\n useEffect(() => {\n if (onExportReady) {\n const exportFn = async () => {\n // Get the active artboard\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) {\n throw new Error('[SnowconeCanvas] No active artboard found');\n }\n\n // Calculate effective scale (capped to maxExportSize)\n const effectiveScale = calculateEffectiveScale(\n activeArtboard.width,\n activeArtboard.height,\n exportScale,\n maxExportSize\n );\n\n // Export as single artboard (returns string or blob)\n const result = autoExportFormat === 'blob'\n ? await exportArtboardAsBlob(activeArtboard.id, { scale: effectiveScale })\n : await exportArtboard(activeArtboard.id, { scale: effectiveScale });\n\n // Return as Record keyed by artboard name (to match expected interface)\n return { [activeArtboard.name]: result };\n };\n onExportReady(exportFn);\n }\n }, [onExportReady, exportArtboard, exportArtboardAsBlob, artboardManager, autoExportFormat, exportScale, maxExportSize]);\n\n // === Auto export ===\n const triggerExport = useCallback(async () => {\n if (!onExport && !onExportStatus) return;\n\n const exportNumber = ++exportCounterRef.current;\n const startTime = Date.now();\n\n // Calculate effective scale for each artboard (capped to maxExportSize)\n // Use the most restrictive scale to ensure all artboards fit within maxExportSize\n let effectiveScale = exportScale;\n if (maxExportSize > 0 && isFinite(maxExportSize)) {\n for (const ab of artboards) {\n const abEffectiveScale = calculateEffectiveScale(ab.width, ab.height, exportScale, maxExportSize);\n effectiveScale = Math.min(effectiveScale, abEffectiveScale);\n }\n }\n\n // Determine the artboard ID for status events\n const activeAb = artboardManager.getActiveArtboard();\n const statusArtboardId = activeAb?.id ?? 'unknown';\n\n // Fire 'rendering' status event\n onExportStatus?.({ status: 'rendering', artboardId: statusArtboardId });\n\n lastExportRef.current = startTime;\n\n try {\n let results: Record<string, string | Blob>;\n\n const exportOptions = {\n format: exportImageFormat as 'png' | 'jpg' | 'jpeg' | 'webp',\n scale: effectiveScale,\n quality: exportImageQuality,\n };\n\n if (autoExportFormat === 'blob') {\n // Export as Blob (more efficient for API uploads)\n if (autoExportAll) {\n const allResults = await exportAllArtboardsAsBlobs(exportOptions);\n results = {};\n for (const [artboardId, data] of Object.entries(allResults)) {\n const artboard = artboards.find((ab) => ab.id === artboardId);\n if (artboard) {\n results[artboard.name] = data;\n }\n }\n } else {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) return;\n const blob = await exportArtboardAsBlob(activeArtboard.id, exportOptions);\n results = { [activeArtboard.name]: blob };\n }\n } else {\n // Export as dataUrl (default)\n if (autoExportAll) {\n const allResults = await exportAllArtboards(exportOptions);\n results = {};\n for (const [artboardId, data] of Object.entries(allResults)) {\n const artboard = artboards.find((ab) => ab.id === artboardId);\n if (artboard) {\n results[artboard.name] = data;\n }\n }\n } else {\n const activeArtboard = artboardManager.getActiveArtboard();\n if (!activeArtboard) return;\n const dataUrl = await exportArtboard(activeArtboard.id, exportOptions);\n results = { [activeArtboard.name]: dataUrl };\n }\n }\n\n // Fire legacy callback (backwards compat)\n onExport?.(results);\n\n // Fire unified status callback\n onExportStatus?.({ status: 'complete', artboardId: statusArtboardId, result: results });\n } catch (error) {\n // Silently skip if canvas ref not available (canvas still mounting)\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage.includes('Canvas ref not available')) {\n // Canvas not ready yet, will retry on next interval\n return;\n }\n const duration = Date.now() - startTime;\n log.error('Export failed (export #' + exportNumber + '):', {\n duration: `${duration}ms`,\n error: errorMessage,\n });\n\n // Fire unified error status callback\n const err = error instanceof Error ? error : new Error(errorMessage);\n onExportStatus?.({ status: 'error', artboardId: statusArtboardId, error: err });\n\n // Fire general onError callback\n emitError({\n category: 'export',\n message: err.message,\n originalError: err,\n artboardId: statusArtboardId,\n recoverable: true,\n });\n }\n }, [onExport, onExportStatus, autoExportAll, autoExportFormat, exportScale, maxExportSize, exportImageFormat, exportImageQuality, exportArtboard, exportAllArtboards, exportArtboardAsBlob, exportAllArtboardsAsBlobs, artboards, artboardManager, elements, autoExportConfig, emitError]);\n\n // === Deprecation warning for autoExportInterval ===\n useEffect(() => {\n if (autoExportInterval !== undefined && autoExportInterval > 0) {\n log.warn(\n 'autoExportInterval is deprecated and ignored. ' +\n 'Use autoExportConfig={{ enabled: true, debounceMs: 100, maxWaitMs: 1000 }} instead.'\n );\n }\n }, []);\n\n // === Combined scheduled callback (fires both legacy + unified) ===\n const handleExportScheduled = useCallback(() => {\n // Fire legacy callback (backwards compat)\n onExportScheduled?.();\n\n // Fire unified status callback with 'scheduled' event\n if (onExportStatus) {\n const activeAb = artboardManager.getActiveArtboard();\n onExportStatus({ status: 'scheduled', artboardId: activeAb?.id ?? 'unknown' });\n }\n }, [onExportScheduled, onExportStatus, artboardManager]);\n\n // === Content readiness (gates auto-export on full content readiness) ===\n const isContentReady = useContentReady({\n isCanvasReady,\n elements,\n hasInitialElements,\n initialElementsLoaded,\n });\n\n // === Fire onReady callback once content is ready ===\n const hasCalledOnReadyRef = useRef(false);\n useEffect(() => {\n if (isContentReady && !hasCalledOnReadyRef.current) {\n hasCalledOnReadyRef.current = true;\n onReady?.();\n }\n }, [isContentReady, onReady]);\n\n // === Smart auto-export with debouncing and change detection ===\n useAutoExport({\n config: autoExportConfig ? {\n enabled: autoExportConfig.enabled ?? true,\n debounceMs: autoExportConfig.debounceMs ?? 100,\n maxWaitMs: autoExportConfig.maxWaitMs ?? 1000,\n } : {\n enabled: false, // Only use smart export if autoExportConfig is provided\n },\n historyManager,\n artboards: artboards,\n elements,\n onExport: triggerExport,\n onExportScheduled: handleExportScheduled,\n isCanvasReady: isContentReady, // Gate exports until content is fully ready (fonts loaded, elements deserialized)\n });\n\n // === Signal canvas ready when artboards are initialized ===\n // This gates auto-export to prevent \"[useExport] Canvas ref not available\" errors\n useEffect(() => {\n if (artboards.length > 0 && !isCanvasReady) {\n // Small delay to ensure canvas DOM is mounted after artboards are ready\n const timeoutId = requestAnimationFrame(() => {\n setCanvasReady(true);\n });\n return () => cancelAnimationFrame(timeoutId);\n }\n return undefined;\n }, [artboards.length, isCanvasReady, setCanvasReady]);\n\n // === Cleanup canvas ready on unmount ===\n useEffect(() => {\n return () => {\n setCanvasReady(false);\n };\n }, [setCanvasReady]);\n}\n","/**\n * SnowconeCanvas - Main embeddable canvas component for e-commerce integration\n *\n * A self-contained canvas editor that wraps EditorProvider and handles:\n * - Artboard initialization and synchronization\n * - Image loading from URL with loading/error states\n * - Auto-export on interval or artboard change\n * - State change callbacks\n *\n * @example\n * ```tsx\n * import { SnowconeCanvas } from '@snowcone-app/canvas';\n *\n * function ProductCustomizer() {\n * return (\n * <SnowconeCanvas\n * artboards={[\n * { name: 'Front', width: 1200, height: 1200, clipShape: 'circle' },\n * { name: 'Back', width: 1200, height: 1200 },\n * ]}\n * initialImage=\"https://example.com/product.png\"\n * autoExportInterval={500}\n * onExport={(exports) => {\n * // exports = { 'Front': dataUrl, 'Back': dataUrl }\n * console.log('Design updated:', exports);\n * }}\n * />\n * );\n * }\n * ```\n */\n\nimport React, { useEffect, useMemo, forwardRef } from 'react';\nimport { KitProvider } from '../../contexts/KitContext.js';\nimport { type CanvasServices } from '../../contexts/CanvasServicesContext.js';\nimport { Canvas } from './Canvas.js';\nimport { KitLayout } from './KitLayout.js';\nimport { ErrorBoundary } from './ErrorBoundary.js';\nimport { EmbedRoot } from './EmbedRoot.js';\nimport {\n useArtboardKernel,\n useCanvasErrorChannel,\n useChangeCallbacksKernel,\n useExportKernel,\n useInitialElementsKernel,\n useInitialImageKernel,\n} from './kernel.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { CanvasError } from '../../types/index.js';\n\nconst log = createLogger('SnowconeCanvas');\nimport { Spinner } from './LoadingStates.js';\nimport { resolveKit } from '../../kits/registry.js';\nimport { validateKit } from '../../kits/validation.js';\nimport type { KitPresetId, KitDefinition } from '../../kits/types.js';\nimport type { ArtboardRole, ClipShape, AnyElementConfig } from '../../types/index.js';\nimport type {\n PieceGuide,\n PieceGuideRendererPiece,\n PieceGuideRendererStyle,\n PieceGuideVisibility,\n} from '../../rendering/PieceGuideRenderer.js';\n\n// Re-export ClipShape type for convenience\nexport type { ClipShape, ArtboardRole } from '../../types/index.js';\n\n/** 9-point alignment grid values */\nexport type AlignmentPoint = 'tl' | 't' | 'tr' | 'l' | 'c' | 'r' | 'bl' | 'b' | 'br' | 'center' | 'top' | 'bottom' | 'left' | 'right';\n\n/** Events emitted during the export lifecycle */\nexport type ExportStatusEvent =\n | { status: 'scheduled'; artboardId: string }\n | { status: 'rendering'; artboardId: string }\n | { status: 'complete'; artboardId: string; result: Record<string, string | Blob> }\n | { status: 'error'; artboardId: string; error: Error };\n\nexport interface ArtboardConfig {\n /** Display name for the artboard (used as key in export results) */\n name: string;\n /** Width in pixels */\n width: number;\n /** Height in pixels */\n height: number;\n /** Optional clip shape for content masking */\n clipShape?: ClipShape;\n /** Optional background color */\n backgroundColor?: string;\n /**\n * Whether the background color is part of the design — painted in exports\n * and server renders (realtime mockups), not just in the editor. Defaults to\n * `true` when `backgroundColor` is explicitly set (and not 'transparent'),\n * `false` otherwise: an author who sets a background expects the mockup to\n * match the editor.\n */\n exportBackground?: boolean;\n /**\n * Pacing role in a multi-page template (ADR-0086 albums):\n * `'cover' | 'front-matter' | 'repeating' | 'back-matter'`. Template\n * metadata only — auto-fill clones `repeating` spreads as the page count\n * grows. Round-trips through saved states and `onChange`; never sent on\n * the server render wire (`serializeStateForServer` strips it).\n */\n role?: ArtboardRole;\n /** Scale mode for initial image: \"cover\" fills area (may crop), \"contain\" fits inside (may have space). Default: \"cover\" */\n scaleMode?: 'cover' | 'contain';\n /** Top margin in pixels for contain mode (reduces available area) */\n fitMarginTop?: number;\n /** Right margin in pixels for contain mode (reduces available area) */\n fitMarginRight?: number;\n /** Bottom margin in pixels for contain mode (reduces available area) */\n fitMarginBottom?: number;\n /** Left margin in pixels for contain mode (reduces available area) */\n fitMarginLeft?: number;\n /** Alignment override for contain mode (9-point grid: 'tl','t','tr','l','c','r','bl','b','br') */\n fitAlign?: AlignmentPoint;\n}\n\nexport interface CanvasState {\n /**\n * Serialized element configurations for persistence. Each element carries an\n * `artboard` field — the NAME of the artboard it lives on — so a\n * multi-artboard design round-trips through save→`initialElements` and\n * `serializeStateForServer` can emit every artboard with its own elements.\n */\n elements: AnyElementConfig[];\n /** Artboard configurations */\n artboards: ArtboardConfig[];\n /** Currently active artboard name */\n activeArtboard: string;\n}\n\n/**\n * Imperative handle for programmatic control of SnowconeCanvas\n */\nexport interface SnowconeCanvasHandle {\n /**\n * Trigger an export of artboards programmatically.\n * Returns a promise that resolves with the exported artboards.\n *\n * @param options Export options\n * @returns Promise resolving to exported artboards keyed by name\n *\n * @example\n * const canvasRef = useRef<SnowconeCanvasHandle>(null);\n * const handleExport = async () => {\n * const exports = await canvasRef.current?.exportArtboards();\n * console.log('Exported:', exports);\n * };\n */\n exportArtboards: (options?: {\n /** Export format */\n format?: 'dataUrl' | 'blob';\n /** Resolution multiplier */\n scale?: number;\n /** Export all artboards or just active */\n all?: boolean;\n }) => Promise<Record<string, string | Blob>>;\n}\n\n/**\n * Configuration for export behavior (scale, format, auto-export).\n *\n * Group these props together instead of passing them individually:\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * exportConfig={{\n * autoExportConfig: { enabled: true, debounceMs: 100, maxWaitMs: 1000 },\n * format: 'blob',\n * exportAll: true,\n * scale: 2,\n * maxSize: 4000,\n * imageFormat: 'webp',\n * imageQuality: 0.85,\n * }}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n */\nexport interface ExportConfig {\n /**\n * Smart auto-export configuration with debouncing and change detection.\n * Replaces the timer-based `autoExportInterval` with an event-driven approach.\n */\n autoExportConfig?: {\n /** Enable/disable auto-export */\n enabled?: boolean;\n /** Debounce delay - wait this long after last change before exporting (ms) */\n debounceMs?: number;\n /** Maximum wait time - force export after this duration even if changes keep coming (ms) */\n maxWaitMs?: number;\n };\n\n /**\n * Export format: 'dataUrl' (base64 string) or 'blob' (binary).\n * Blob is more efficient for API uploads.\n */\n format?: 'dataUrl' | 'blob';\n\n /**\n * If true, exports all artboards on each interval.\n * If false, only exports the active artboard.\n */\n exportAll?: boolean;\n\n /**\n * Resolution multiplier for exports.\n * Default: 2 (recommended for print quality).\n *\n * Note: This scale is applied intelligently - if the resulting image would\n * exceed `maxSize`, the scale is reduced to cap the largest dimension.\n */\n scale?: number;\n\n /**\n * Maximum export dimension (largest side) in pixels.\n * Default: 4000 (4K).\n *\n * When the artboard dimensions multiplied by scale would exceed this,\n * the effective scale is reduced to cap the output at this size.\n * Set to 0 or Infinity to disable this capping behavior.\n */\n maxSize?: number;\n\n /**\n * Image format for auto-exports.\n * Default: 'png'.\n * Use 'webp' for smaller file sizes with transparency support (good for realtime preview).\n */\n imageFormat?: 'png' | 'jpg' | 'jpeg' | 'webp';\n\n /**\n * Image quality for auto-exports (0-1).\n * Only applies to JPEG and WebP formats. PNG ignores this.\n * Default: 0.92.\n */\n imageQuality?: number;\n}\n\n/**\n * Configuration for the initial image loaded into the canvas.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * imageConfig={{\n * src: 'https://example.com/artwork.png',\n * alignment: 'center',\n * scale: 1.2,\n * scaleMode: 'contain',\n * }}\n * />\n * ```\n */\nexport interface ImageConfig {\n /**\n * URL of initial image to load into the canvas.\n * Shows loading state while fetching, error state on failure.\n */\n src?: string;\n\n /**\n * Alignment for the initial image on the artboard.\n * Uses a 9-point grid: 'tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br'\n * Or semantic values: 'center', 'top', 'bottom', 'left', 'right'\n * Default: 'center'\n */\n alignment?: AlignmentPoint;\n\n /**\n * Scale multiplier for the initial image.\n * Default: 1 (image covers artboard exactly)\n * Values > 1 make the image larger, < 1 make it smaller.\n */\n scale?: number;\n\n /**\n * Scale mode for initial image placement.\n * - \"cover\" (default): image fills artboard completely, may crop edges\n * - \"contain\": image fits entirely inside artboard, may have empty space\n */\n scaleMode?: 'cover' | 'contain';\n}\n\n/**\n * Configuration for layout and visual appearance of the canvas.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * layoutConfig={{\n * width: 1200,\n * height: 1200,\n * viewPadding: 0.85,\n * artboardBorderRadius: 12,\n * showToolbar: false,\n * showLayers: false,\n * }}\n * />\n * ```\n */\nexport interface LayoutConfig {\n /**\n * Artboard width (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n */\n width?: number;\n\n /**\n * Artboard height (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n */\n height?: number;\n\n /**\n * Controls how much of the container the artboard fills.\n * Value between 0 and 1, where 1 means the artboard fills 100% of the container.\n * Default is 0.9 (90%).\n */\n viewPadding?: number;\n\n /**\n * Border radius for artboard corners in pixels.\n * This is a visual-only effect - exports will still have the full artboard.\n * @default 0\n */\n artboardBorderRadius?: number;\n\n /**\n * Fixed screen-space margin in pixels.\n * When set, the artboard will have this exact left/top margin regardless of zoom level.\n * Overrides viewPadding for positioning.\n */\n fixedMargin?: number;\n\n /**\n * Maximum HTML height in pixels.\n * When the artboard (with vertical fixedMargin padding) would exceed this height,\n * horizontal padding is increased to shrink the artboard until it fits.\n */\n maxHeight?: number;\n\n /**\n * Show toolbar UI.\n * Default: false (canvas-only mode).\n */\n showToolbar?: boolean;\n\n /**\n * Show layers panel.\n * Default: false (canvas-only mode).\n */\n showLayers?: boolean;\n\n /**\n * Whether to show the rotation handle on the canvas.\n * Set to false for embedded mode where rotation is handled via toolbar panel.\n * @default true\n */\n showRotationHandle?: boolean;\n\n /**\n * When true, hides the internal Canvas component.\n * Use this when rendering a separate Canvas (e.g., in fullscreen mode)\n * that shares the same EditorProvider context.\n */\n hideCanvas?: boolean;\n\n /**\n * CSS class name for the canvas wrapper element.\n */\n canvasWrapperClassName?: string;\n\n /**\n * Inline styles for the canvas wrapper element.\n */\n canvasWrapperStyle?: React.CSSProperties;\n\n /**\n * ADR-0054 Phase 4: cutout shapes (in artboard-space px) that get\n * punched through the `<canvas>` element as truly transparent holes,\n * revealing whatever sits behind SnowconeCanvas in the DOM. Internally\n * translated to a CSS `clip-path: path(evenodd, ...)` that accounts\n * for the canvas's padding margin and zoom. Each shape is a rect,\n * rounded rect, or SVG path — the same `SvgPath` shape used by the\n * `VisualGuideOverlay`.\n */\n canvasCutouts?: CanvasCutoutShape[];\n}\n\n/**\n * ADR-0054: piece-guide config passed to `SnowconeCanvas`. The render\n * loop calls `renderPieceGuides` with these values after drawing the\n * artboard and its elements, so boundary/zone/label coordinates are in\n * the artboard's local pixel frame and compose cleanly with the canvas\n * viewport transform.\n */\nexport interface PieceGuidesConfig {\n /** Pieces (artboard-space rects) the guides attach to. */\n pieces: PieceGuideRendererPiece[];\n /** Authored guides keyed by piece id. Pieces without a guide fall back to the piece rect. */\n guides?: Record<string, PieceGuide>;\n /** Per-layer visibility toggles. All default to true. */\n show?: PieceGuideVisibility;\n /** Optional style overrides. */\n style?: PieceGuideRendererStyle;\n}\n\n/** ADR-0054 Phase 4: shape union used for `canvasCutouts`. Mirrors the\n * `SvgPath` shape in `VisualGuideOverlay`. Coordinates are in artboard\n * pixel units (the canvas's intrinsic/native size). */\nexport type CanvasCutoutShape =\n | { kind: \"rect\"; x: number; y: number; width: number; height: number }\n | {\n kind: \"roundedRect\";\n x: number;\n y: number;\n width: number;\n height: number;\n rx?: number;\n ry?: number;\n }\n | { kind: \"path\"; d: string };\n\n/**\n * Props for the SnowconeCanvas component.\n *\n * Props can be passed individually (flat) or grouped using config objects.\n * When both are provided, individual props take precedence over config values.\n *\n * @example Grouped config pattern (recommended for new code)\n * ```tsx\n * <SnowconeCanvas\n * artboards={[{ name: 'Front', width: 1200, height: 1200 }]}\n * imageConfig={{\n * src: 'https://example.com/artwork.png',\n * alignment: 'center',\n * scaleMode: 'contain',\n * }}\n * exportConfig={{\n * autoExportConfig: { enabled: true, debounceMs: 100 },\n * format: 'blob',\n * scale: 2,\n * imageFormat: 'webp',\n * }}\n * layoutConfig={{\n * viewPadding: 0.85,\n * artboardBorderRadius: 12,\n * showRotationHandle: false,\n * }}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n *\n * @example Flat props (backwards compatible, still fully supported)\n * ```tsx\n * <SnowconeCanvas\n * artboards={[{ name: 'Front', width: 1200, height: 1200 }]}\n * initialImage=\"https://example.com/artwork.png\"\n * autoExportConfig={{ enabled: true, debounceMs: 100 }}\n * autoExportFormat=\"blob\"\n * exportScale={2}\n * viewPadding={0.85}\n * onExportStatus={(event) => { ... }}\n * />\n * ```\n */\nexport interface SnowconeCanvasProps {\n // === Config object props (grouped) ===\n\n /**\n * Grouped export configuration. See {@link ExportConfig}.\n * Individual export props (autoExportConfig, autoExportFormat, etc.) take precedence if also provided.\n */\n exportConfig?: ExportConfig;\n\n /**\n * Grouped initial image configuration. See {@link ImageConfig}.\n * Individual image props (initialImage, initialImageAlignment, etc.) take precedence if also provided.\n */\n imageConfig?: ImageConfig;\n\n /**\n * Grouped layout/appearance configuration. See {@link LayoutConfig}.\n * Individual layout props (width, height, viewPadding, etc.) take precedence if also provided.\n */\n layoutConfig?: LayoutConfig;\n\n // === Kit configuration ===\n\n /**\n * Editor kit configuration. Use a preset name ('compact-customizer', 'pro-studio', 'embed-only')\n * or a custom KitDefinition for full control over layout, capabilities, and behavior.\n * @default 'pro-studio'\n */\n kit?: KitPresetId | KitDefinition;\n\n // === Privileged services (Image Browser search / AI generation) ===\n\n /**\n * Privileged-service wiring for the editor chrome (the Image Browser's\n * stock-image search and AI generation tabs). Host these URLs on your\n * server; your vendor keys live there — the canvas never sees a key and\n * never calls a vendor directly.\n *\n * Tabs without a configured service do not render at all. With no\n * `services` prop, the Image Browser shows only the URL/upload tab.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * services={{\n * imageSearchUrl: '/api/image-search', // your same-origin proxy route\n * generateUrl: '/api/generate-image', // your same-origin proxy route\n * }}\n * />\n * ```\n */\n services?: CanvasServices;\n\n // === Artboards (placements) ===\n\n /**\n * Array of artboard configurations.\n * Each artboard represents a placement area (e.g., 'Front', 'Back').\n */\n artboards?: ArtboardConfig[];\n\n /**\n * Currently active artboard by name.\n * Used for controlled artboard selection.\n */\n activeArtboard?: string;\n\n /**\n * Callback when user switches artboards.\n * Receives the name of the newly active artboard.\n */\n onArtboardChange?: (name: string) => void;\n\n // === Single artboard shorthand ===\n\n /**\n * Artboard width (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n * @deprecated Use `layoutConfig.width` instead.\n */\n width?: number;\n\n /**\n * Artboard height (shorthand for single artboard).\n * If `artboards` prop is provided, this is ignored.\n * @deprecated Use `layoutConfig.height` instead.\n */\n height?: number;\n\n // === Initial content ===\n\n /**\n * URL of initial image to load into the canvas.\n * Shows loading state while fetching, error state on failure.\n *\n * The image will be sized to \"cover\" the artboard (like CSS object-fit: cover)\n * and positioned according to `initialImageAlignment`.\n * @deprecated Use `imageConfig.src` instead.\n */\n initialImage?: string;\n\n /**\n * Alignment for the initial image on the artboard.\n * Uses a 9-point grid: 'tl', 't', 'tr', 'l', 'c', 'r', 'bl', 'b', 'br'\n * Or semantic values: 'center', 'top', 'bottom', 'left', 'right'\n *\n * Default: 'center' (places image centered on artboard)\n * @deprecated Use `imageConfig.alignment` instead.\n */\n initialImageAlignment?: AlignmentPoint;\n\n /**\n * Scale multiplier for the initial image.\n * Default: 1 (image covers artboard exactly)\n * Values > 1 make the image larger, < 1 make it smaller.\n * @deprecated Use `imageConfig.scale` instead.\n */\n initialImageScale?: number;\n\n /**\n * Scale mode for initial image placement.\n * - \"cover\" (default): image fills artboard completely, may crop edges\n * - \"contain\": image fits entirely inside artboard, may have empty space\n * @deprecated Use `imageConfig.scaleMode` instead.\n */\n initialImageScaleMode?: 'cover' | 'contain';\n\n /**\n * Serialized element configurations for restoring saved state.\n * Use this to restore a previously saved design.\n *\n * Elements carrying an `artboard` field (the artboard NAME, stamped by\n * `onChange`) are restored onto that artboard; elements without one land on\n * the first artboard, so pre-existing saved states keep working.\n */\n initialElements?: AnyElementConfig[];\n\n // === State callbacks ===\n\n /**\n * Called when canvas content changes (elements added, modified, or removed).\n * Use this to persist the design state.\n */\n onChange?: (state: CanvasState) => void;\n\n /**\n * Called when selection changes.\n * Receives null when nothing is selected.\n */\n onSelectionChange?: (elementId: string | null) => void;\n\n // === Auto export ===\n\n /**\n * Interval in milliseconds for automatic exports.\n * Set to 0 or undefined to disable auto-export.\n * Recommended: 500ms for responsive preview updates.\n *\n * @deprecated Use `autoExportConfig` or `exportConfig.autoExportConfig` instead, which provides smart debouncing and change detection rather than fixed-interval polling.\n */\n autoExportInterval?: number;\n\n /**\n * Smart auto-export configuration with debouncing and change detection.\n * Replaces the timer-based `autoExportInterval` with an event-driven approach.\n * @deprecated Use `exportConfig.autoExportConfig` instead.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * autoExportConfig={{\n * enabled: true,\n * debounceMs: 100, // Wait 100ms after last change\n * maxWaitMs: 1000, // Force export after 1s of continuous changes\n * }}\n * onExport={(exports) => console.log('Exported:', exports)}\n * />\n * ```\n */\n autoExportConfig?: {\n /** Enable/disable auto-export */\n enabled?: boolean;\n /** Debounce delay - wait this long after last change before exporting (ms) */\n debounceMs?: number;\n /** Maximum wait time - force export after this duration even if changes keep coming (ms) */\n maxWaitMs?: number;\n };\n\n /**\n * Export format: 'dataUrl' (base64 string) or 'blob' (binary).\n * Blob is more efficient for API uploads.\n * @deprecated Use `exportConfig.format` instead.\n */\n autoExportFormat?: 'dataUrl' | 'blob';\n\n /**\n * If true, exports all artboards on each interval.\n * If false, only exports the active artboard.\n * @deprecated Use `exportConfig.exportAll` instead.\n */\n autoExportAll?: boolean;\n\n /**\n * Resolution multiplier for exports.\n * Default: 2 (recommended for print quality).\n *\n * Note: This scale is applied intelligently - if the resulting image would\n * exceed `maxExportSize`, the scale is reduced to cap the largest dimension.\n * @deprecated Use `exportConfig.scale` instead.\n */\n exportScale?: number;\n\n /**\n * Maximum export dimension (largest side) in pixels.\n * Default: 4000 (4K).\n *\n * When the artboard dimensions multiplied by exportScale would exceed this,\n * the effective scale is reduced to cap the output at this size.\n *\n * For example:\n * - Artboard 3951x4800 with exportScale=2 would normally be 7902x9600\n * - With maxExportSize=4000, it stays at 3951x4800 (scale effectively 1)\n * - Artboard 1200x1200 with exportScale=2 becomes 2400x2400 (under limit)\n *\n * Set to 0 or Infinity to disable this capping behavior.\n * @deprecated Use `exportConfig.maxSize` instead.\n */\n maxExportSize?: number;\n\n /**\n * Image format for auto-exports.\n * Default: 'png'.\n * Use 'webp' for smaller file sizes with transparency support (good for realtime preview).\n * @deprecated Use `exportConfig.imageFormat` instead.\n */\n exportImageFormat?: 'png' | 'jpg' | 'jpeg' | 'webp';\n\n /**\n * Image quality for auto-exports (0-1).\n * Only applies to JPEG and WebP formats. PNG ignores this.\n * Default: 0.92.\n * @deprecated Use `exportConfig.imageQuality` instead.\n */\n exportImageQuality?: number;\n\n /**\n * Called with export results.\n * Results are keyed by artboard name.\n *\n * @deprecated Use `onExportStatus` for a unified export lifecycle callback, or `onExportReady` for on-demand export control.\n */\n onExport?: (exports: Record<string, string | Blob>) => void;\n\n /**\n * Called immediately when an export is scheduled (before debounce).\n * Use this to show loading indicators right away instead of waiting for export to complete.\n * This fires as soon as a change is detected, giving consumers early warning.\n *\n * @deprecated Use `onExportStatus` instead, which provides `{ status: 'scheduled' }` events along with the full export lifecycle.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onExportScheduled={() => {\n * // Show shimmer/loading immediately\n * setIsExporting(true);\n * }}\n * onExport={(exports) => {\n * // Hide shimmer after export completes\n * setIsExporting(false);\n * }}\n * />\n * ```\n */\n onExportScheduled?: () => void;\n\n /**\n * Unified export status callback. Prefer this over individual `onExport`/`onExportScheduled` callbacks.\n *\n * Receives events for each phase of the export lifecycle:\n * - `scheduled` - Export was requested (before debounce completes)\n * - `rendering` - Export rendering has started\n * - `complete` - Export finished successfully with results\n * - `error` - Export failed with an error\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onExportStatus={(event) => {\n * switch (event.status) {\n * case 'scheduled':\n * setIsExporting(true);\n * break;\n * case 'complete':\n * setIsExporting(false);\n * uploadResults(event.result);\n * break;\n * case 'error':\n * setIsExporting(false);\n * showError(event.error);\n * break;\n * }\n * }}\n * />\n * ```\n */\n onExportStatus?: (event: ExportStatusEvent) => void;\n\n /**\n * Callback that provides an export function.\n * Called once when the canvas is ready.\n * Use this for manual/on-demand exports triggered by UI buttons.\n *\n * @example\n * ```tsx\n * const [exportFn, setExportFn] = useState<(() => Promise<Record<string, string | Blob>>) | null>(null);\n *\n * <SnowconeCanvas\n * onExportReady={(fn) => setExportFn(() => fn)}\n * ...\n * />\n *\n * <button onClick={() => exportFn?.()}>Export</button>\n * ```\n */\n onExportReady?: (exportFn: () => Promise<Record<string, string | Blob>>) => void;\n\n // === Image loading callbacks ===\n\n /**\n * Called when initialImage loads successfully.\n */\n onImageLoad?: (src: string) => void;\n\n /**\n * Called when initialImage fails to load.\n */\n onImageError?: (src: string, error: Error) => void;\n\n // === Error handling ===\n\n /**\n * General error callback for recoverable and non-recoverable canvas errors.\n * Receives a structured CanvasError with category, message, and context.\n *\n * This fires for:\n * - Image loading failures (category: 'image')\n * - Export errors (category: 'export')\n * - Element import/deserialization errors (category: 'import')\n * - Unexpected React crashes caught by ErrorBoundary (category: 'unknown')\n *\n * Existing `onExportStatus` and `onImageError` callbacks continue to work.\n * `onError` provides a unified stream of all errors for logging/analytics.\n *\n * @example\n * ```tsx\n * <SnowconeCanvas\n * onError={(error) => {\n * console.error(`[${error.category}] ${error.message}`);\n * if (!error.recoverable) {\n * analytics.track('canvas_crash', { category: error.category });\n * }\n * }}\n * />\n * ```\n */\n onError?: (error: CanvasError) => void;\n\n // === Appearance ===\n\n /**\n * Theme name (reserved for future use).\n */\n theme?: string;\n\n /**\n * When true, skips the internal ThemeProvider and inherits theme from parent.\n * Use this when embedding SnowconeCanvas in an app that already manages themes.\n * This prevents the canvas from overriding the parent app's theme on document.documentElement.\n *\n * @default false\n */\n inheritTheme?: boolean;\n\n /**\n * When true, assumes EditorProvider already exists in a parent component.\n * Skips creating the internal EditorProvider so multiple SnowconeCanvas instances\n * can share the same editor state (e.g., when conditionally rendering mobile/desktop layouts).\n *\n * @default false\n */\n externalProvider?: boolean;\n\n /**\n * Rebuild the editor document from scratch on mount: clear all artboards\n * and elements before applying `artboards`/`initialElements`. Used by\n * `Editor.Canvas` (P1 inversion): the editor document lives at\n * `Editor.Root` and OUTLIVES Canvas mounts, so a remount must reflect the\n * live seed instead of accreting onto the previous mount's document. The\n * legacy `externalProvider` persistence semantics (mobile/desktop layout\n * switch) are unchanged when this is false.\n *\n * @internal\n * @default false\n */\n resetDocumentOnMount?: boolean;\n\n /**\n * CSS class name for the container.\n */\n className?: string;\n\n /**\n * Inline styles for the container.\n */\n style?: React.CSSProperties;\n\n /**\n * CSS class name for the canvas wrapper element.\n * Use this to apply CSS masks, clips, or other effects to just the canvas\n * without affecting the overlay elements.\n * @deprecated Use `layoutConfig.canvasWrapperClassName` instead.\n */\n canvasWrapperClassName?: string;\n\n /**\n * Inline styles for the canvas wrapper element.\n * Use this to apply CSS masks, clips, or other effects to just the canvas\n * without affecting the overlay elements.\n * @deprecated Use `layoutConfig.canvasWrapperStyle` instead.\n */\n canvasWrapperStyle?: React.CSSProperties;\n\n /**\n * ADR-0054 Phase 4: shapes punched through the `<canvas>` as truly\n * transparent holes. See matching field on LayoutConfig for details.\n * Takes precedence over `layoutConfig.canvasCutouts`.\n */\n canvasCutouts?: CanvasCutoutShape[];\n\n /**\n * ADR-0054: piece boundary / safe-area / zone / label overlays drawn\n * directly into the canvas render loop (no DOM SVG overlay). Pieces\n * are in artboard-space coordinates (same frame as placements and\n * `canvasCutouts`). `guides` is keyed by piece id. Supply `undefined`\n * or omit to skip piece-guide rendering entirely.\n *\n * Replaces the old `VisualGuideOverlay` sibling-overlay pattern —\n * drawing directly in the ctx means no fixedMargin-unit / aspect-\n * ratio-letterbox / canvas-wrapper-negative-margin shenanigans.\n */\n pieceGuides?: PieceGuidesConfig;\n\n /**\n * ADR-0060: viewport focus mode for multi-piece spread layouts.\n *\n * - Omit / `'spread'` — show the entire artboard with all pieces\n * visible (historical behaviour). The toggle is a no-op for\n * single-piece layouts.\n * - `{ pieceId: 'front' }` — fit the viewport to that piece's\n * rect plus a peek margin so neighbour pieces remain visible\n * at the edges (dimmed) for overflow feedback. The piece id\n * must match an entry in `pieceGuides.pieces[]`; unrecognised\n * ids fall back to spread mode.\n *\n * Element data is unaffected — elements always live in spread\n * (artboard) coordinates regardless of focus. Switching focus is\n * purely an editor viewport / chrome decision.\n */\n pieceFocus?: 'spread' | { pieceId: string };\n\n // === Feature flags ===\n\n /**\n * Show toolbar UI (reserved for future use).\n * Default: false (canvas-only mode).\n * @deprecated Use `layoutConfig.showToolbar` instead.\n */\n showToolbar?: boolean;\n\n /**\n * Show layers panel (reserved for future use).\n * Default: false (canvas-only mode).\n * @deprecated Use `layoutConfig.showLayers` instead.\n */\n showLayers?: boolean;\n\n /**\n * Enable keyboard shortcuts.\n * Default: true.\n */\n enableShortcuts?: boolean;\n\n // === Overlay ===\n\n /**\n * Render custom controls on top of the canvas.\n * Useful for adding menu buttons, toolbars, etc.\n * The overlay is rendered inside the EditorProvider context.\n */\n overlay?: React.ReactNode;\n\n /**\n * When true, hides the internal Canvas component.\n * Use this when rendering a separate Canvas (e.g., in fullscreen mode)\n * that shares the same EditorProvider context.\n * This prevents two Canvas components from fighting over the same refs.\n * @deprecated Use `layoutConfig.hideCanvas` instead.\n */\n hideCanvas?: boolean;\n\n // === View configuration ===\n\n /**\n * Controls how much of the container the artboard fills.\n * Value between 0 and 1, where 1 means the artboard fills 100% of the container.\n * Default is 0.9 (90%), leaving 5% padding on each side.\n * Use smaller values (e.g., 0.8) to show more canvas area around the artboard,\n * which is useful for seeing selection handles that extend beyond the artboard.\n * @deprecated Use `layoutConfig.viewPadding` instead.\n */\n viewPadding?: number;\n\n /**\n * Border radius for artboard corners in pixels.\n * Use this to give the artboard rounded corners in the editor.\n * This is a visual-only effect - exports will still have the full artboard.\n * @default 0\n * @deprecated Use `layoutConfig.artboardBorderRadius` instead.\n */\n artboardBorderRadius?: number;\n\n /**\n * Fixed screen-space margin in pixels.\n * When set, the artboard will have this exact left/top margin regardless of zoom level.\n * This is useful for aligning the artboard with fixed-position UI elements like headers.\n * Overrides viewPadding for positioning (viewPadding still affects zoom calculation).\n * @deprecated Use `layoutConfig.fixedMargin` instead.\n */\n fixedMargin?: number;\n\n /**\n * Maximum HTML height in pixels.\n * When the artboard (with vertical fixedMargin padding) would exceed this height,\n * horizontal padding is increased to shrink the artboard until it fits within\n * this constraint while still respecting vertical fixedMargin.\n * @deprecated Use `layoutConfig.maxHeight` instead.\n */\n maxHeight?: number;\n\n /**\n * Whether to show the rotation handle on the canvas.\n * Set to false for embedded mode where rotation is handled via toolbar panel.\n * @default true\n * @deprecated Use `layoutConfig.showRotationHandle` instead.\n */\n showRotationHandle?: boolean;\n\n /**\n * Called once when the canvas is fully ready for interaction and export.\n * Fires after: artboards initialized, initial elements loaded, fonts ready,\n * and at least one render frame completed.\n */\n onReady?: () => void;\n}\n\n/**\n * Inner component with access to EditorContext\n */\nconst SnowconeCanvasInner = forwardRef<SnowconeCanvasHandle, SnowconeCanvasProps>((props, ref) => {\n // === Merge config objects with individual props (individual props take precedence) ===\n const {\n exportConfig: _exportConfig,\n imageConfig: _imageConfig,\n layoutConfig: _layoutConfig,\n kit,\n artboards: artboardConfigs,\n activeArtboard: controlledActiveArtboard,\n onArtboardChange,\n initialElements,\n onChange,\n onSelectionChange,\n autoExportInterval: autoExportInterval_prop,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onImageLoad,\n onImageError,\n onError,\n onReady,\n className,\n style,\n enableShortcuts: enableShortcuts_prop,\n overlay,\n resetDocumentOnMount,\n } = props;\n\n // Export config: individual props override exportConfig values\n const autoExportInterval = autoExportInterval_prop;\n const autoExportConfig = props.autoExportConfig ?? _exportConfig?.autoExportConfig;\n const autoExportFormat = props.autoExportFormat ?? _exportConfig?.format ?? 'dataUrl';\n const autoExportAll = props.autoExportAll ?? _exportConfig?.exportAll ?? false;\n const exportScale = props.exportScale ?? _exportConfig?.scale ?? 2;\n const maxExportSize = props.maxExportSize ?? _exportConfig?.maxSize ?? 4000;\n const exportImageFormat = props.exportImageFormat ?? _exportConfig?.imageFormat ?? 'png';\n const exportImageQuality = props.exportImageQuality ?? _exportConfig?.imageQuality ?? 0.92;\n\n // Image config: individual props override imageConfig values\n const initialImage = props.initialImage ?? _imageConfig?.src;\n const initialImageAlignment = props.initialImageAlignment ?? _imageConfig?.alignment ?? 'center';\n const initialImageScale = props.initialImageScale ?? _imageConfig?.scale ?? 1;\n const initialImageScaleMode = props.initialImageScaleMode ?? _imageConfig?.scaleMode ?? 'cover';\n\n // Layout config: individual props override layoutConfig values\n const width = props.width ?? _layoutConfig?.width ?? 1200;\n const height = props.height ?? _layoutConfig?.height ?? 1200;\n const viewPadding = props.viewPadding ?? _layoutConfig?.viewPadding ?? 0.9;\n const artboardBorderRadius = props.artboardBorderRadius ?? _layoutConfig?.artboardBorderRadius ?? 0;\n const fixedMargin = props.fixedMargin ?? _layoutConfig?.fixedMargin;\n const maxHeight = props.maxHeight ?? _layoutConfig?.maxHeight;\n const showRotationHandle = props.showRotationHandle ?? _layoutConfig?.showRotationHandle ?? true;\n const hideCanvas = props.hideCanvas ?? _layoutConfig?.hideCanvas ?? false;\n const canvasWrapperClassName = props.canvasWrapperClassName ?? _layoutConfig?.canvasWrapperClassName;\n const canvasWrapperStyle = props.canvasWrapperStyle ?? _layoutConfig?.canvasWrapperStyle;\n const canvasCutouts = props.canvasCutouts ?? _layoutConfig?.canvasCutouts;\n const pieceGuides = props.pieceGuides;\n const pieceFocus = props.pieceFocus;\n const enableShortcuts = enableShortcuts_prop ?? true;\n\n // === Resolve kit configuration ===\n const resolvedKit = useMemo(() => resolveKit(kit ?? 'pro-studio'), [kit]);\n\n // === Validate kit in development ===\n useEffect(() => {\n // Guarded: `process` is undefined in browser bundlers that don't shim it —\n // an unguarded deref white-screens the consumer app (ReferenceError: process\n // is not defined). See the packaging browser-mount gate that pins this.\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development' && resolvedKit) {\n const result = validateKit(resolvedKit);\n if (!result.valid) {\n log.warn('Kit validation warnings:', result.errors);\n }\n if (result.warnings.length > 0) {\n log.warn('Kit validation warnings:', result.warnings);\n }\n }\n }, [resolvedKit]);\n\n // === The kernel (P1: behaviors as hooks — see ./kernel.tsx) ===\n const { emitError, handleBoundaryError } = useCanvasErrorChannel(onError);\n\n useArtboardKernel({\n artboardConfigs,\n width,\n height,\n pieceGuides,\n controlledActiveArtboard,\n onArtboardChange,\n resetDocumentOnMount,\n });\n\n const { initialElementsLoadedState } = useInitialElementsKernel({\n initialElements,\n emitError,\n });\n\n const { imageLoadingState, imageError } = useInitialImageKernel({\n initialImage,\n initialImageAlignment,\n initialImageScale,\n initialImageScaleMode,\n artboardConfigs,\n onImageLoad,\n onImageError,\n emitError,\n });\n\n useChangeCallbacksKernel({ onChange, onSelectionChange });\n\n useExportKernel(\n {\n autoExportInterval,\n autoExportConfig,\n autoExportFormat,\n autoExportAll,\n exportScale,\n maxExportSize,\n exportImageFormat,\n exportImageQuality,\n onExport,\n onExportScheduled,\n onExportStatus,\n onExportReady,\n onReady,\n hasInitialElements: !!initialElements && initialElements.length > 0,\n initialElementsLoaded: initialElementsLoadedState,\n emitError,\n },\n ref,\n );\n\n // === Render loading state ===\n if (imageLoadingState === 'loading') {\n return (\n <div\n className={className}\n style={{\n ...style,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'var(--surface, #f5f5f5)',\n }}\n >\n <Spinner size={48} />\n </div>\n );\n }\n\n // === Render error state ===\n if (imageLoadingState === 'error' && imageError) {\n return (\n <div\n className={className}\n style={{\n ...style,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: 'var(--surface, #f5f5f5)',\n color: 'var(--danger, #dc2626)',\n padding: '1rem',\n }}\n >\n <div className=\"sc:text-center\">\n <div className=\"sc:font-medium\">Failed to load image</div>\n <div className=\"sc:text-sm sc:mt-1 sc:opacity-70\">{imageError.message}</div>\n </div>\n </div>\n );\n }\n\n // === Render canvas ===\n // Note: 'sc-root' class is required for toolbar CSS to work (see src/App.css)\n // Canvas-only kits (e.g. `embed-only`) are headless embedding: the host owns\n // the surface the editor sits on, so the editor-surface fill\n // (--color-canvas-bg, painted by the <canvas> outside the artboard) must not\n // cover it with an opaque block — neutralize it to transparent. Kits that\n // render their own chrome keep the fill so the design canvas still reads as\n // a distinct surface inside that chrome. Declared FIRST in the style object\n // so a host-supplied `style` var can still override it.\n const kitIsCanvasOnly = Object.entries(resolvedKit.layout?.slots ?? {}).every(\n ([slotId, sections]) => slotId === 'canvas' || !sections || sections.length === 0\n );\n const rootSurfaceVars = kitIsCanvasOnly\n ? ({ '--color-canvas-bg': 'transparent' } as React.CSSProperties)\n : undefined;\n return (\n <KitProvider kit={resolvedKit}>\n <div className={`sc-root ${className || ''}`} style={{ ...rootSurfaceVars, ...style, position: 'relative' }}>\n <ErrorBoundary\n onError={handleBoundaryError}\n fallback={(error, reset) => (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n padding: '24px',\n minHeight: '200px',\n backgroundColor: 'var(--surface, #f5f5f5)',\n color: 'var(--foreground, #333)',\n fontFamily: 'system-ui, sans-serif',\n textAlign: 'center',\n }}\n >\n <div style={{ fontSize: '18px', fontWeight: 600, marginBottom: '8px' }}>\n Something went wrong\n </div>\n <div style={{ fontSize: '14px', opacity: 0.7, marginBottom: '16px', maxWidth: '400px' }}>\n {error.message}\n </div>\n <button\n onClick={reset}\n style={{\n padding: '8px 20px',\n backgroundColor: 'var(--primary, #333)',\n color: 'var(--primary-foreground, #fff)',\n border: 'none',\n borderRadius: '6px',\n fontSize: '14px',\n fontWeight: 500,\n cursor: 'pointer',\n }}\n >\n Try again\n </button>\n </div>\n )}\n >\n {!hideCanvas && (() => {\n // The kit's `layout.slots` decide what chrome (toolbar / panels)\n // renders around the canvas. KitLayout resolves each slot's section\n // ids to components; a canvas-only kit (e.g. `embed-only`) collapses\n // to just the bare canvas, preserving headless embedding.\n const canvasEl = (\n <Canvas\n style={{ width: '100%' }}\n fitPadding={viewPadding}\n artboardBorderRadius={artboardBorderRadius}\n fixedMargin={fixedMargin}\n maxHeight={maxHeight}\n showRotationHandle={showRotationHandle}\n enableShortcuts={enableShortcuts}\n canvasCutouts={canvasCutouts}\n pieceGuides={pieceGuides}\n pieceFocus={pieceFocus}\n />\n );\n const wrapped =\n canvasWrapperClassName || canvasWrapperStyle ? (\n <div className={canvasWrapperClassName} style={canvasWrapperStyle}>\n {canvasEl}\n </div>\n ) : (\n canvasEl\n );\n return <KitLayout kit={resolvedKit} canvas={wrapped} />;\n })()}\n {overlay}\n </ErrorBoundary>\n </div>\n </KitProvider>\n );\n});\n\nSnowconeCanvasInner.displayName = 'SnowconeCanvasInner';\n\n/**\n * SnowconeCanvas - Self-contained embeddable canvas editor\n * Wraps with ThemeProvider and EditorProvider for full functionality\n *\n * @example With imperative export\n * ```tsx\n * const canvasRef = useRef<SnowconeCanvasHandle>(null);\n *\n * const handleExport = async () => {\n * const exports = await canvasRef.current?.exportArtboards();\n * console.log('Exported:', exports);\n * };\n *\n * return <SnowconeCanvas ref={canvasRef} ... />;\n * ```\n *\n * @example Embedded in app with existing theme\n * ```tsx\n * // When your app already manages themes, use inheritTheme to prevent conflicts\n * <SnowconeCanvas inheritTheme ... />\n * ```\n */\nexport const SnowconeCanvas = forwardRef<SnowconeCanvasHandle, SnowconeCanvasProps>((props, ref) => {\n const { inheritTheme, externalProvider, services } = props;\n\n // Resolve viewPadding from either flat prop or layoutConfig (flat takes precedence)\n const resolvedViewPadding = props.viewPadding ?? props.layoutConfig?.viewPadding;\n\n return (\n <EmbedRoot\n inheritTheme={inheritTheme}\n externalProvider={externalProvider}\n services={services}\n viewPadding={resolvedViewPadding}\n >\n <SnowconeCanvasInner ref={ref} {...props} />\n </EmbedRoot>\n );\n});\n\nSnowconeCanvas.displayName = 'SnowconeCanvas';\n\nexport default SnowconeCanvas;\n"],"names":["log","createLogger","CANVAS_SECTION_ID","MIN_CHROME_WIDTH","registered","ensureSectionsRegistered","registerBuiltinSectionsSync","EffectsPanel","ExportPanel","ImagePanel","GlyphPanel","ArtboardTabs","ContextualToolbars","ZoomControls","CropPanel","KitSectionHost","children","internalsRef","useRef","registry","EditorPartsRegistry","DesignStateStore","CommerceStateStore","makeInvariantReporter","error","EditorRootInternalsContext","SlotSections","ids","jsx","id","section","getSection","process","Component","KitLayout","kit","canvas","slots","hasTopbar","_a","hasLeft","_b","hasRight","_c","hasBottombar","_d","isCanvasOnly","useMemo","rootRef","hasChrome","warnedRef","useEffect","el","check","width","observer","entries","entry","jsxs","logger","ErrorBoundary","props","errorInfo","canvasError","EmbedRoot","inheritTheme","externalProvider","services","viewPadding","tree","ThemeProvider","EditorProvider","CanvasServicesProvider","useAutoExport","options","config","historyManager","artboards","elements","onExport","onExportScheduled","isCanvasReady","managerRef","pendingExportRef","statsRef","stats","setStats","useState","onExportRef","manager","AutoExportManager","DEFAULT_AUTO_EXPORT_CONFIG","unsubscribe","subscribeToImageLoads","elementId","e","updateConfig","useCallback","newConfig","forceExport","resetStats","resetChangeDetection","extractFontFamilies","fonts","richText","span","ensureFontsLoaded","fontFamilies","pending","family","testString","resolve","waitOneFrame","useContentReady","hasInitialElements","initialElementsLoaded","isContentReady","setIsContentReady","checkingRef","cancelled","err","alignmentMap","getAlignment","value","calculateArtworkPlacement","artwork","placement","mode","mTop","mRight","mBottom","mLeft","effectiveWidth","effectiveHeight","scaleX","scaleY","coverScale","userScale","finalScale","scaledArtworkWidth","scaledArtworkHeight","alignment","x","y","overflowX","overflowY","offsetX","offsetY","xRange","yRange","DEFAULT_OPTIONS","isValidImageUrl","url","parsed","loadImageFromURL","opts","lastError","attempt","loadImageOnce","delay","timeout","img","timeoutId","resolved","cleanup","handleSuccess","w","h","handleError","errorMsg","createImageElementWithPlacement","placementOptions","loaderOptions","loadResult","placementConfig","topLeftX","topLeftY","centerX","centerY","ImageElement","_e","_f","_g","ms","calculateEffectiveScale","height","requestedScale","maxSize","scaledWidth","scaledHeight","largestSide","cappedScale","useCanvasErrorChannel","onError","onErrorRef","emitError","CanvasRenderer","handleBoundaryError","useArtboardKernel","artboardConfigs","pieceGuides","controlledActiveArtboard","onArtboardChange","resetDocumentOnMount","setElements","setSelectedId","setMultiSelection","artboardManager","refreshArtboards","useEditor","createArtboard","selectArtboard","useArtboards","initializedRef","hasInitialSyncRef","configs","defaultBg","resolveExportBackground","c","all","existingArtboards","firstConfig","firstArtboard","i","targetArtboard","ab","activeArtboard","useInitialElementsKernel","initialElements","initialElementsLoadedRef","initialElementsLoadedState","setInitialElementsLoadedState","aliveRef","deserialized","ElementFactory","allArtboards","byName","targetName","target","familySet","fontsApi","linkPromises","linkId","link","finish","families","prev","importErr","useInitialImageKernel","initialImage","initialImageAlignment","initialImageScale","initialImageScaleMode","onImageLoad","onImageError","imageLoadingState","setImageLoadingState","imageError","setImageError","prevImageRef","newElements","artboard","artboardConfig","effectiveScaleMode","result","loadErr","useChangeCallbacksKernel","onChange","onSelectionChange","selectedId","onSelectionChangeRef","onChangeRef","cb","state","json","useExportKernel","ref","autoExportInterval","autoExportConfig","autoExportFormat","autoExportAll","exportScale","maxExportSize","exportImageFormat","exportImageQuality","onExportStatus","onExportReady","onReady","setCanvasReady","exportArtboard","exportAllArtboards","exportArtboardAsBlob","exportAllArtboardsAsBlobs","useExport","lastExportRef","exportCounterRef","useImperativeHandle","format","effectiveScale","abEffectiveScale","exportOptions","resultsById","resultsByName","artboardId","blob","dataUrl","triggerExport","exportNumber","startTime","activeAb","statusArtboardId","results","allResults","data","errorMessage","duration","handleExportScheduled","hasCalledOnReadyRef","SnowconeCanvasInner","forwardRef","_exportConfig","_imageConfig","_layoutConfig","autoExportInterval_prop","className","style","enableShortcuts_prop","overlay","artboardBorderRadius","fixedMargin","maxHeight","showRotationHandle","hideCanvas","canvasWrapperClassName","canvasWrapperStyle","canvasCutouts","pieceFocus","enableShortcuts","resolvedKit","resolveKit","validateKit","Spinner","rootSurfaceVars","slotId","sections","KitProvider","reset","canvasEl","Canvas","SnowconeCanvas","resolvedViewPadding"],"mappings":"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"}