@snowcone-app/canvas 0.37.1 → 0.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ERROR_CONTRACT.md +39 -8
- package/STYLING_CONTRACT.md +24 -2
- package/dist/{CanvasRenderer-CTN4j-uO.js → CanvasRenderer-D-E5WWLm.js} +4 -4
- package/dist/{CanvasRenderer-CTN4j-uO.js.map → CanvasRenderer-D-E5WWLm.js.map} +1 -1
- package/dist/{CanvasStateV1-DLE5d1-d.js → CanvasStateV1-C9LQt2J0.js} +7 -7
- package/dist/{CanvasStateV1-DLE5d1-d.js.map → CanvasStateV1-C9LQt2J0.js.map} +1 -1
- package/dist/{ContextualToolbars-CG5Gmv_i.js → ContextualToolbars-C62As-xY.js} +202 -202
- package/dist/{ContextualToolbars-CG5Gmv_i.js.map → ContextualToolbars-C62As-xY.js.map} +1 -1
- package/dist/EditorCanvasImpl-BnxEbr2p.js +144 -0
- package/dist/EditorCanvasImpl-BnxEbr2p.js.map +1 -0
- package/dist/{ExportManager-CDX5_up8.js → ExportManager-BbBfO22S.js} +45 -45
- package/dist/{ExportManager-CDX5_up8.js.map → ExportManager-BbBfO22S.js.map} +1 -1
- package/dist/{FontAnalyzer-CEYA_6fY.js → FontAnalyzer-DWHS41ba.js} +128 -128
- package/dist/FontAnalyzer-DWHS41ba.js.map +1 -0
- package/dist/{ImagePanel-DLxIDKV-.js → ImagePanel-BaX0UE-L.js} +57 -57
- package/dist/{ImagePanel-DLxIDKV-.js.map → ImagePanel-BaX0UE-L.js.map} +1 -1
- package/dist/SnowconeCanvas-CWSPyqqj.js +1112 -0
- package/dist/SnowconeCanvas-CWSPyqqj.js.map +1 -0
- package/dist/advanced.mjs +138 -138
- package/dist/api/placement.d.ts +3 -0
- package/dist/components/editor/EditorPlacements.d.ts +19 -6
- package/dist/components/editor/RootInternals.d.ts +7 -0
- package/dist/components/editor/designStateStore.d.ts +18 -0
- package/dist/components/editor/index.d.ts +3 -1
- package/dist/components/editor/placementLinks.d.ts +52 -0
- package/dist/components/editor/usePlacements.d.ts +28 -4
- package/dist/components/embed-realtime/context.d.ts +20 -4
- package/dist/components/embed-realtime/usePieceGuideBundle.d.ts +1 -0
- package/dist/components/embed-realtime/validateEmbed.d.ts +6 -3
- package/dist/{custom-icons-D51KIAfz.js → custom-icons-o1U13Vcg.js} +14 -14
- package/dist/{custom-icons-D51KIAfz.js.map → custom-icons-o1U13Vcg.js.map} +1 -1
- package/dist/{design-builder-CqgTpp9B.js → design-builder-CoCtjIwj.js} +86 -86
- package/dist/{design-builder-CqgTpp9B.js.map → design-builder-CoCtjIwj.js.map} +1 -1
- package/dist/{embed-CLz3CTcX.js → embed-SOFUZlGB.js} +843 -729
- package/dist/embed-SOFUZlGB.js.map +1 -0
- package/dist/embed.mjs +28 -28
- package/dist/errors/editorError.d.ts +7 -2
- package/dist/{guards-DldCuuJq.js → guards-DMmhw_Ow.js} +4 -4
- package/dist/{guards-DldCuuJq.js.map → guards-DMmhw_Ow.js.map} +1 -1
- package/dist/index.mjs +21 -21
- package/dist/internals.mjs +108 -108
- package/dist/internals.mjs.map +1 -1
- package/dist/placement-Br5jI_-u.js +90 -0
- package/dist/placement-Br5jI_-u.js.map +1 -0
- package/dist/placement.d.ts +2 -0
- package/dist/placement.mjs +5 -0
- package/dist/placement.mjs.map +1 -0
- package/dist/template-fields-CBLHgW7G.js +556 -0
- package/dist/template-fields-CBLHgW7G.js.map +1 -0
- package/dist/testing.mjs +24 -24
- package/dist/{useCommands-CQi4Pyd2.js → useCommands-x8J7_Mzy.js} +262 -262
- package/dist/{useCommands-CQi4Pyd2.js.map → useCommands-x8J7_Mzy.js.map} +1 -1
- package/dist/usePieceGuideBundle-C7r06a_x.js +65 -0
- package/dist/usePieceGuideBundle-C7r06a_x.js.map +1 -0
- package/dist/{validation-THw5W6N_.js → validation-BG-V98h0.js} +846 -846
- package/dist/validation-BG-V98h0.js.map +1 -0
- package/example/package.json +1 -1
- package/package.json +7 -2
- package/dist/EditorCanvasImpl-BRdFWZA5.js +0 -81
- package/dist/EditorCanvasImpl-BRdFWZA5.js.map +0 -1
- package/dist/FontAnalyzer-CEYA_6fY.js.map +0 -1
- package/dist/SnowconeCanvas-BaFR8QXs.js +0 -1196
- package/dist/SnowconeCanvas-BaFR8QXs.js.map +0 -1
- package/dist/embed-CLz3CTcX.js.map +0 -1
- package/dist/template-fields-D8uOutD4.js +0 -505
- package/dist/template-fields-D8uOutD4.js.map +0 -1
- package/dist/usePieceGuideBundle-BUQKb0Ja.js +0 -61
- package/dist/usePieceGuideBundle-BUQKb0Ja.js.map +0 -1
- package/dist/validation-THw5W6N_.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design-builder-CqgTpp9B.js","sources":["../src/contexts/BoundLayerGuardContext.ts","../src/hooks/useCommandHistory.ts","../src/hooks/useElementProperties.ts","../src/utils/selectionPreservation.ts","../src/utils/inkLuminance.ts","../src/contexts/ViewportContext.tsx","../src/contexts/SelectionContext.tsx","../src/contexts/HistoryContext.tsx","../src/contexts/ToolStateContext.tsx","../src/contexts/ElementsContext.tsx","../src/contexts/CommandContext.tsx","../src/contexts/EditorContext.tsx","../src/testing/editorErrors.ts","../src/rendering/serialize-for-server.ts","../src/design/design-builder.ts"],"sourcesContent":["/**\n * Bound-layer mutation guard (ADR-0085 §3, enforced in P1).\n *\n * While an `Editor.TextField` is registered for layer N, renaming or deleting\n * layer N must be impossible: the editor UI suppresses the affordances, and\n * the programmatic command path throws `E_BOUND_LAYER_MUTATION` naming the\n * bound part — so an end-user gesture can never detonate the prod error card\n * (the invalid state is unreachable from the UI), while agent/host code gets\n * a loud structured throw.\n *\n * `Editor.Root` provides its parts registry through this context ABOVE\n * `EditorProvider`, so the command layer (`useCommandHistory`) and the chrome\n * (LayersPanel, keyboard shortcuts) can consult it. Without a Root (legacy\n * standalone `SnowconeCanvas`) the context is null and nothing changes.\n *\n * This module is deliberately dependency-free: it sits in the always-mounted\n * Editor.Root graph (renderer-free, CI-gated) and is imported by both the\n * contexts layer and the editor parts.\n */\n\nimport { createContext, useCallback, useContext, useSyncExternalStore } from 'react';\n\nexport interface BoundLayerGuard {\n /** Subscribe to registry changes (TextFields mount/unmount). */\n subscribe(listener: () => void): () => void;\n /** Monotonic version for `useSyncExternalStore`. */\n getVersion(): number;\n /** The part bound to a layer name, or null when the layer is unbound. */\n getBoundLayer(name: string): { part: string } | null;\n}\n\nexport const BoundLayerGuardContext = createContext<BoundLayerGuard | null>(null);\n\nconst noopSubscribe = (): (() => void) => () => {};\nconst zeroVersion = (): number => 0;\n\n/**\n * Reactive bound-layer test for UI suppression: re-renders when TextFields\n * mount/unmount, so rename/delete affordances appear the moment a layer\n * stops being bound. Always returns false outside an `<Editor.Root>`.\n */\nexport function useIsLayerBound(): (name: string | undefined) => boolean {\n const guard = useContext(BoundLayerGuardContext);\n const version = useSyncExternalStore(\n guard ? guard.subscribe : noopSubscribe,\n guard ? guard.getVersion : zeroVersion,\n guard ? guard.getVersion : zeroVersion,\n );\n return useCallback(\n (name: string | undefined) => Boolean(guard && name && guard.getBoundLayer(name)),\n // `version` is the reactivity key: a registry change must mint a new\n // callback so memoized consumers re-evaluate.\n [guard, version],\n );\n}\n","/**\n * useCommandHistory - React hook for hybrid undo/redo functionality\n * Now supports artboard-level and element-level operations\n */\n\nimport { useState, useCallback, useContext, useRef, useEffect } from 'react';\nimport { createLogger } from '../utils/logger.js';\nimport { BoundLayerGuardContext } from '../contexts/BoundLayerGuardContext.js';\nimport { EditorError } from '../errors/editorError.js';\nimport {\n UpdateElementCommand,\n AddElementCommand,\n RemoveElementCommand,\n CreateArtboardCommand,\n DeleteArtboardCommand,\n UpdateArtboardCommand,\n ReorderElementCommand,\n} from '../core/CommandHistory.js';\nimport { HybridHistoryManager } from '../core/HybridHistoryManager.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\n\nconst logger = createLogger('useCommandHistory');\n\n/** Union of all element types */\ntype AnyElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\n/**\n * Hook to manage hybrid command history for undo/redo\n * Supports both global (artboard) and per-artboard (element) operations\n */\nexport function useCommandHistory(\n elements: AnyElement[],\n setElements: React.Dispatch<React.SetStateAction<AnyElement[]>>,\n artboardManager: ArtboardManager,\n onArtboardsChange?: () => void\n) {\n const historyManagerRef = useRef(new HybridHistoryManager(artboardManager));\n\n // Bound-layer mutation guard (ADR-0085 §3 / E_BOUND_LAYER_MUTATION): while\n // an <Editor.TextField> is mounted for layer N, renaming or deleting N via\n // the command path throws. Null outside an <Editor.Root> — the legacy\n // standalone editor is unchanged. The editor UI additionally suppresses the\n // affordances (LayersPanel, keyboard delete) so an end-user gesture can\n // never reach this throw; it exists for the programmatic path.\n const boundLayerGuard = useContext(BoundLayerGuardContext);\n const assertLayerMutable = useCallback(\n (element: AnyElement | undefined | null, operation: 'rename' | 'delete') => {\n const name = element?.name;\n if (!boundLayerGuard || !name) return;\n const bound = boundLayerGuard.getBoundLayer(name);\n if (bound) {\n throw new EditorError(\n 'E_BOUND_LAYER_MUTATION',\n `cannot ${operation} layer \"${name}\" — a mounted <${bound.part} layer={design.layer(\"${name}\")}> ` +\n `is bound to it. Unmount that field first, or keep the layer and set its text through the ` +\n `field / its binding instead.`,\n { layer: name, part: bound.part, operation, docSlug: 'embed-mode' },\n );\n }\n },\n [boundLayerGuard],\n );\n const [historyState, setHistoryState] = useState({\n canUndo: false,\n canRedo: false,\n // Per-artboard state for the active artboard\n canUndoActiveArtboard: false,\n canRedoActiveArtboard: false,\n });\n\n // Update history state\n const updateHistoryState = useCallback(() => {\n const manager = historyManagerRef.current;\n const activeArtboardId = artboardManager.getActiveArtboardId();\n setHistoryState({\n canUndo: manager.canUndo(),\n canRedo: manager.canRedo(),\n // Per-artboard state - only check active artboard's history\n canUndoActiveArtboard: activeArtboardId ? manager.canUndoArtboard(activeArtboardId) : false,\n canRedoActiveArtboard: activeArtboardId ? manager.canRedoArtboard(activeArtboardId) : false,\n });\n }, [artboardManager]);\n\n /**\n * Execute an element update with undo support (on active artboard)\n */\n const executeElementUpdate = useCallback(\n (\n oldElement: AnyElement | undefined | null,\n newElement: AnyElement\n ) => {\n // A name change through the update path is a layer RENAME.\n if (oldElement && oldElement.name !== newElement.name) {\n assertLayerMutable(oldElement, 'rename');\n }\n const command = new UpdateElementCommand(\n newElement.id,\n oldElement || null,\n newElement,\n (element) => {\n setElements((prevElements) => prevElements.map((s) => (s.id === element.id ? element as AnyElement : s)));\n }\n );\n\n // Execute on the artboard that contains this element\n const artboardId = artboardManager.getArtboardIdForElement(newElement.id);\n if (artboardId) {\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n } else {\n // Fallback: execute on active artboard\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId) {\n historyManagerRef.current.executeOnArtboard(activeArtboardId, command);\n }\n }\n\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState, assertLayerMutable]\n );\n\n /**\n * Execute an add element command\n */\n const executeAddElement = useCallback(\n (element: AnyElement, artboardId?: string, insertAfterElementId?: string, insertBeforeElementId?: string) => {\n const targetArtboardId = artboardId || artboardManager.getActiveArtboardId();\n if (!targetArtboardId) {\n logger.warn('No active artboard to add element to');\n return;\n }\n\n const command = new AddElementCommand(\n element,\n (addedElement) => {\n const newElement = addedElement as AnyElement;\n setElements((prevElements) => {\n // Insert before a specific element (e.g., promoting from group to appear below it visually)\n if (insertBeforeElementId) {\n const insertIndex = prevElements.findIndex((e) => e.id === insertBeforeElementId);\n if (insertIndex !== -1) {\n const newElements = [...prevElements];\n newElements.splice(insertIndex, 0, newElement);\n return newElements;\n }\n }\n // If insertAfterElementId is provided, insert after that element\n if (insertAfterElementId) {\n // First check if it's a top-level element\n const insertIndex = prevElements.findIndex((e) => e.id === insertAfterElementId);\n if (insertIndex !== -1) {\n // Insert after the found element (layers are displayed in reverse)\n const newElements = [...prevElements];\n newElements.splice(insertIndex + 1, 0, newElement);\n return newElements;\n }\n\n // If not found at top level, check if it's a child within a group\n // In this case, add the new element as a child of that group\n for (let i = 0; i < prevElements.length; i++) {\n const element = prevElements[i];\n if (element instanceof GroupElement && element.children) {\n const childIndex = element.children.findIndex((child) => child.id === insertAfterElementId);\n if (childIndex !== -1) {\n // Found the parent group - add new element as a child after the selected child\n const updatedGroup = element.clone();\n updatedGroup.children.splice(childIndex + 1, 0, newElement);\n\n const newElements = [...prevElements];\n newElements[i] = updatedGroup;\n return newElements;\n }\n }\n }\n }\n // Default: append to end\n return [...prevElements, newElement];\n });\n artboardManager.addElementToArtboard(addedElement.id, targetArtboardId);\n },\n (id: string) => {\n setElements((prevElements) => prevElements.filter((s) => s.id !== id));\n artboardManager.removeElementFromArtboard(id);\n }\n );\n\n historyManagerRef.current.executeOnArtboard(targetArtboardId, command);\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState]\n );\n\n /**\n * Execute a remove element command\n */\n const executeRemoveElement = useCallback(\n (element: AnyElement) => {\n assertLayerMutable(element, 'delete');\n const artboardId = artboardManager.getArtboardIdForElement(element.id);\n if (!artboardId) {\n logger.warn('Element not found in any artboard');\n return;\n }\n\n const command = new RemoveElementCommand(\n element,\n (addedElement) => {\n setElements((prevElements) => [...prevElements, addedElement as AnyElement]);\n artboardManager.addElementToArtboard(addedElement.id, artboardId);\n },\n (id: string) => {\n setElements((prevElements) => prevElements.filter((s) => s.id !== id));\n artboardManager.removeElementFromArtboard(id);\n }\n );\n\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState, assertLayerMutable]\n );\n\n /**\n * Execute a create artboard command (global operation)\n */\n const executeCreateArtboard = useCallback(\n (artboard: ArtboardElement) => {\n const command = new CreateArtboardCommand(artboard, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute a delete artboard command (global operation)\n */\n const executeDeleteArtboard = useCallback(\n (artboard: ArtboardElement) => {\n const command = new DeleteArtboardCommand(artboard, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute an update artboard command (global operation)\n */\n const executeUpdateArtboard = useCallback(\n (artboardId: string, oldProperties: Partial<ArtboardElement>, newProperties: Partial<ArtboardElement>) => {\n const command = new UpdateArtboardCommand(artboardId, oldProperties, newProperties, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute a batch of commands as a single undo entry on the active artboard.\n * All commands are grouped into one CompoundCommand so that undo/redo\n * treats the entire batch as a single atomic operation.\n *\n * No-op if commands array is empty.\n */\n const executeCommandBatch = useCallback(\n (commands: Array<{ execute: () => void; undo: () => void }>) => {\n if (commands.length === 0) {\n return;\n }\n\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n logger.warn('No active artboard for batch execution');\n return;\n }\n\n // Wrap plain objects as Command instances\n const wrappedCommands = commands.map((cmd) => {\n const command = new (class extends Object {\n execute() { cmd.execute(); }\n undo() { cmd.undo(); }\n })();\n return command as import('../core/CommandHistory.js').Command;\n });\n\n historyManagerRef.current.executeBatchOnArtboard(activeArtboardId, wrappedCommands);\n updateHistoryState();\n },\n [artboardManager, updateHistoryState]\n );\n\n /**\n * Smart undo - handles both global and artboard operations\n */\n const undo = useCallback(() => {\n if (historyManagerRef.current.undo()) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [updateHistoryState, onArtboardsChange]);\n\n /**\n * Smart redo - handles both global and artboard operations\n */\n const redo = useCallback(() => {\n if (historyManagerRef.current.redo()) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [updateHistoryState, onArtboardsChange]);\n\n /**\n * Undo only on the active artboard (element operations only)\n */\n const undoActiveArtboard = useCallback(() => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId && historyManagerRef.current.undoArtboard(activeArtboardId)) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [artboardManager, updateHistoryState, onArtboardsChange]);\n\n /**\n * Redo only on the active artboard (element operations only)\n */\n const redoActiveArtboard = useCallback(() => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId && historyManagerRef.current.redoArtboard(activeArtboardId)) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [artboardManager, updateHistoryState, onArtboardsChange]);\n\n /**\n * Clear all history\n */\n const clearHistory = useCallback(() => {\n historyManagerRef.current.clear();\n updateHistoryState();\n }, [updateHistoryState]);\n\n /**\n * Clear history for a specific artboard\n */\n const clearArtboardHistory = useCallback(\n (artboardId: string) => {\n historyManagerRef.current.clearArtboardHistory(artboardId);\n updateHistoryState();\n },\n [updateHistoryState]\n );\n\n // Track active artboard changes to update undo/redo state\n // This is needed because canUndoActiveArtboard/canRedoActiveArtboard depend on which artboard is active\n const activeArtboardIdRef = useRef<string | null>(null);\n useEffect(() => {\n const currentActiveId = artboardManager.getActiveArtboardId();\n if (currentActiveId !== activeArtboardIdRef.current) {\n activeArtboardIdRef.current = currentActiveId;\n updateHistoryState();\n }\n });\n\n /**\n * Execute a reorder elements command\n */\n const executeReorderElement = useCallback(\n (draggedId: string, targetId: string, position: 'before' | 'after') => {\n // Get current element order\n const currentOrder = elements.map((e) => e.id);\n\n // Find which artboard these elements belong to\n const artboardId = artboardManager.getArtboardIdForElement(draggedId);\n if (!artboardId) {\n logger.warn('Element not found in any artboard');\n return;\n }\n\n const command = new ReorderElementCommand(draggedId, targetId, position, currentOrder, (newOrder: string[]) => {\n // Reorder elements based on the new order\n setElements((prevElements) => {\n const elementMap = new Map(prevElements.map((e) => [e.id, e]));\n return newOrder.map((id) => elementMap.get(id)).filter(Boolean) as AnyElement[];\n });\n });\n\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n updateHistoryState();\n },\n [elements, setElements, artboardManager, updateHistoryState]\n );\n\n // Keyboard shortcuts for undo/redo (removed - will be handled in App.tsx)\n // This allows App.tsx to handle all keyboard shortcuts in one place\n\n return {\n // Element operations\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n\n // Batch operations\n executeCommandBatch,\n\n // Artboard operations\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n\n // Undo/redo (smart - considers last operation and active artboard)\n undo,\n redo,\n // Undo/redo for active artboard only (element operations)\n undoActiveArtboard,\n redoActiveArtboard,\n clearHistory,\n clearArtboardHistory,\n\n // State (smart - considers last operation)\n canUndo: historyState.canUndo,\n canRedo: historyState.canRedo,\n // State for active artboard only\n canUndoActiveArtboard: historyState.canUndoActiveArtboard,\n canRedoActiveArtboard: historyState.canRedoActiveArtboard,\n historyManager: historyManagerRef.current,\n };\n}\n\nexport default useCommandHistory;\n","/**\n * useElementProperties - Custom hook for managing element property state\n * Reduces state management complexity in App.jsx\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { MIN_FONT_SIZE, MAX_FONT_SIZE } from '../constants.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { CircleTransform } from '../transforms/CircleTransform.js';\nimport { FontAnalyzer } from '../utils/FontAnalyzer.js';\nimport type { TextAlign } from '../types/index.js';\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\n/** Type guard to check if element has text properties */\nfunction hasTextProperties(element: EditorElement): element is TextElement {\n return element && 'fontSize' in element && 'color' in element && 'fontFamily' in element;\n}\n\n/**\n * Hook to manage element properties and UI synchronization\n * @param selectedElement - Currently selected element\n * @param onElementUpdate - Callback to update element\n * @returns Property getters and setters\n */\nexport function useElementProperties(\n selectedElement: EditorElement | undefined,\n onElementUpdate: (element: EditorElement) => void\n) {\n const [fontSize, setFontSize] = useState<number>(32);\n const [fontColor, setFontColor] = useState<string>('#333333');\n const [fontFamily, setFontFamily] = useState<string>('Arial');\n const [textAlign, setTextAlign] = useState<TextAlign>('center');\n\n // Sync UI state with selected element\n useEffect(() => {\n if (selectedElement && hasTextProperties(selectedElement)) {\n // Handle circle mode's effective font size\n const newFontSize = selectedElement.transformType === 'circle' && 'getEffectiveFontSize' in selectedElement\n ? Math.round((selectedElement as CircleTransform).getEffectiveFontSize())\n : Math.round(selectedElement.fontSize);\n\n const newFontColor = selectedElement.color;\n const newFontFamily = selectedElement.fontFamily;\n const newTextAlign = selectedElement.textAlign;\n\n // Only update state if values actually changed to prevent unnecessary re-renders\n setFontSize(prev => prev !== newFontSize ? newFontSize : prev);\n setFontColor(prev => prev !== newFontColor ? newFontColor : prev);\n setFontFamily(prev => prev !== newFontFamily ? newFontFamily : prev);\n setTextAlign(prev => prev !== newTextAlign ? newTextAlign : prev);\n }\n }, [selectedElement]);\n\n // Update font size\n const updateFontSize = useCallback(\n (delta: number) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n // Allow any size increment, not restricted to FONT_SIZES array\n const newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, fontSize + delta));\n setFontSize(newSize);\n\n const updatedElement = selectedElement.clone();\n if (updatedElement.transformType === 'circle' && 'setEffectiveFontSize' in updatedElement) {\n (updatedElement as CircleTransform).setEffectiveFontSize(newSize);\n } else {\n updatedElement.setFontSize(newSize);\n }\n\n onElementUpdate(updatedElement);\n },\n [selectedElement, fontSize, onElementUpdate]\n );\n\n // Set font size directly\n const setFontSizeValue = useCallback(\n (newSize: number) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setFontSize(newSize);\n\n const updatedElement = selectedElement.clone();\n if (updatedElement.transformType === 'circle' && 'setEffectiveFontSize' in updatedElement) {\n (updatedElement as CircleTransform).setEffectiveFontSize(newSize);\n } else {\n updatedElement.setFontSize(newSize);\n }\n\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update font color\n const updateFontColor = useCallback(\n (newColor: string) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setFontColor(newColor);\n\n const updatedElement = selectedElement.clone();\n // Set element-level color and clear all span colors so they inherit\n // This ensures ALL text changes color, not just text matching the old color\n updatedElement.color = newColor;\n if (updatedElement.richText) {\n for (const span of updatedElement.richText.spans) {\n span.style.color = undefined;\n }\n }\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update font family\n const updateFontFamily = useCallback(\n (newFontFamily: string) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n // Clear old font from cache if switching fonts (prevents iOS Safari memory crashes)\n const oldFontFamily = selectedElement.fontFamily;\n if (oldFontFamily !== newFontFamily) {\n FontAnalyzer.clearCache(oldFontFamily);\n }\n\n setFontFamily(newFontFamily);\n\n const updatedElement = selectedElement.clone();\n updatedElement.fontFamily = newFontFamily;\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update text alignment\n const updateTextAlign = useCallback(\n (alignment: TextAlign) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setTextAlign(alignment);\n\n const updatedElement = selectedElement.clone();\n updatedElement.textAlign = alignment;\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n return {\n // State\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n // State setters (for UI only)\n setFontSize,\n setFontColor,\n setFontFamily,\n setTextAlign,\n // Update functions (update element + UI)\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n };\n}\n\nexport default useElementProperties;\n","/**\n * Selection Preservation Utility\n *\n * Provides a declarative way to mark UI elements that should NOT\n * trigger canvas deselection when clicked.\n *\n * Usage:\n * 1. Add data-preserve-selection attribute to any UI container\n * 2. The document-level handler in EditorContext checks for this attribute\n *\n * @example\n * // Direct attribute\n * <div data-preserve-selection>\n * <MyToolbar />\n * </div>\n *\n * // Spread helper\n * <div {...preserveSelectionProps}>\n * <MySettings />\n * </div>\n */\n\n/** Data attribute name for marking selection-preserving elements */\nexport const PRESERVE_SELECTION_ATTR = 'data-preserve-selection';\n\n/**\n * Check if a click event should preserve the current selection.\n * Walks up the DOM tree from the click target checking for the\n * data-preserve-selection attribute.\n *\n * Performance: O(depth) where depth is typically 10-20 nodes.\n * This is negligible compared to other click handling operations.\n *\n * @param target - The event target element\n * @returns true if selection should be preserved, false otherwise\n */\nexport function shouldPreserveSelection(target: EventTarget | null): boolean {\n // Handle both HTMLElement and SVGElement (icons inside buttons)\n if (!(target instanceof Element)) {\n return false;\n }\n\n // Start from the target, walking up through SVG and HTML elements\n let element: Element | null = target;\n const path: string[] = [];\n\n while (element) {\n // Build path for logging\n const id = element.id ? `#${element.id}` : '';\n const classes = element.className ? `.${String(element.className).split(' ').slice(0, 2).join('.')}` : '';\n path.push(`${element.tagName.toLowerCase()}${id}${classes}`);\n\n // Check for our custom attribute\n if (element.hasAttribute(PRESERVE_SELECTION_ATTR)) {\n return true;\n }\n\n // Check for common dialog/popover/menu patterns (third-party components)\n const role = element.getAttribute('role');\n if (role === 'dialog' || role === 'alertdialog' || role === 'menu' || role === 'listbox') {\n return true;\n }\n\n // Check for Radix/HeroUI popover patterns\n const dataSlot = element.getAttribute('data-slot');\n if (dataSlot && (dataSlot.includes('popover') || dataSlot.includes('dialog'))) {\n return true;\n }\n\n // Check for Radix data attributes (portaled content)\n if (element.hasAttribute('data-radix-popper-content-wrapper')) {\n return true;\n }\n\n element = element.parentElement;\n }\n\n return false;\n}\n\n/**\n * React props helper - spread this on containers that should preserve selection\n * @example <div {...preserveSelectionProps}>...</div>\n */\nexport const preserveSelectionProps = {\n [PRESERVE_SELECTION_ATTR]: '',\n} as const;\n","/**\n * inkLuminance - Shared colour-luminance helpers.\n *\n * Quick \"is this ink dark / light\" checks used to decide whether to\n * flip an author-supplied colour for the current theme/background. We\n * only need to handle hex literals (`#rgb`, `#rrggbb`) and `rgb(...)` —\n * the formats Loom emits. CSS colour names / `hsl()` fall through to\n * `null` (\"neither\"), which lets callers skip any flip — the safe\n * choice (no surprising visual change for unfamiliar inputs).\n *\n * Originally lived in PieceGuideRenderer; extracted so element-creation\n * defaults (artboard-aware ink) can reuse the same luminance math.\n *\n * @internal — exposed for tests; not part of the package's public\n * API. Don't import these from outside the canvas package.\n *\n * @module\n */\n\n/** @internal */\nexport function isInkDark(css: string): boolean {\n const luma = inkLuminance(css);\n return luma !== null && luma < 0.5;\n}\n\n/** @internal */\nexport function isInkLight(css: string): boolean {\n const luma = inkLuminance(css);\n return luma !== null && luma >= 0.5;\n}\n\n/** @internal */\nexport function inkLuminance(css: string): number | null {\n const trimmed = css.trim();\n // #rgb / #rrggbb\n if (trimmed.startsWith('#')) {\n const hex = trimmed.slice(1);\n let r: number, g: number, b: number;\n if (hex.length === 3) {\n r = parseInt(hex[0] + hex[0], 16);\n g = parseInt(hex[1] + hex[1], 16);\n b = parseInt(hex[2] + hex[2], 16);\n } else if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16);\n g = parseInt(hex.slice(2, 4), 16);\n b = parseInt(hex.slice(4, 6), 16);\n } else return null;\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;\n return relLuma(r, g, b);\n }\n // rgb(...) / rgba(...)\n const m = trimmed.match(/^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/);\n if (m) return relLuma(Number(m[1]), Number(m[2]), Number(m[3]));\n return null;\n}\n\nfunction relLuma(r: number, g: number, b: number): number {\n const lin = (c: number) => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : Math.pow((cs + 0.055) / 1.055, 2.4);\n };\n return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);\n}\n\n// ── Element-creation default ink ─────────────────────────────────────────────\n\n/** Default text ink for light artboard backgrounds. */\nexport const DEFAULT_TEXT_INK = '#333333';\n/** Default text ink for dark artboard backgrounds. */\nexport const DEFAULT_TEXT_INK_ON_DARK = '#f4f4f5';\n\n/**\n * Pick the default text colour for a newly created element based on the\n * artboard background it lands on: dark backgrounds get light ink so new\n * text is visible; everything else (light, transparent, unknown formats)\n * keeps the classic dark ink.\n *\n * Used ONLY by the element-creation default path (handleAddElement) —\n * bindings, templates, and deserialization always honour explicit colours.\n */\nexport function defaultTextColorForBackground(\n backgroundColor: string | null | undefined\n): string {\n if (backgroundColor && isInkDark(backgroundColor)) {\n return DEFAULT_TEXT_INK_ON_DARK;\n }\n return DEFAULT_TEXT_INK;\n}\n","/**\n * ViewportContext - Manages zoom and pan state for the canvas viewport.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on viewport state (e.g., layers panel, effects panel).\n * Components that only need zoom/pan information should use `useViewport()`\n * instead of `useEditor()`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\n\n/** Pan offset for infinite canvas viewport */\nexport interface PanOffset {\n x: number;\n y: number;\n}\n\n/** Value exposed by ViewportContext. */\nexport interface ViewportContextValue {\n /** Current zoom level (1.0 = 100%) */\n zoom: number;\n /** Current pan offset in world coordinates */\n panOffset: PanOffset;\n /** Whether the user is currently panning */\n isPanning: boolean;\n /** Increment zoom by one step */\n zoomIn: () => void;\n /** Decrement zoom by one step */\n zoomOut: () => void;\n /** Zoom to fit the active artboard in the viewport */\n zoomToFit: () => void;\n /** Reset zoom to 100% and pan offset to origin */\n resetView: () => void;\n /** Set zoom to an exact value */\n setZoom: React.Dispatch<React.SetStateAction<number>>;\n /** Set pan offset to an exact value */\n setPanOffset: React.Dispatch<React.SetStateAction<PanOffset>>;\n /** Set whether the user is currently panning */\n setIsPanning: React.Dispatch<React.SetStateAction<boolean>>;\n\n /**\n * User-driven zoom multiplier applied on top of the auto-fit zoom\n * the embedded canvas computes from container size. 1.0 = exactly\n * fit-zoom (no extra zoom). >1 = zoomed in for fine-detail editing.\n * Composes with `zoom` (which is the *effective* zoom = fit × user)\n * so legacy consumers reading `zoom` see one value.\n */\n userZoom: number;\n setUserZoom: React.Dispatch<React.SetStateAction<number>>;\n /** Reset userZoom to 1.0 and panOffset to origin (returns to fit). */\n resetUserView: () => void;\n}\n\nconst ViewportContext = createContext<ViewportContextValue | null>(null);\n\n// Zoom limits\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 4.0;\nconst ZOOM_STEP = 0.25;\n\n/** Props for {@link ViewportProvider}. */\nexport interface ViewportProviderProps {\n children: ReactNode;\n /** ArtboardManager instance, used by zoomToFit to read active artboard dimensions. */\n artboardManager: ArtboardManager;\n /** Ref to the canvas element, used by zoomToFit to measure the container. */\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n /**\n * Controls how much of the container the artboard fills when using zoomToFit.\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 */\n viewPadding?: number;\n}\n\n/**\n * Provides viewport state (zoom, pan) and control functions to all descendants.\n *\n * Must be composed inside EditorProvider or used standalone for viewport-only\n * scenarios. Most consumers should use the `useViewport()` hook.\n */\nexport const ViewportProvider: React.FC<ViewportProviderProps> = ({\n children,\n artboardManager,\n canvasRef,\n viewPadding = 0.9,\n}) => {\n const [zoom, setZoom] = useState<number>(1.0);\n const [panOffset, setPanOffset] = useState<PanOffset>({ x: 0, y: 0 });\n const [isPanning, setIsPanning] = useState<boolean>(false);\n const [userZoom, setUserZoom] = useState<number>(1.0);\n\n const resetUserView = useCallback(() => {\n setUserZoom(1.0);\n setPanOffset({ x: 0, y: 0 });\n }, []);\n\n const zoomIn = useCallback(() => {\n setZoom((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM));\n }, []);\n\n const zoomOut = useCallback(() => {\n setZoom((prev) => Math.max(prev - ZOOM_STEP, MIN_ZOOM));\n }, []);\n\n const zoomToFit = useCallback(() => {\n const artboard = artboardManager.getActiveArtboard();\n if (!artboard || !canvasRef.current) return;\n\n // Walk up the DOM tree to find a container with actual dimensions\n let container = canvasRef.current.parentElement;\n let containerWidth = 0;\n let containerHeight = 0;\n\n while (container) {\n containerWidth = container.clientWidth;\n containerHeight = container.clientHeight;\n if (containerWidth > 0 && containerHeight > 0) {\n break;\n }\n container = container.parentElement;\n }\n\n if (!containerWidth || !containerHeight) return;\n\n const fitZoom = Math.min(\n (containerWidth * viewPadding) / artboard.width,\n (containerHeight * viewPadding) / artboard.height,\n 1.0 // Never zoom in beyond 100%\n );\n\n setZoom(fitZoom);\n setPanOffset({ x: 0, y: 0 });\n }, [artboardManager, canvasRef, viewPadding]);\n\n const resetView = useCallback(() => {\n setZoom(1.0);\n setPanOffset({ x: 0, y: 0 });\n }, []);\n\n const value = useMemo<ViewportContextValue>(\n () => ({\n zoom,\n panOffset,\n isPanning,\n zoomIn,\n zoomOut,\n zoomToFit,\n resetView,\n setZoom,\n setPanOffset,\n setIsPanning,\n userZoom,\n setUserZoom,\n resetUserView,\n }),\n [zoom, panOffset, isPanning, zoomIn, zoomOut, zoomToFit, resetView, userZoom, resetUserView]\n );\n\n return <ViewportContext.Provider value={value}>{children}</ViewportContext.Provider>;\n};\n\n/**\n * Access viewport state: zoom, pan, and control functions.\n *\n * Prefer this over `useEditor()` in components that only need viewport information\n * (e.g., zoom controls, minimap overlays). Changes to non-viewport state won't\n * trigger re-renders in components using this hook.\n *\n * @throws {Error} If called outside of a `ViewportProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function ZoomBar() {\n * const { zoom, zoomIn, zoomOut, zoomToFit } = useViewportContext();\n * return (\n * <div>\n * <button onClick={zoomOut}>-</button>\n * <span>{Math.round(zoom * 100)}%</span>\n * <button onClick={zoomIn}>+</button>\n * <button onClick={zoomToFit}>Fit</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useViewportContext(): ViewportContextValue {\n const context = useContext(ViewportContext);\n if (!context) {\n throw new Error('useViewportContext must be used within a ViewportProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * SelectionContext - Manages element selection state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on selection state (e.g., zoom controls, export panel).\n * Components that only need selection information should use `useSelectionContext()`\n * instead of `useEditor()`.\n *\n * Note: `selectedElement` (the derived element object) is NOT in this context\n * because it depends on `elements` state which lives in EditorContext. The\n * `useEditor()` facade computes it from `selectedId` + `elements`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';\nimport type { EditorElement } from './EditorContext.js';\n\n/** Value exposed by SelectionContext. */\nexport interface SelectionContextValue {\n /** ID of the currently selected element, or null if nothing is selected */\n selectedId: string | null;\n /** IDs of elements in the current multi-selection */\n multiSelection: string[];\n /** The child element being directly edited inside a group */\n activeChildElement: EditorElement | null;\n /** ID of the element currently being hovered */\n hoveredElementId: string | null;\n /** Whether to hide selection handles (e.g., during text editing) */\n hideHandles: boolean;\n\n /** Set the selected element ID */\n setSelectedId: React.Dispatch<React.SetStateAction<string | null>>;\n /** Set multi-selection IDs */\n setMultiSelection: React.Dispatch<React.SetStateAction<string[]>>;\n /** Set the hovered element ID */\n setHoveredElementId: React.Dispatch<React.SetStateAction<string | null>>;\n /** Set whether to hide selection handles */\n setHideHandles: React.Dispatch<React.SetStateAction<boolean>>;\n\n /** Handler for element selection changes */\n handleSelectionChange: (id: string | null) => void;\n /** Handler for active child element changes (group editing) */\n handleActiveChildChange: (child: EditorElement | null) => void;\n}\n\nconst SelectionContext = createContext<SelectionContextValue | null>(null);\n\n/** Props for {@link SelectionProvider}. */\nexport interface SelectionProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides selection state and handlers to all descendants.\n *\n * Manages which element is selected, multi-selection, hover state,\n * and active child element for group editing.\n */\nexport const SelectionProvider: React.FC<SelectionProviderProps> = ({ children }) => {\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [multiSelection, setMultiSelection] = useState<string[]>([]);\n const [activeChildElement, setActiveChildElement] = useState<EditorElement | null>(null);\n const [hoveredElementId, setHoveredElementId] = useState<string | null>(null);\n const [hideHandles, setHideHandles] = useState<boolean>(false);\n\n const handleSelectionChange = useCallback((id: string | null) => {\n setSelectedId(id);\n }, []);\n\n const handleActiveChildChange = useCallback((child: EditorElement | null) => {\n setActiveChildElement(child);\n }, []);\n\n const value = useMemo<SelectionContextValue>(\n () => ({\n selectedId,\n multiSelection,\n activeChildElement,\n hoveredElementId,\n hideHandles,\n setSelectedId,\n setMultiSelection,\n setHoveredElementId,\n setHideHandles,\n handleSelectionChange,\n handleActiveChildChange,\n }),\n [\n selectedId,\n multiSelection,\n activeChildElement,\n hoveredElementId,\n hideHandles,\n handleSelectionChange,\n handleActiveChildChange,\n ]\n );\n\n return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;\n};\n\n/**\n * Access selection state: selected element ID, multi-selection, hover, and handlers.\n *\n * Prefer this over `useEditor()` in components that only need selection information\n * (e.g., layers panel highlighting, toolbar enable/disable logic).\n *\n * Note: This does NOT provide `selectedElement` (the resolved element object).\n * Use `useEditor()` or `useSelectedElement()` for that, as it requires\n * cross-referencing with element state.\n *\n * @throws {Error} If called outside of a `SelectionProvider` (or `EditorProvider`)\n */\nexport function useSelectionContext(): SelectionContextValue {\n const context = useContext(SelectionContext);\n if (!context) {\n throw new Error('useSelectionContext must be used within a SelectionProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * HistoryContext - Exposes undo/redo state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on history state (e.g., most rendering components).\n * Components that only need undo/redo controls should use `useHistoryContext()`\n * instead of `useEditor()`.\n *\n * Note: The heavy `useCommandHistory` hook and `execute*` functions remain in\n * EditorContext because they depend on `elements` and `artboardManager`. This\n * context only exposes the undo/redo actions and their availability flags.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\n\n/** Value exposed by HistoryContext. */\nexport interface HistoryContextValue {\n /** Undo the last operation (global scope) */\n undo: () => void;\n /** Redo the last undone operation (global scope) */\n redo: () => void;\n /** Whether there is an operation to undo (global scope) */\n canUndo: boolean;\n /** Whether there is an operation to redo (global scope) */\n canRedo: boolean;\n /** Undo the last operation on the active artboard */\n undoActiveArtboard: () => void;\n /** Redo the last undone operation on the active artboard */\n redoActiveArtboard: () => void;\n /** Whether there is an operation to undo on the active artboard */\n canUndoActiveArtboard: boolean;\n /** Whether there is an operation to redo on the active artboard */\n canRedoActiveArtboard: boolean;\n}\n\nconst HistoryContext = createContext<HistoryContextValue | null>(null);\n\n/** Props for {@link HistoryProvider}. */\nexport interface HistoryProviderProps {\n children: ReactNode;\n /**\n * The actual undo/redo functions and state from the command history system.\n * These are provided by EditorProvider which owns the command history.\n */\n value: HistoryContextValue;\n}\n\n/**\n * Provides undo/redo state and actions to all descendants.\n *\n * This is a \"pass-through\" provider: the actual history state is computed\n * by EditorProvider (via useCommandHistory) and passed in as props. This\n * allows components to subscribe to history changes without subscribing\n * to all of EditorContext.\n */\nexport const HistoryProvider: React.FC<HistoryProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but history state hasn't actually changed\n const memoizedValue = useMemo<HistoryContextValue>(\n () => ({\n undo: value.undo,\n redo: value.redo,\n canUndo: value.canUndo,\n canRedo: value.canRedo,\n undoActiveArtboard: value.undoActiveArtboard,\n redoActiveArtboard: value.redoActiveArtboard,\n canUndoActiveArtboard: value.canUndoActiveArtboard,\n canRedoActiveArtboard: value.canRedoActiveArtboard,\n }),\n [\n value.undo,\n value.redo,\n value.canUndo,\n value.canRedo,\n value.undoActiveArtboard,\n value.redoActiveArtboard,\n value.canUndoActiveArtboard,\n value.canRedoActiveArtboard,\n ]\n );\n\n return <HistoryContext.Provider value={memoizedValue}>{children}</HistoryContext.Provider>;\n};\n\n/**\n * Access undo/redo state and actions.\n *\n * Prefer this over `useEditor()` in components that only need undo/redo\n * controls (e.g., undo/redo buttons in a toolbar).\n *\n * @throws {Error} If called outside of a `HistoryProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function UndoRedoButtons() {\n * const { undo, redo, canUndo, canRedo } = useHistoryContext();\n * return (\n * <>\n * <button onClick={undo} disabled={!canUndo}>Undo</button>\n * <button onClick={redo} disabled={!canRedo}>Redo</button>\n * </>\n * );\n * }\n * ```\n */\nexport function useHistoryContext(): HistoryContextValue {\n const context = useContext(HistoryContext);\n if (!context) {\n throw new Error('useHistoryContext must be used within a HistoryProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * ToolStateContext - Manages UI tool state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on tool state (e.g., canvas rendering, layers panel).\n * Components that only need tool UI state should use `useToolStateContext()`\n * instead of `useEditor()`.\n *\n * Includes: expanded panel type, toolbar menu state, text selection version,\n * canvas ready state, and rotation state.\n *\n * Note: `clearExpandedPanel` is NOT in this context because it depends on\n * `selectedElement` and `executeElementUpdate` (cross-context dependency).\n * It remains in EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useMemo, type ReactNode } from 'react';\n\n/** Expanded panel types for contextual toolbars */\nexport type ExpandedPanelType = 'cornerRadius' | 'effects' | 'crop' | 'rotation' | null;\n\n/** Value exposed by ToolStateContext. */\nexport interface ToolStateContextValue {\n /** Which contextual panel is currently expanded, or null */\n expandedPanelType: ExpandedPanelType;\n /** Set the expanded panel type */\n setExpandedPanelType: React.Dispatch<React.SetStateAction<ExpandedPanelType>>;\n\n /** Whether any toolbar dropdown/menu/panel is open */\n isToolbarMenuOpen: boolean;\n /** Set toolbar menu open state */\n setIsToolbarMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n\n /** Version counter for text selection changes (forces re-render on selection) */\n textSelectionVersion: number;\n /** Increment text selection version */\n setTextSelectionVersion: React.Dispatch<React.SetStateAction<number>>;\n\n /** Whether the canvas has mounted and is ready for operations */\n isCanvasReady: boolean;\n /** Set the canvas ready state */\n setCanvasReady: (ready: boolean) => void;\n\n /** Whether the user is currently rotating an element */\n isRotating: boolean;\n /** Set the rotation state */\n setIsRotating: React.Dispatch<React.SetStateAction<boolean>>;\n}\n\nconst ToolStateContext = createContext<ToolStateContextValue | null>(null);\n\n/** Props for {@link ToolStateProvider}. */\nexport interface ToolStateProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides UI tool state to all descendants.\n *\n * Manages which panel is expanded, whether toolbar menus are open,\n * text selection tracking, canvas readiness, and rotation state.\n */\nexport const ToolStateProvider: React.FC<ToolStateProviderProps> = ({ children }) => {\n const [expandedPanelType, setExpandedPanelType] = useState<ExpandedPanelType>(null);\n const [isToolbarMenuOpen, setIsToolbarMenuOpen] = useState<boolean>(false);\n const [textSelectionVersion, setTextSelectionVersion] = useState<number>(0);\n const [isCanvasReady, setCanvasReady] = useState<boolean>(false);\n const [isRotating, setIsRotating] = useState<boolean>(false);\n\n const value = useMemo<ToolStateContextValue>(\n () => ({\n expandedPanelType,\n setExpandedPanelType,\n isToolbarMenuOpen,\n setIsToolbarMenuOpen,\n textSelectionVersion,\n setTextSelectionVersion,\n isCanvasReady,\n setCanvasReady,\n isRotating,\n setIsRotating,\n }),\n [expandedPanelType, isToolbarMenuOpen, textSelectionVersion, isCanvasReady, isRotating]\n );\n\n return <ToolStateContext.Provider value={value}>{children}</ToolStateContext.Provider>;\n};\n\n/**\n * Access UI tool state: expanded panels, toolbar menus, canvas readiness, etc.\n *\n * Prefer this over `useEditor()` in components that only need tool UI state\n * (e.g., contextual toolbars checking which panel is expanded).\n *\n * @throws {Error} If called outside of a `ToolStateProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function PanelToggle() {\n * const { expandedPanelType, setExpandedPanelType } = useToolStateContext();\n * return (\n * <button onClick={() => setExpandedPanelType(\n * expandedPanelType === 'effects' ? null : 'effects'\n * )}>\n * {expandedPanelType === 'effects' ? 'Close' : 'Effects'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useToolStateContext(): ToolStateContextValue {\n const context = useContext(ToolStateContext);\n if (!context) {\n throw new Error('useToolStateContext must be used within a ToolStateProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * ElementsContext - Provides element and artboard state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on element state (e.g., zoom controls, undo/redo buttons).\n * Components that only need element/artboard information should use\n * `useElementsContext()` instead of `useEditor()`.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via InternalEditorProvider) and passed in as props. This\n * allows components to subscribe to element changes without subscribing\n * to all of EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport type { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\nimport type { ElementStore } from '../core/ElementStore.js';\nimport type { CanvasEditorHandle } from '../components/CanvasEditor.jsx';\nimport type { EditorElement } from './EditorContext.js';\nimport type { TextAlign } from '../types/index.js';\n\n/** Value exposed by ElementsContext. */\nexport interface ElementsContextValue {\n /** All elements in the editor */\n elements: EditorElement[];\n /** Normalized element store for O(1) lookups */\n elementStore: ElementStore;\n /** All artboards */\n artboards: ArtboardElement[];\n /** Artboard manager instance */\n artboardManager: ArtboardManager;\n /**\n * Version counter that increments on every artboard mutation.\n * Components can depend on this to re-render when artboard state changes.\n */\n artboardVersion: number;\n\n /** Set elements (same SetStateAction signature as before) */\n setElements: React.Dispatch<React.SetStateAction<EditorElement[]>>;\n /** Refresh artboards from the artboard manager */\n refreshArtboards: () => void;\n /** O(1) element lookup by ID. Returns undefined if not found. */\n getElementById: (id: string) => EditorElement | undefined;\n\n /** Add an element by type (text, image, shape, etc.) */\n handleAddElement: (elementType: string) => Promise<void>;\n /** Update an element */\n handleElementUpdate: (updatedElement: EditorElement) => void;\n /** Load a workspace (artboards + elements) */\n handleLoadWorkspace: (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => void;\n /** Copy selected elements to clipboard */\n handleCopyElements: () => void;\n /** Paste elements from clipboard */\n handlePasteElements: () => void;\n\n // Font property helpers\n /** Current font size of selected element */\n fontSize: number;\n /** Current font color of selected element */\n fontColor: string;\n /** Current font family of selected element */\n fontFamily: string;\n /** Current text alignment of selected element */\n textAlign: TextAlign;\n /** Update font size by delta */\n updateFontSize: (delta: number) => void;\n /** Set font size to exact value */\n setFontSizeValue: (size: number) => void;\n /** Update font color */\n updateFontColor: (color: string) => void;\n /** Update font family */\n updateFontFamily: (fontFamily: string) => void;\n /** Update text alignment */\n updateTextAlign: (textAlign: TextAlign) => void;\n\n // Refs\n /** Ref to the canvas HTML element */\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n /** Ref to the imperative canvas editor handle */\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n}\n\nconst ElementsContext = createContext<ElementsContextValue | null>(null);\n\n/** Props for {@link ElementsProvider}. */\nexport interface ElementsProviderProps {\n children: ReactNode;\n /**\n * The actual element/artboard state from the editor.\n * Provided by InternalEditorProvider which owns all state.\n */\n value: ElementsContextValue;\n}\n\n/**\n * Provides element and artboard state to all descendants.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via InternalEditorProvider) and passed in as props.\n * This allows components to subscribe to element changes without\n * subscribing to all of EditorContext.\n */\nexport const ElementsProvider: React.FC<ElementsProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but element state hasn't actually changed\n const memoizedValue = useMemo<ElementsContextValue>(\n () => ({\n elements: value.elements,\n elementStore: value.elementStore,\n artboards: value.artboards,\n artboardManager: value.artboardManager,\n artboardVersion: value.artboardVersion,\n setElements: value.setElements,\n refreshArtboards: value.refreshArtboards,\n getElementById: value.getElementById,\n handleAddElement: value.handleAddElement,\n handleElementUpdate: value.handleElementUpdate,\n handleLoadWorkspace: value.handleLoadWorkspace,\n handleCopyElements: value.handleCopyElements,\n handlePasteElements: value.handlePasteElements,\n fontSize: value.fontSize,\n fontColor: value.fontColor,\n fontFamily: value.fontFamily,\n textAlign: value.textAlign,\n updateFontSize: value.updateFontSize,\n setFontSizeValue: value.setFontSizeValue,\n updateFontColor: value.updateFontColor,\n updateFontFamily: value.updateFontFamily,\n updateTextAlign: value.updateTextAlign,\n canvasRef: value.canvasRef,\n canvasEditorRef: value.canvasEditorRef,\n }),\n [\n value.elements,\n value.elementStore,\n value.artboards,\n value.artboardManager,\n value.artboardVersion,\n value.setElements,\n value.refreshArtboards,\n value.getElementById,\n value.handleAddElement,\n value.handleElementUpdate,\n value.handleLoadWorkspace,\n value.handleCopyElements,\n value.handlePasteElements,\n value.fontSize,\n value.fontColor,\n value.fontFamily,\n value.textAlign,\n value.updateFontSize,\n value.setFontSizeValue,\n value.updateFontColor,\n value.updateFontFamily,\n value.updateTextAlign,\n value.canvasRef,\n value.canvasEditorRef,\n ]\n );\n\n return <ElementsContext.Provider value={memoizedValue}>{children}</ElementsContext.Provider>;\n};\n\n/**\n * Access element and artboard state: elements, artboards, element operations,\n * font properties, and canvas refs.\n *\n * Prefer this over `useEditor()` in components that only need element/artboard\n * information (e.g., layers panel, effects panel, property inspectors).\n * Changes to non-element state (viewport, selection, tool state) won't\n * trigger re-renders in components using this hook.\n *\n * @throws {Error} If called outside of an `ElementsProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function ElementCount() {\n * const { elements, artboards } = useElementsContext();\n * return (\n * <div>\n * <span>{elements.length} elements</span>\n * <span>{artboards.length} artboards</span>\n * </div>\n * );\n * }\n * ```\n */\nexport function useElementsContext(): ElementsContextValue {\n const context = useContext(ElementsContext);\n if (!context) {\n throw new Error('useElementsContext must be used within an ElementsProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * CommandContext - Provides command execution and undo/redo for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on command execution (e.g., layers panel, zoom controls).\n * Components that only need command execution should use `useCommandContext()`\n * instead of `useEditor()`.\n *\n * This is a \"pass-through\" provider: the actual command state is computed by\n * EditorProvider (via InternalEditorProvider / useCommandHistory) and passed\n * in as props. This allows components to subscribe to command-related changes\n * without subscribing to all of EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport type { useCommandHistory } from '../hooks/useCommandHistory.js';\n\n/** Value exposed by CommandContext. */\nexport interface CommandContextValue {\n /** Execute an element update (old -> new) through command history */\n executeElementUpdate: ReturnType<typeof useCommandHistory>['executeElementUpdate'];\n /** Execute adding a new element through command history */\n executeAddElement: ReturnType<typeof useCommandHistory>['executeAddElement'];\n /** Execute removing an element through command history */\n executeRemoveElement: ReturnType<typeof useCommandHistory>['executeRemoveElement'];\n /** Execute reordering an element through command history */\n executeReorderElement: ReturnType<typeof useCommandHistory>['executeReorderElement'];\n /** Execute creating an artboard through command history */\n executeCreateArtboard: ReturnType<typeof useCommandHistory>['executeCreateArtboard'];\n /** Execute deleting an artboard through command history */\n executeDeleteArtboard: ReturnType<typeof useCommandHistory>['executeDeleteArtboard'];\n /** Execute updating an artboard through command history */\n executeUpdateArtboard: ReturnType<typeof useCommandHistory>['executeUpdateArtboard'];\n /** Execute a batch of commands as a single undo entry */\n executeCommandBatch: ReturnType<typeof useCommandHistory>['executeCommandBatch'];\n\n /** Undo the last operation (global scope) */\n undo: () => void;\n /** Redo the last undone operation (global scope) */\n redo: () => void;\n /** Whether there is an operation to undo (global scope) */\n canUndo: boolean;\n /** Whether there is an operation to redo (global scope) */\n canRedo: boolean;\n\n /** Undo the last operation on the active artboard */\n undoActiveArtboard: () => void;\n /** Redo the last undone operation on the active artboard */\n redoActiveArtboard: () => void;\n /** Whether there is an operation to undo on the active artboard */\n canUndoActiveArtboard: boolean;\n /** Whether there is an operation to redo on the active artboard */\n canRedoActiveArtboard: boolean;\n\n /** The underlying history manager instance */\n historyManager: ReturnType<typeof useCommandHistory>['historyManager'];\n}\n\nconst CommandContext = createContext<CommandContextValue | null>(null);\n\n/** Props for {@link CommandProvider}. */\nexport interface CommandProviderProps {\n children: ReactNode;\n /**\n * The actual command execution functions and undo/redo state.\n * Provided by InternalEditorProvider which owns the command history.\n */\n value: CommandContextValue;\n}\n\n/**\n * Provides command execution and undo/redo state to all descendants.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via useCommandHistory) and passed in as props. This\n * allows components to subscribe to command state without subscribing\n * to all of EditorContext.\n */\nexport const CommandProvider: React.FC<CommandProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but command state hasn't actually changed\n const memoizedValue = useMemo<CommandContextValue>(\n () => ({\n executeElementUpdate: value.executeElementUpdate,\n executeAddElement: value.executeAddElement,\n executeRemoveElement: value.executeRemoveElement,\n executeReorderElement: value.executeReorderElement,\n executeCreateArtboard: value.executeCreateArtboard,\n executeDeleteArtboard: value.executeDeleteArtboard,\n executeUpdateArtboard: value.executeUpdateArtboard,\n executeCommandBatch: value.executeCommandBatch,\n undo: value.undo,\n redo: value.redo,\n canUndo: value.canUndo,\n canRedo: value.canRedo,\n undoActiveArtboard: value.undoActiveArtboard,\n redoActiveArtboard: value.redoActiveArtboard,\n canUndoActiveArtboard: value.canUndoActiveArtboard,\n canRedoActiveArtboard: value.canRedoActiveArtboard,\n historyManager: value.historyManager,\n }),\n [\n value.executeElementUpdate,\n value.executeAddElement,\n value.executeRemoveElement,\n value.executeReorderElement,\n value.executeCreateArtboard,\n value.executeDeleteArtboard,\n value.executeUpdateArtboard,\n value.executeCommandBatch,\n value.undo,\n value.redo,\n value.canUndo,\n value.canRedo,\n value.undoActiveArtboard,\n value.redoActiveArtboard,\n value.canUndoActiveArtboard,\n value.canRedoActiveArtboard,\n value.historyManager,\n ]\n );\n\n return <CommandContext.Provider value={memoizedValue}>{children}</CommandContext.Provider>;\n};\n\n/**\n * Access command execution and undo/redo state.\n *\n * Prefer this over `useEditor()` in components that only need to execute\n * commands or check undo/redo availability (e.g., toolbar buttons that\n * add/remove elements, undo/redo controls).\n *\n * Note: For undo/redo-only access (without execute functions), prefer\n * `useHistoryContext()` which is even more focused.\n *\n * @throws {Error} If called outside of a `CommandProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { executeRemoveElement } = useCommandContext();\n * const { selectedElement } = useEditor();\n *\n * return (\n * <button\n * onClick={() => selectedElement && executeRemoveElement(selectedElement)}\n * disabled={!selectedElement}\n * >\n * Delete\n * </button>\n * );\n * }\n * ```\n */\nexport function useCommandContext(): CommandContextValue {\n const context = useContext(CommandContext);\n if (!context) {\n throw new Error('useCommandContext must be used within a CommandProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * EditorContext - Central state management for the canvas editor.\n *\n * Composes focused sub-contexts (ViewportContext, SelectionContext,\n * HistoryContext, ToolStateContext, ElementsContext, CommandContext) and\n * provides a backwards-compatible `useEditor()` facade that aggregates\n * all sub-context values.\n *\n * Every hook and UI component in `@snowcone-app/canvas` requires an `EditorProvider`\n * ancestor in the component tree. The `SnowconeCanvas` component creates one\n * automatically; use `EditorProvider` directly only when building custom layouts.\n *\n * ## Sub-Context Architecture\n *\n * State that was previously all in one context is now split into focused contexts\n * for better render performance:\n *\n * - **ViewportContext** - zoom, panOffset, isPanning, zoomIn/Out/ToFit/ResetView\n * - **SelectionContext** - selectedId, multiSelection, hoveredElementId, hideHandles\n * - **HistoryContext** - undo, redo, canUndo, canRedo (per-artboard variants)\n * - **ToolStateContext** - expandedPanelType, isToolbarMenuOpen, isCanvasReady, etc.\n * - **ElementsContext** - elements, artboards, element operations, font properties, refs\n * - **CommandContext** - command execution (execute*), undo/redo, historyManager\n * - **EditorContext (internal)** - full combined value for backwards compatibility\n *\n * Components can use focused hooks (useViewportContext, useSelectionContext,\n * useElementsContext, useCommandContext, etc.) to subscribe to only the state\n * they need. `useEditor()` still works as before and returns the full combined value.\n *\n * @module\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n useRef,\n useMemo,\n ReactNode,\n useEffect,\n} from 'react';\nimport { ArtboardElement } from '../core/ArtboardElement.js';\nimport { ArtboardManager } from '../core/ArtboardManager.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { ElementStore } from '../core/ElementStore.js';\nimport { useCommandHistory } from '../hooks/useCommandHistory.js';\nimport { useElementProperties } from '../hooks/useElementProperties.js';\nimport { DEFAULT_ARTBOARD_COLOR } from '../constants.js';\nimport { getTransformById } from '../transforms/registry.js';\nimport { WAVE_DEFAULTS, ARCH_DEFAULTS } from '../transforms/defaults.js';\nimport { shouldPreserveSelection } from '../utils/selectionPreservation.js';\nimport { EditorError } from '../errors/editorError.js';\nimport { defaultTextColorForBackground } from '../utils/inkLuminance.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ViewportProvider, useViewportContext } from './ViewportContext.js';\nimport { SelectionProvider, useSelectionContext } from './SelectionContext.js';\nimport { HistoryProvider } from './HistoryContext.js';\nimport { ToolStateProvider, useToolStateContext } from './ToolStateContext.js';\nimport { ElementsProvider } from './ElementsContext.js';\nimport { CommandProvider } from './CommandContext.js';\nimport type { CanvasEditorHandle } from '../components/CanvasEditor.jsx';\nimport type { TransformType, TextAlign, ShapeType } from '../types/index.js';\n\nconst logger = createLogger('EditorContext');\n\n// Re-export sub-context types for backwards compatibility.\n// Consumers that import PanOffset or ExpandedPanelType from EditorContext\n// will continue to work without changes.\nexport type { PanOffset } from './ViewportContext.js';\nexport type { ExpandedPanelType } from './ToolStateContext.js';\n\nexport type EditorElement =\n | TextElement\n | ImageElement\n | GroupElement\n | ShapeElement\n | PathElement;\n\n/**\n * Spec for {@link EditorContextValue.addElement}. `name` makes the created\n * element addressable afterwards (bindings, `updateElement`, layer tokens).\n */\nexport interface AddElementSpec {\n /** What to create. Text seeds editable \"New Text\"; image without `src` gets a placeholder. */\n type: 'text' | 'shape' | 'image';\n /** Shape variant (default `'rectangle'`). Shape type only. */\n shapeType?: ShapeType;\n /** Image source URL — the element loads it and sizes to the real aspect ratio. Image type only. */\n src?: string;\n /** Layer name for the new element, so it can be targeted afterwards. */\n name?: string;\n}\n\n/**\n * Handle to an element created by {@link EditorContextValue.addElement}:\n * the generated `id` plus the layer `name` it answers to (empty string when\n * no `name` was given). The new element arrives selected.\n */\nexport interface ElementHandle {\n id: string;\n name: string;\n}\n\n/**\n * Full value exposed by EditorContext.\n *\n * Contains core state (elements, selection, artboards, zoom), refs to the\n * canvas DOM and imperative editor handle, setter functions, command history\n * operations (undo/redo), and element property helpers.\n */\nexport interface EditorContextValue {\n // Core state\n artboardManager: ArtboardManager;\n artboards: ArtboardElement[];\n elements: EditorElement[];\n /** Normalized element store for O(1) lookups. Use getElementById() for convenience. */\n elementStore: ElementStore;\n selectedId: string | null;\n multiSelection: string[];\n selectedElement: EditorElement | undefined;\n activeChildElement: EditorElement | null;\n hoveredElementId: string | null;\n hideHandles: boolean;\n zoom: number;\n panOffset: import('./ViewportContext.js').PanOffset;\n isPanning: boolean;\n isRotating: boolean;\n\n /** O(1) element lookup by ID. Returns undefined if not found. */\n getElementById: (id: string) => EditorElement | undefined;\n\n // Expanded panel state (shared with ContextualToolbars for two-level deselect)\n expandedPanelType: import('./ToolStateContext.js').ExpandedPanelType;\n setExpandedPanelType: React.Dispatch<\n React.SetStateAction<import('./ToolStateContext.js').ExpandedPanelType>\n >;\n clearExpandedPanel: () => void;\n\n // Toolbar menu open state - tracks if any dropdown/menu/panel is open in the toolbar\n // Used to disable keyboard shortcuts like Delete when interacting with toolbar\n isToolbarMenuOpen: boolean;\n setIsToolbarMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n\n // Refs\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n\n // Setters\n setElements: React.Dispatch<React.SetStateAction<EditorElement[]>>;\n setSelectedId: React.Dispatch<React.SetStateAction<string | null>>;\n setZoom: React.Dispatch<React.SetStateAction<number>>;\n setPanOffset: React.Dispatch<\n React.SetStateAction<import('./ViewportContext.js').PanOffset>\n >;\n setIsPanning: React.Dispatch<React.SetStateAction<boolean>>;\n setHoveredElementId: React.Dispatch<React.SetStateAction<string | null>>;\n setHideHandles: React.Dispatch<React.SetStateAction<boolean>>;\n setMultiSelection: React.Dispatch<React.SetStateAction<string[]>>;\n setTextSelectionVersion: React.Dispatch<React.SetStateAction<number>>;\n setIsRotating: React.Dispatch<React.SetStateAction<boolean>>;\n\n // Viewport control helpers\n zoomIn: () => void;\n zoomOut: () => void;\n zoomToFit: () => void;\n resetView: () => void;\n\n /**\n * Version counter that increments on every artboard mutation.\n * Components can depend on this to re-render when artboard state changes,\n * even though ArtboardManager is a mutable class instance.\n */\n artboardVersion: number;\n\n // Handlers\n refreshArtboards: () => void;\n handleSelectionChange: (id: string | null) => void;\n handleActiveChildChange: (child: EditorElement | null) => void;\n handleElementUpdate: (updatedElement: EditorElement) => void;\n /**\n * Create an element on the active artboard and get a handle back. The new\n * element arrives SELECTED. Throws `E_NO_ACTIVE_ARTBOARD` (an\n * {@link EditorError}, dev AND prod) when there is no artboard to add to.\n */\n addElement: (spec: AddElementSpec) => Promise<ElementHandle>;\n /**\n * @deprecated Use {@link addElement} — it returns an `ElementHandle`,\n * accepts a `name`, and throws `E_NO_ACTIVE_ARTBOARD` instead of the old\n * `window.alert`. This wrapper now propagates that throw too.\n */\n handleAddElement: (\n elementType: string,\n opts?: { shapeType?: ShapeType; src?: string }\n ) => Promise<void>;\n handleLoadWorkspace: (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => void;\n handleCopyElements: () => void;\n handlePasteElements: () => void;\n /**\n * Whether the editor's internal copy/paste clipboard holds anything.\n * `handlePasteElements` is a silent no-op when this is false — surfaces\n * offering a Paste affordance (canvas context menu) use this to disable\n * the entry instead of presenting an action that does nothing.\n */\n hasClipboardContent: boolean;\n\n // Command history\n historyManager: ReturnType<typeof useCommandHistory>['historyManager'];\n executeElementUpdate: ReturnType<typeof useCommandHistory>['executeElementUpdate'];\n executeAddElement: ReturnType<typeof useCommandHistory>['executeAddElement'];\n executeRemoveElement: ReturnType<typeof useCommandHistory>['executeRemoveElement'];\n executeReorderElement: ReturnType<typeof useCommandHistory>['executeReorderElement'];\n executeCreateArtboard: ReturnType<typeof useCommandHistory>['executeCreateArtboard'];\n executeDeleteArtboard: ReturnType<typeof useCommandHistory>['executeDeleteArtboard'];\n executeUpdateArtboard: ReturnType<typeof useCommandHistory>['executeUpdateArtboard'];\n executeCommandBatch: ReturnType<typeof useCommandHistory>['executeCommandBatch'];\n undo: () => void;\n redo: () => void;\n canUndo: boolean;\n canRedo: boolean;\n // Per-artboard undo/redo (for active artboard only)\n undoActiveArtboard: () => void;\n redoActiveArtboard: () => void;\n canUndoActiveArtboard: boolean;\n canRedoActiveArtboard: boolean;\n\n // Element properties\n fontSize: number;\n fontColor: string;\n fontFamily: string;\n textAlign: TextAlign;\n updateFontSize: (delta: number) => void;\n setFontSizeValue: (size: number) => void;\n updateFontColor: (color: string) => void;\n updateFontFamily: (fontFamily: string) => void;\n updateTextAlign: (textAlign: TextAlign) => void;\n\n // Text selection version\n textSelectionVersion: number;\n\n // Canvas ready state - gates exports until canvas is mounted\n isCanvasReady: boolean;\n setCanvasReady: (ready: boolean) => void;\n}\n\nconst EditorContext = createContext<EditorContextValue | null>(null);\n\n/**\n * Stable lazy access to the latest {@link EditorContextValue} — re-render\n * containment for chrome that needs editor state only inside EVENT HANDLERS\n * (save, export, add-element), never at render time (ADR-0085 / D3:\n * subscribing to the fat context re-renders on every editor change, including\n * each keystroke routed through the command path).\n *\n * The provided object's identity NEVER changes; `get()` returns the value as\n * of the provider's latest render. Do not render from it — anything read at\n * render time will go stale without a re-render. For reactive reads use the\n * narrow sub-context hooks instead.\n */\nexport interface EditorHandles {\n /** The latest full editor context value (lazy — call inside handlers). */\n get(): EditorContextValue;\n}\n\nconst EditorHandlesContext = createContext<EditorHandles | null>(null);\n\n/**\n * Read the stable editor handles. Same provider requirement as `useEditor()`.\n *\n * @throws {Error} If called outside of an `EditorProvider`\n */\nexport const useEditorHandles = (): EditorHandles => {\n const context = useContext(EditorHandlesContext);\n if (!context) {\n throw new Error('useEditorHandles must be used within EditorProvider');\n }\n return context;\n};\n\n/** Props for {@link EditorProvider}. */\nexport interface EditorProviderProps {\n children: ReactNode;\n /** Configuration for the default artboard created on mount. */\n initialArtboardConfig?: {\n name?: string;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n backgroundColor?: string;\n };\n /**\n * Controls how much of the container the artboard fills when using fitToArtboard.\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 */\n viewPadding?: number;\n}\n\n/**\n * Internal provider that holds element/artboard state and composes with sub-contexts.\n *\n * This is the \"inner\" provider that has access to all sub-context values via hooks.\n * It reads from ViewportContext, SelectionContext, and ToolStateContext,\n * then combines everything into the full EditorContextValue for backwards compatibility.\n */\nconst InternalEditorProvider: React.FC<{\n children: ReactNode;\n artboardManager: ArtboardManager;\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n}> = ({ children, artboardManager, canvasRef, canvasEditorRef }) => {\n // Backing ref + literally-stable value for EditorHandlesContext (see\n // useEditorHandles). Declared first so the assignment after the big value\n // memo below is the last write before render.\n const handlesRef = useRef<EditorContextValue | null>(null);\n const handles = useMemo<EditorHandles>(() => ({ get: () => handlesRef.current! }), []);\n\n // --- Read sub-context values ---\n const viewport = useViewportContext();\n const selection = useSelectionContext();\n const toolState = useToolStateContext();\n\n // --- Artboard state ---\n const [artboards, setArtboards] = useState<ArtboardElement[]>(() =>\n artboardManager.getAllArtboards()\n );\n // Version counter for artboard mutations - incremented by refreshArtboards\n // so React can detect changes to the mutable ArtboardManager class instance.\n const [artboardVersion, setArtboardVersion] = useState(0);\n\n // ---------------------------------------------------------------------------\n // Normalized element storage (ElementStore)\n //\n // Internally we store elements in an ElementStore (Map-based, O(1) lookups).\n // Externally we still expose `elements: EditorElement[]` and\n // `setElements: Dispatch<SetStateAction<EditorElement[]>>` so every existing\n // consumer continues to work unchanged.\n // ---------------------------------------------------------------------------\n const [elementStore, setElementStore] = useState<ElementStore>(\n () => new ElementStore()\n );\n\n // Derived plain array - recomputed only when the store reference changes.\n const elements = useMemo(() => elementStore.toArray(), [elementStore]);\n\n // Wrapper that accepts the same SetStateAction<EditorElement[]> signature\n // that all consumers already use, but updates the ElementStore internally.\n const setElements: React.Dispatch<React.SetStateAction<EditorElement[]>> =\n useCallback((action: React.SetStateAction<EditorElement[]>) => {\n setElementStore((prevStore) => {\n const prevArray = prevStore.toArray();\n const nextArray =\n typeof action === 'function' ? action(prevArray) : action;\n // Avoid unnecessary re-creation when the array is the same reference\n if (nextArray === prevArray) return prevStore;\n return ElementStore.fromArray(nextArray);\n });\n }, []);\n\n // O(1) element lookup exposed to consumers\n const getElementById = useCallback(\n (id: string): EditorElement | undefined => elementStore.get(id),\n [elementStore]\n );\n\n const [clipboard, setClipboard] = useState<EditorElement[]>([]);\n\n // --- Derived state (cross-context: needs elements + selectedId) ---\n // O(1) lookup via ElementStore instead of O(n) elements.find()\n const selectedElement = selection.selectedId\n ? elementStore.get(selection.selectedId)\n : undefined;\n\n // Callback to refresh artboards state.\n // Also bumps artboardVersion so any component depending on artboard state re-renders,\n // even though ArtboardManager is a mutable class instance (fixes React integration).\n const refreshArtboards = useCallback(() => {\n setArtboards([...artboardManager.getAllArtboards()]);\n setArtboardVersion((v) => v + 1);\n }, [artboardManager]);\n\n // --- Command history ---\n const {\n historyManager,\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n } = useCommandHistory(\n elements,\n setElements,\n artboardManager,\n refreshArtboards\n );\n\n // --- Element update handlers ---\n // Update element handler - uses O(1) lookup via ElementStore\n const handleElementUpdate = useCallback(\n (updatedElement: EditorElement) => {\n const oldElement = elementStore.get(updatedElement.id);\n executeElementUpdate(oldElement, updatedElement);\n },\n [elementStore, executeElementUpdate]\n );\n\n // Update child element within a group\n const handleChildElementUpdate = useCallback(\n (updatedChild: EditorElement) => {\n if (\n !selectedElement ||\n !(selectedElement instanceof GroupElement)\n ) {\n return;\n }\n\n const updatedGroup = selectedElement.clone();\n const childIndex = updatedGroup.children.findIndex(\n (c) => c.id === updatedChild.id\n );\n\n if (childIndex >= 0) {\n // Cast is safe because GroupElement.children accepts the same element types\n updatedGroup.children[childIndex] =\n updatedChild as Parameters<GroupElement['addChild']>[0];\n handleElementUpdate(updatedGroup);\n }\n },\n [selectedElement, handleElementUpdate]\n );\n\n // --- Element properties ---\n const elementForProperties = selection.activeChildElement || selectedElement;\n const elementUpdateHandler = selection.activeChildElement\n ? handleChildElementUpdate\n : handleElementUpdate;\n const {\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n } = useElementProperties(elementForProperties, elementUpdateHandler);\n\n // --- Add element handler ---\n const addElement = useCallback(\n async (spec: AddElementSpec): Promise<ElementHandle> => {\n const elementType = spec.type;\n const opts = spec;\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n throw new EditorError(\n 'E_NO_ACTIVE_ARTBOARD',\n `addElement({ type: \"${elementType}\" }) needs an active artboard and none exists. ` +\n `<Editor.Root> seeds one automatically; with a raw EditorProvider create one first ` +\n `(executeCreateArtboard) or select one (artboardManager.setActiveArtboard).`,\n { type: elementType, docSlug: 'embed-mode' },\n );\n }\n\n const newId = `element-${Date.now()}`;\n const activeArtboard = artboardManager.getActiveArtboard();\n const centerX = activeArtboard\n ? activeArtboard.x + activeArtboard.width / 2\n : window.innerWidth / 2;\n const centerY = activeArtboard\n ? activeArtboard.y + activeArtboard.height / 2\n : window.innerHeight / 2;\n\n // Get current zoom level\n const currentZoom =\n canvasEditorRef.current?.getZoom() || viewport.zoom || 1.0;\n\n // Image elements: with `opts.src`, create the element pointed at that\n // URL and run the real load path (mirrors useImageBinding's swap logic)\n // so the element sizes to the loaded image instead of the placeholder\n // box. Without `src`, keep the 1x1 transparent placeholder — the caller\n // is responsible for replacing the image URL after creation.\n if (elementType === 'image') {\n const placeholderImage =\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';\n const boxSize = 200 / currentZoom;\n const src = opts?.src;\n const newImageElement = new ImageElement({\n id: newId,\n name: spec.name,\n x: centerX,\n y: centerY,\n // Defer loading `src` until after the element is in the store so a\n // synchronously-cached image can't fire onLoadCallback too early.\n imageUrl: src ? '' : placeholderImage,\n transformData: {\n type: 'image',\n width: boxSize,\n height: boxSize,\n cropX: 0,\n cropY: 0,\n cropWidth: 1,\n cropHeight: 1,\n flipHorizontal: false,\n flipVertical: false,\n borderRadius: 0,\n },\n });\n\n if (src) {\n // Same reset + load sequence as useImageBinding's setImageUrl\n // (\"contain\" fit): mark unloaded, set the URL, and once the image\n // loads, size to the real aspect ratio within the default box.\n newImageElement.imageLoaded = false;\n newImageElement.imageElement = null;\n newImageElement.isCropping = false;\n newImageElement.imageUrl = src;\n newImageElement.preserveDimensions = false;\n newImageElement.onLoadCallback = (loadedElement) => {\n // loadImage already set height = width / aspectRatio; scale the\n // whole frame down so it fits the default box, full image visible.\n const w = loadedElement.transformData.width;\n const h = loadedElement.transformData.height;\n const scale = Math.min(boxSize / w, boxSize / h, 1);\n if (scale < 1) {\n loadedElement.transformData.width = w * scale;\n loadedElement.transformData.height = h * scale;\n }\n loadedElement.transformData.cropX = 0;\n loadedElement.transformData.cropY = 0;\n loadedElement.transformData.cropWidth = 1;\n loadedElement.transformData.cropHeight = 1;\n // Single atomic update: command history + element store see only\n // the final loaded state.\n executeElementUpdate(newImageElement, loadedElement);\n };\n }\n\n executeAddElement(newImageElement, activeArtboardId);\n selection.setSelectedId(newImageElement.id);\n\n if (src) {\n newImageElement.loadImage(src);\n }\n return { id: newImageElement.id, name: spec.name ?? '' };\n }\n\n let newElement: EditorElement;\n\n // Map element type to transform type\n const typeMap: Record<string, TransformType> = {\n text: 'custom',\n circle: 'circle',\n wave: 'wave',\n arch: 'arch',\n shape: 'shape',\n };\n\n const transformType: TransformType = typeMap[elementType] || 'custom';\n const transformDef = getTransformById(transformType);\n\n if (!transformDef || !transformDef.Component) {\n // Unreachable for the public AddElementSpec union (typeMap falls back\n // to 'custom', which is always registered) — loud anyway, never silent.\n throw new Error(`[EditorContext] no transform registered for element type \"${elementType}\"`);\n }\n\n // Calculate appropriate sizes based on artboard\n const artboard = artboardManager.getActiveArtboard();\n const baseFontSize = artboard\n ? Math.min(artboard.width, artboard.height) * 0.1\n : 80;\n const transformWidth = artboard ? artboard.width * 0.5 : 300;\n\n if (elementType === 'shape') {\n const shapeType: ShapeType = opts?.shapeType ?? 'rectangle';\n // Sensible default dims per shape type — ellipses and lines\n // read better at non-square aspect ratios.\n let shapeW = 200 / currentZoom;\n let shapeH = 200 / currentZoom;\n if (shapeType === 'ellipse') {\n shapeW = 150 / currentZoom;\n shapeH = 250 / currentZoom;\n } else if (shapeType === 'line') {\n shapeW = 300 / currentZoom;\n shapeH = 8 / currentZoom;\n }\n newElement = new ShapeElement({\n id: newId,\n name: spec.name,\n x: centerX,\n y: centerY,\n transformData: {\n type: 'shape',\n shapeType,\n width: shapeW,\n height: shapeH,\n fillColor: '#3b82f6',\n fillOpacity: 1,\n },\n });\n } else {\n // Text-based elements. Default ink is artboard-aware: dark\n // backgrounds get light ink so new text isn't invisible. Applies to\n // the creation default only — bindings, templates, and\n // deserialization always honour explicit colours.\n newElement = new transformDef.Component({\n id: newId,\n name: spec.name,\n text: 'New Text',\n x: centerX,\n y: centerY,\n fontSize: baseFontSize,\n fontFamily: 'Poppins',\n color: defaultTextColorForBackground(activeArtboard?.backgroundColor),\n transformData:\n transformType === 'arch'\n ? ({\n type: 'arch',\n width: transformWidth,\n archHeight: ARCH_DEFAULTS.archHeight,\n } as const)\n : transformType === 'wave'\n ? ({\n type: 'wave',\n width: transformWidth,\n amplitude: WAVE_DEFAULTS.amplitude,\n frequency: WAVE_DEFAULTS.frequency,\n } as const)\n : transformType === 'circle'\n ? ({\n type: 'circle',\n radius: transformWidth / 2,\n } as const)\n : undefined,\n } as Record<string, unknown>);\n }\n\n artboardManager.addElementToArtboard(newId, activeArtboardId);\n executeAddElement(\n newElement,\n activeArtboardId,\n selection.selectedId || undefined\n );\n selection.setSelectedId(newId);\n return { id: newId, name: spec.name ?? '' };\n },\n [artboardManager, executeAddElement, executeElementUpdate, selection, viewport.zoom, canvasEditorRef]\n );\n\n /**\n * @deprecated Thin wrapper over {@link addElement} kept for the legacy\n * chrome (string element types incl. 'circle'/'wave'/'arch'). No more\n * `window.alert`: the `E_NO_ACTIVE_ARTBOARD` throw propagates.\n */\n const handleAddElement = useCallback(\n async (elementType: string, opts?: { shapeType?: ShapeType; src?: string }): Promise<void> => {\n await addElement({ ...opts, type: elementType as AddElementSpec['type'] });\n },\n [addElement]\n );\n\n // --- Copy/Paste ---\n // Copy selected element(s) to clipboard\n const handleCopyElements = useCallback(() => {\n const elementsToCopy: EditorElement[] = [];\n\n // Handle multi-selection\n if (selection.multiSelection.length > 0) {\n const selectedElements = elements.filter((s) =>\n selection.multiSelection.includes(s.id)\n );\n elementsToCopy.push(...selectedElements);\n } else if (selectedElement) {\n // Handle single selection\n elementsToCopy.push(selectedElement);\n }\n\n if (elementsToCopy.length > 0) {\n // Clone elements for clipboard (they keep their IDs for reference)\n const clonedElements = elementsToCopy.map((el) => el.clone());\n setClipboard(clonedElements);\n }\n }, [selectedElement, selection.multiSelection, elements]);\n\n // Paste elements from clipboard with diagonal offset\n const handlePasteElements = useCallback(() => {\n if (clipboard.length === 0) return;\n\n const pastedIds: string[] = [];\n const offsetX = 30; // Diagonal offset\n const offsetY = 30;\n\n clipboard.forEach((clonedElement) => {\n // O(1) lookup for the original element to get its current position and artboard\n const originalElement = elementStore.get(clonedElement.id);\n\n // Use original element's position if it still exists, otherwise use cloned position\n const baseX = originalElement ? originalElement.x : clonedElement.x;\n const baseY = originalElement ? originalElement.y : clonedElement.y;\n\n // Generate a new ID for the pasted element\n // Clone the element config and remove ID to get a fresh instance\n const elementConfig = clonedElement.toJSON();\n delete (elementConfig as Partial<typeof elementConfig>).id;\n const Constructor = clonedElement.constructor as new (config: Record<string, unknown>) => EditorElement;\n const pastedElement = new Constructor(elementConfig as unknown as Record<string, unknown>);\n\n // Offset position diagonally from the original\n pastedElement.x = baseX + offsetX;\n pastedElement.y = baseY + offsetY;\n\n // Get artboard for the original element (or use active artboard)\n let artboardId: string | null = null;\n\n if (originalElement) {\n artboardId = artboardManager.getArtboardIdForElement(\n originalElement.id\n );\n }\n\n if (!artboardId) {\n artboardId = artboardManager.getActiveArtboardId();\n }\n\n if (!artboardId) {\n logger.warn('[handlePasteElements] No artboard found for pasting');\n return;\n }\n\n // Add pasted element after the original (or at end if original not found)\n const insertAfterId = originalElement ? originalElement.id : undefined;\n executeAddElement(pastedElement, artboardId, insertAfterId);\n pastedIds.push(pastedElement.id);\n });\n\n // Select the pasted element(s)\n if (pastedIds.length === 1) {\n selection.handleSelectionChange(pastedIds[0]);\n } else if (pastedIds.length > 1) {\n selection.setMultiSelection(pastedIds);\n }\n }, [\n clipboard,\n elementStore,\n artboardManager,\n executeAddElement,\n selection,\n ]);\n\n // --- Cross-context: clearExpandedPanel ---\n // Clear expanded panel and exit crop mode if needed\n // This is called by two-level deselect and can be used by ContextualToolbars\n const clearExpandedPanel = useCallback(() => {\n // If closing the crop panel, also exit crop mode on the element\n if (\n toolState.expandedPanelType === 'crop' &&\n selectedElement?.transformType === 'image'\n ) {\n const imageElement = selectedElement as ImageElement;\n if (imageElement.isCropping) {\n const updatedElement = imageElement.clone();\n updatedElement.exitCropMode();\n executeElementUpdate(imageElement, updatedElement);\n }\n }\n toolState.setExpandedPanelType(null);\n }, [\n toolState.expandedPanelType,\n toolState.setExpandedPanelType,\n selectedElement,\n executeElementUpdate,\n ]);\n\n // --- Document-level click handler for deselection ---\n // Implements two-level deselect: first closes expanded panel, then deselects element\n useEffect(() => {\n const handleDocumentPointerDown = (e: PointerEvent) => {\n const target = e.target as HTMLElement;\n\n // Skip canvas clicks - canvas has its own selection logic\n if (target.tagName === 'CANVAS') {\n return;\n }\n\n // Skip if clicking on preserved selection area (toolbars, drawers, etc.)\n const shouldPreserve = shouldPreserveSelection(target);\n if (shouldPreserve) {\n return;\n }\n\n\n // If a data-preserve-selection element is focused (e.g., text edit textarea),\n // blur it to exit edit mode, but don't deselect the element (two-level backout)\n const activeElement = document.activeElement as HTMLElement | null;\n if (activeElement?.hasAttribute('data-preserve-selection')) {\n activeElement.blur(); // Exit edit mode\n return; // Don't deselect - element stays selected\n }\n\n // Two-level deselect: if a panel is expanded, close it first (keep element selected)\n if (toolState.expandedPanelType !== null) {\n clearExpandedPanel();\n return;\n }\n\n // Second level: actually deselect elements.\n // Guarded: an unconditional setMultiSelection([]) allocates a new array\n // every pointerdown, forcing a re-render even when nothing is selected.\n // That re-render rebuilds Iconify SVG internals between mouseup and\n // click dispatch, so the browser suppresses the click — icon-only\n // buttons outside data-preserve-selection zones ate their first click.\n if (selection.selectedId !== null) {\n selection.setSelectedId(null);\n }\n if (selection.multiSelection.length > 0) {\n selection.setMultiSelection([]);\n }\n };\n\n document.addEventListener('pointerdown', handleDocumentPointerDown);\n return () =>\n document.removeEventListener('pointerdown', handleDocumentPointerDown);\n }, [toolState.expandedPanelType, clearExpandedPanel, selection]);\n\n // --- Load workspace handler ---\n const handleLoadWorkspace = useCallback(\n (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => {\n artboardManager.clear();\n\n loadedArtboards.forEach((artboard) => {\n artboardManager.createArtboard(artboard.toJSON());\n });\n\n if (activeArtboardId) {\n artboardManager.setActiveArtboard(activeArtboardId);\n }\n\n setArtboards([...artboardManager.getAllArtboards()]);\n setArtboardVersion((v) => v + 1);\n setElements(loadedElements);\n selection.setSelectedId(null);\n },\n [artboardManager, selection, setElements]\n );\n\n // --- Compose the full EditorContextValue ---\n // Memoized so `useEditor()` returns a STABLE object identity across renders\n // that don't change editor state. Without this, every render minted a new\n // value object; any consumer that put `editor` (or a member read off the\n // unstable object) into an effect/memo dep array — then set state — entered\n // an infinite \"Maximum update depth exceeded\" loop on mount. The deps below\n // list every value/state slice this object exposes; the callbacks are\n // already useCallback-stable. Pinned by useTextBinding.loop.repro.test.tsx.\n const value: EditorContextValue = useMemo(() => ({\n // From ViewportContext\n zoom: viewport.zoom,\n panOffset: viewport.panOffset,\n isPanning: viewport.isPanning,\n zoomIn: viewport.zoomIn,\n zoomOut: viewport.zoomOut,\n zoomToFit: viewport.zoomToFit,\n resetView: viewport.resetView,\n setZoom: viewport.setZoom,\n setPanOffset: viewport.setPanOffset,\n setIsPanning: viewport.setIsPanning,\n\n // From SelectionContext\n selectedId: selection.selectedId,\n multiSelection: selection.multiSelection,\n activeChildElement: selection.activeChildElement,\n hoveredElementId: selection.hoveredElementId,\n hideHandles: selection.hideHandles,\n setSelectedId: selection.setSelectedId,\n setMultiSelection: selection.setMultiSelection,\n setHoveredElementId: selection.setHoveredElementId,\n setHideHandles: selection.setHideHandles,\n handleSelectionChange: selection.handleSelectionChange,\n handleActiveChildChange: selection.handleActiveChildChange,\n\n // From ToolStateContext\n expandedPanelType: toolState.expandedPanelType,\n setExpandedPanelType: toolState.setExpandedPanelType,\n isToolbarMenuOpen: toolState.isToolbarMenuOpen,\n setIsToolbarMenuOpen: toolState.setIsToolbarMenuOpen,\n textSelectionVersion: toolState.textSelectionVersion,\n setTextSelectionVersion: toolState.setTextSelectionVersion,\n isCanvasReady: toolState.isCanvasReady,\n setCanvasReady: toolState.setCanvasReady,\n isRotating: toolState.isRotating,\n setIsRotating: toolState.setIsRotating,\n\n // Cross-context handler\n clearExpandedPanel,\n\n // Core state (element/artboard domain)\n artboardManager,\n artboards,\n elements,\n elementStore,\n getElementById,\n\n // Derived state (cross-context)\n selectedElement,\n\n // Refs\n canvasRef,\n canvasEditorRef,\n\n // Element setters\n setElements,\n\n // Artboard version counter\n artboardVersion,\n\n // Handlers\n refreshArtboards,\n handleElementUpdate,\n addElement,\n handleAddElement,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\n hasClipboardContent: clipboard.length > 0,\n\n // Command history (undo/redo actions + execute functions)\n historyManager,\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n\n // Element properties\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n }), [\n // Viewport\n viewport.zoom, viewport.panOffset, viewport.isPanning, viewport.zoomIn,\n viewport.zoomOut, viewport.zoomToFit, viewport.resetView, viewport.setZoom,\n viewport.setPanOffset, viewport.setIsPanning,\n // Selection\n selection.selectedId, selection.multiSelection, selection.activeChildElement,\n selection.hoveredElementId, selection.hideHandles, selection.setSelectedId,\n selection.setMultiSelection, selection.setHoveredElementId,\n selection.setHideHandles, selection.handleSelectionChange,\n selection.handleActiveChildChange,\n // Tool state\n toolState.expandedPanelType, toolState.setExpandedPanelType,\n toolState.isToolbarMenuOpen, toolState.setIsToolbarMenuOpen,\n toolState.textSelectionVersion, toolState.setTextSelectionVersion,\n toolState.isCanvasReady, toolState.setCanvasReady,\n toolState.isRotating, toolState.setIsRotating,\n clearExpandedPanel,\n // Element/artboard domain\n artboardManager, artboards, elements, elementStore, getElementById,\n selectedElement, canvasRef, canvasEditorRef, setElements, artboardVersion,\n refreshArtboards, handleElementUpdate, addElement, handleAddElement,\n handleLoadWorkspace, handleCopyElements, handlePasteElements, clipboard,\n // Command history\n historyManager, executeElementUpdate, executeAddElement, executeRemoveElement,\n executeReorderElement, executeCommandBatch, executeCreateArtboard,\n executeDeleteArtboard, executeUpdateArtboard, undo, redo, canUndo, canRedo,\n undoActiveArtboard, redoActiveArtboard, canUndoActiveArtboard,\n canRedoActiveArtboard,\n // Element properties\n fontSize, fontColor, fontFamily, textAlign, updateFontSize, setFontSizeValue,\n updateFontColor, updateFontFamily, updateTextAlign,\n ]);\n\n // Stable lazy handles (re-render containment, ADR-0085 / D3): the ref\n // tracks the latest fat value; the context VALUE identity never changes, so\n // consumers never re-render from editor state churn. For chrome that only\n // needs editor state inside event handlers (Editor.Menu / Editor.Add —\n // save, export, add-element), not at render time.\n handlesRef.current = value;\n\n return (\n <EditorHandlesContext.Provider value={handles}>\n <HistoryProvider\n value={{\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n }}\n >\n <ElementsProvider\n value={{\n elements,\n elementStore,\n artboards,\n artboardManager,\n artboardVersion,\n setElements,\n refreshArtboards,\n getElementById,\n handleAddElement,\n handleElementUpdate,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n canvasRef,\n canvasEditorRef,\n }}\n >\n <CommandProvider\n value={{\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n historyManager,\n }}\n >\n <EditorContext.Provider value={value}>{children}</EditorContext.Provider>\n </CommandProvider>\n </ElementsProvider>\n </HistoryProvider>\n </EditorHandlesContext.Provider>\n );\n};\n\n/**\n * Provides editor state and operations to all descendant components.\n *\n * Composes sub-providers (ViewportProvider, SelectionProvider, ToolStateProvider,\n * HistoryProvider) around an internal EditorProvider. This gives each sub-context\n * its own React context, so changes to viewport state (e.g., zoom) won't re-render\n * components that only subscribe to selection state, and vice versa.\n *\n * Must wrap any component that calls `useEditor()` or any of the convenience\n * hooks (`useArtboards`, `useLayers`, `useViewportContext`, etc.).\n *\n * @example\n * ```tsx\n * import { EditorProvider, Canvas, useEditor } from '@snowcone-app/canvas';\n *\n * function MyEditor() {\n * return (\n * <EditorProvider initialArtboardConfig={{ width: 1200, height: 1200 }}>\n * <Canvas style={{ width: '100%' }} />\n * <CustomToolbar />\n * </EditorProvider>\n * );\n * }\n * ```\n */\nexport const EditorProvider: React.FC<EditorProviderProps> = ({\n children,\n initialArtboardConfig = {},\n viewPadding = 0.9,\n}) => {\n // Initialize artboard manager synchronously to avoid race conditions\n const [artboardManager] = useState(() => {\n const manager = new ArtboardManager();\n const config = {\n name: initialArtboardConfig.name || 'Artboard 1',\n x: initialArtboardConfig.x ?? 0,\n y: initialArtboardConfig.y ?? 0,\n width: initialArtboardConfig.width ?? 1000,\n height: initialArtboardConfig.height ?? 1000,\n backgroundColor:\n initialArtboardConfig.backgroundColor || DEFAULT_ARTBOARD_COLOR,\n };\n manager.createArtboard(config);\n return manager;\n });\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const canvasEditorRef = useRef<CanvasEditorHandle>(null);\n\n return (\n <ViewportProvider\n artboardManager={artboardManager}\n canvasRef={canvasRef}\n viewPadding={viewPadding}\n >\n <SelectionProvider>\n <ToolStateProvider>\n <InternalEditorProvider\n artboardManager={artboardManager}\n canvasRef={canvasRef}\n canvasEditorRef={canvasEditorRef}\n >\n {children}\n </InternalEditorProvider>\n </ToolStateProvider>\n </SelectionProvider>\n </ViewportProvider>\n );\n};\n\n/**\n * Access the full editor context: elements, selection, artboards, history, and operations.\n *\n * ## Stability contract (ADR-0085)\n *\n * `useEditor()` is the headless floor of the `Editor.*` primitives. Its v1\n * STABLE subset — pinned by the type-level snapshot in\n * `components/editor/__tests__/useEditorStableSubset.test.ts` — is:\n *\n * - **Elements**: `elements`, `elementStore`, `getElementById`\n * - **Selection**: `selectedId`, `multiSelection`, `selectedElement`,\n * `setSelectedId`, `setMultiSelection`, `handleSelectionChange`\n * - **Command execution**: `executeElementUpdate`, `executeAddElement`,\n * `executeRemoveElement`, `executeReorderElement`, `executeCommandBatch`\n * - **Undo/redo**: `undo`, `redo`, `canUndo`, `canRedo`\n *\n * Everything else on the returned value is `@experimental` and may change in\n * minors — the stable subset above is the documented headless contract.\n *\n * Must be called inside an `EditorProvider`. For focused access, prefer the\n * sub-context hooks which expose smaller slices of state and cause fewer re-renders:\n *\n * - `useViewportContext()` - zoom, pan\n * - `useSelectionContext()` - selection, hover\n * - `useHistoryContext()` - undo, redo\n * - `useToolStateContext()` - panel state, toolbar menus\n * - `useElementsContext()` - elements, artboards, element operations, font properties\n * - `useCommandContext()` - command execution (execute*), undo/redo, historyManager\n *\n * Or the convenience hooks:\n * - `useArtboards()`, `useLayers()`, `useExport()`, `useCommands()`\n * - `useSelectedElement()`, `useElementById()`, `useCanvasReady()`, `useViewport()`\n *\n * @returns The complete editor context value\n * @throws {Error} If called outside of an `EditorProvider`\n *\n * @example\n * ```tsx\n * function StatusBar() {\n * const { elements, selectedId, zoom, canUndo, canRedo, undo, redo } = useEditor();\n *\n * return (\n * <div>\n * <span>{elements.length} elements</span>\n * <span>Zoom: {Math.round(zoom * 100)}%</span>\n * <button onClick={undo} disabled={!canUndo}>Undo</button>\n * <button onClick={redo} disabled={!canRedo}>Redo</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useEditor = (): EditorContextValue => {\n const context = useContext(EditorContext);\n if (!context) {\n throw new Error('useEditor must be used within EditorProvider');\n }\n return context;\n};\n","/**\n * Test-side hooks into the `Editor.*` error contract (ADR-0085 / D11).\n *\n * Containment is a PROD feature: under `NODE_ENV=test`, Root's boundary\n * rethrows render-time composition errors, so a broken composition fails the\n * test naturally. The remaining blind spot is the NON-throwing channel —\n * runtime failures routed straight to the error card (e.g. the lazy renderer\n * chunk failing to load, `E_CHUNK_LOAD_FAILED`) and prod-routed handler\n * invariants. Those are recorded here in test envs so\n * {@link expectNoEditorErrors} can fail a green-looking test that actually\n * rendered an error card.\n *\n * ```ts\n * import { expectNoEditorErrors } from '@snowcone-app/canvas/testing';\n *\n * afterEach(() => expectNoEditorErrors());\n * ```\n */\n\nimport { isEditorError, isDevOrTest } from '../errors/editorError.js';\n\nconst recorded: Error[] = [];\n\n/** Record a contained editor error (test/dev envs only). @internal */\nexport function recordContainedEditorError(error: Error): void {\n if (isDevOrTest()) recorded.push(error);\n}\n\n/** Errors contained since the last drain, without draining. */\nexport function getContainedEditorErrors(): readonly Error[] {\n return [...recorded];\n}\n\n/** Drop all recorded errors (e.g. after asserting an EXPECTED failure). */\nexport function resetContainedEditorErrors(): void {\n recorded.length = 0;\n}\n\n/**\n * Assert that no editor error was contained (rendered as an error card)\n * since the last reset/call. Drains the record either way so one leaked\n * error doesn't fail every later test in the file.\n */\nexport function expectNoEditorErrors(): void {\n if (recorded.length === 0) return;\n const summary = recorded\n .map((e) => (isEditorError(e) ? `${e.code}: ${e.message}` : `${e.name}: ${e.message}`))\n .join('\\n - ');\n recorded.length = 0;\n throw new Error(\n `expectNoEditorErrors: ${summary.includes('\\n') ? 'errors were' : 'an error was'} contained by ` +\n `<Editor.Root>'s boundary during this test:\\n - ${summary}\\n` +\n 'A contained error renders an error card in prod — this composition is broken even if the test rendered \"successfully\".',\n );\n}\n","/**\n * Serialize canvas state (from onChange/toJSON) to the format the export worker expects.\n *\n * This bridges the gap between SnowconeCanvas.onChange() output (element configs with\n * transformType and nested transformData) and the export worker's expected format\n * (elements with 'type' field and flattened image properties).\n *\n * Use this when sending canvas state to a server-side renderer that runs the export\n * worker bundle. The client-side serializeForWorkerExport() requires live element\n * instances; this function works on plain JSON from onChange/toJSON.\n */\n\n// Pure value helper from the MODEL side (core) — serialize-for-server is part\n// of the always-mounted state graph (D9 exemption), so it must not pull the\n// heavy pixel pipeline in `rendering/image-adjustments.ts`.\nimport { isNeutralAdjustments } from '../core/image-adjustments.js';\n\n/**\n * Wire-schema version of the server-render `canvas_state` payload (ADR-0079).\n *\n * This is a PUBLIC contract: third parties send `ServerRenderRequest` (produced\n * by {@link serializeStateForServer}) to Snowcone's realtime renderer. Bump the\n * MAJOR whenever the element/artboard shape changes incompatibly so a renderer\n * can reject states it cannot render instead of silently mis-rendering. Servers\n * treat a MISSING `schemaVersion` as `1` — legacy clients that predate the\n * stamp keep working.\n */\nexport const CANVAS_STATE_SCHEMA_VERSION = 1;\n\n// NOTE: this is a `type`, not an `interface`, ON PURPOSE. The SDK's\n// `RenderSession.renderState(placement, state)` takes a `RenderState` that has a\n// string index signature (`[key: string]: unknown`). A named `interface` does\n// NOT get TypeScript's implicit index signature, so an `interface` value is not\n// assignable to an indexed type — passing `serializeStateForServer()`'s result\n// straight into `renderState()` (the headline realtime flow) fails with TS2345\n// \"Index signature for type 'string' is missing\". A `type` alias DOES get the\n// implicit signature, so the flow type-checks with no cast. Do not convert this\n// back to `interface`. Pinned by @snowcone-app/contracts\n// (src/realtime-getting-started.contract.ts).\nexport type ServerRenderRequest = {\n /** Wire-schema version — see {@link CANVAS_STATE_SCHEMA_VERSION}. */\n schemaVersion: number;\n artboards: Array<{\n id: string;\n name: string;\n x: number;\n y: number;\n width: number;\n height: number;\n backgroundColor: string;\n exportBackground: boolean;\n elements: any[];\n distressTexture?: any;\n imageMask?: any;\n }>;\n /** Custom (brand) font dependencies — the server worker's loadFonts needs\n * these to fetch non-Google faces. Omitted when the design uses none. */\n customFonts?: Array<{ family: string; url: string; weight?: number; style?: string }>;\n};\n\n/**\n * Quantize a geometry value to a stable precision (0.01px).\n *\n * `SnowconeCanvas.onChange` fires on the render loop and reports element\n * geometry with sub-pixel float drift (e.g. `height: 355.9609375`) even when the\n * design is idle. Serialized verbatim, every frame produces a *different* wire\n * state, so a `onChange → renderState` wiring streams renders continuously while\n * the user does nothing — silently burning the realtime render budget. Rounding\n * geometry to a precision well below a pixel collapses that drift so identical\n * idle frames serialize identically (and the SDK's send-dedupe drops them).\n */\nfunction q(n: unknown): unknown {\n return typeof n === 'number' && Number.isFinite(n) ? Math.round(n * 100) / 100 : n;\n}\n\n/** Return a shallow copy with geometry fields quantized (see {@link q}). */\nfunction quantizeGeometry<T extends Record<string, any>>(el: T): T {\n return {\n ...el,\n ...(el.x !== undefined && { x: q(el.x) }),\n ...(el.y !== undefined && { y: q(el.y) }),\n ...(el.width !== undefined && { width: q(el.width) }),\n ...(el.height !== undefined && { height: q(el.height) }),\n ...(el.rotation !== undefined && { rotation: q(el.rotation) }),\n };\n}\n\n/**\n * Convert onChange canvas state to the server render format.\n *\n * Emits EVERY artboard in the state, each holding its own elements: elements\n * are grouped by their `artboard` tag (the artboard NAME `SnowconeCanvas.onChange`\n * stamps on each element). Elements without a tag — legacy saved states — land\n * on the first artboard, which also keeps single-artboard output byte-identical\n * to before. The tag itself is client-side metadata and is stripped from the\n * wire elements, so the per-placement `{schemaVersion, artboards: [...]}` shape\n * the server consumes is unchanged.\n *\n * For a multi-placement product, slice the result per placement:\n *\n * ```ts\n * const wire = serializeStateForServer(state);\n * for (const ab of wire.artboards) {\n * session.renderState(ab.name, { schemaVersion: wire.schemaVersion, artboards: [ab] });\n * }\n * ```\n *\n * @param state - The state from SnowconeCanvas onChange callback\n * @returns ServerRenderRequest ready to send via sendCanvasState()\n */\nexport function serializeStateForServer(state: {\n elements?: any[];\n artboards?: Array<{\n name?: string;\n width?: number;\n height?: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n clipShape?: any;\n distressTexture?: any;\n imageMask?: any;\n /** Pacing role (multi-page templates, ADR-0086) — client/template\n * metadata, stripped from the wire like the element `artboard` tag. */\n role?: string;\n }>;\n activeArtboard?: string;\n customFonts?: Array<{ family: string; url: string; weight?: number; style?: string }>;\n}): ServerRenderRequest {\n const srcArtboards = state.artboards?.length\n ? state.artboards\n : [{ name: 'Front', width: 800, height: 800 }];\n\n // Group elements by their artboard tag (artboard NAME, stamped by\n // SnowconeCanvas.onChange). Untagged or unknown-tagged elements fall back to\n // the first artboard — today's behavior for legacy flat states.\n const elementsByArtboard = srcArtboards.map<any[]>(() => []);\n for (const el of state.elements || []) {\n const tag = el?.artboard;\n const idx = typeof tag === 'string'\n ? srcArtboards.findIndex((ab) => (ab.name || 'Front') === tag)\n : -1;\n elementsByArtboard[idx >= 0 ? idx : 0].push(serializeElement(el));\n }\n\n // Wire artboards are built field-by-field: template metadata on the source\n // artboards (the `role` pacing tag, ADR-0086) never reaches the server —\n // same rule as the element `artboard` membership tag below.\n return {\n schemaVersion: CANVAS_STATE_SCHEMA_VERSION,\n artboards: srcArtboards.map((srcArtboard, i) => ({\n id: `artboard-${i + 1}`,\n name: srcArtboard.name || 'Front',\n x: 0,\n y: 0,\n width: q(srcArtboard.width || 800) as number,\n height: q(srcArtboard.height || 800) as number,\n backgroundColor: srcArtboard.backgroundColor || 'transparent',\n // Pass the artboard's flag through — the server worker only paints the\n // background when this is true. Hardcoding `false` here silently dropped\n // explicitly-set artboard backgrounds from every realtime mockup while\n // the editor kept showing them (WYSIWYG drift).\n exportBackground: srcArtboard.exportBackground ?? false,\n elements: elementsByArtboard[i],\n distressTexture: srcArtboard.distressTexture,\n imageMask: srcArtboard.imageMask,\n })),\n // Custom (brand) font dependencies ride the wire so the server worker can\n // load them — without this, server renders of custom-font designs fall\n // back to the wrong family even when the bundle supports custom fonts.\n ...(state.customFonts?.length ? { customFonts: state.customFonts } : {}),\n };\n}\n\n/**\n * Serialize a single element from toJSON() format to export worker format.\n */\nfunction serializeElement(el: any): any {\n const type = el.type || el.transformType;\n\n if (type === 'image') {\n return serializeImageElement(el);\n }\n\n // Text, shape, group, path elements: map transformType → type, keep everything else\n // Text, shape, group, path elements: pass through as-is with type mapped.\n // Builtin mask URLs (builtin-mask:*) are kept as-is — the server worker\n // generates them procedurally via generateMaskBitmap().\n // Geometry is quantized (see `q`) so idle render-loop float drift doesn't\n // produce a new wire state every frame.\n // The `artboard` membership tag is CLIENT-side metadata (it already decided\n // which wire artboard this element belongs to) — strip it so the server\n // payload shape stays unchanged.\n const { artboard: _artboard, ...rest } = el;\n return quantizeGeometry({\n ...rest,\n type,\n });\n}\n\n/**\n * Serialize an image element, flattening transformData to top-level fields\n * as expected by SerializedImageElement and renderImageElement.\n */\nfunction serializeImageElement(el: any): any {\n const td = el.transformData || {};\n const hasMasks = el.masks && el.masks.length > 0;\n\n // Visible dimensions depend on crop and masks\n const fullWidth = td.width || 0;\n const fullHeight = td.height || 0;\n const visibleWidth = hasMasks ? fullWidth : fullWidth * (td.cropWidth ?? 1);\n const visibleHeight = hasMasks ? fullHeight : fullHeight * (td.cropHeight ?? 1);\n\n const serialized: any = {\n id: el.id,\n type: 'image' as const,\n x: el.x || 0,\n y: el.y || 0,\n width: visibleWidth,\n height: visibleHeight,\n rotation: el.rotation || 0,\n imageUrl: el.imageUrl,\n imageAspectRatio: el.imageAspectRatio,\n };\n\n // Crop (only if actually cropped and no masks)\n const isCropped = !hasMasks && (td.cropX !== 0 || td.cropY !== 0 || td.cropWidth !== 1 || td.cropHeight !== 1);\n if (isCropped) {\n // Note: crop values stay as fractions here — the server's Chrome tab\n // will convert to pixels using the actual bitmap dimensions after fetch.\n // The client doesn't have the bitmap dimensions available in onChange state.\n serialized.cropX = td.cropX;\n serialized.cropY = td.cropY;\n serialized.cropWidth = td.cropWidth;\n serialized.cropHeight = td.cropHeight;\n serialized.needsCropPixelConversion = true; // Flag for server to convert\n }\n\n // Flip and border radius\n if (td.flipHorizontal) serialized.flipHorizontal = td.flipHorizontal;\n if (td.flipVertical) serialized.flipVertical = td.flipVertical;\n if (td.borderRadius) serialized.borderRadius = td.borderRadius;\n // Drop shadow — non-image elements pass it through via the {...rest} spread;\n // this allowlist is the only place an image element's shadow could get lost.\n if (el.shadow) serialized.shadow = el.shadow;\n // Brightness/contrast/saturation — applied by the server worker's\n // renderImageElement after it fetches the bitmap. Omitted when neutral;\n // older renderers ignore the unknown field (additive wire change).\n if (!isNeutralAdjustments(td.adjustments)) serialized.adjustments = { ...td.adjustments };\n if (el.opacity !== undefined) serialized.opacity = el.opacity;\n\n // Effects\n if (el.distressEffect) serialized.distressEffect = el.distressEffect;\n if (el.masks?.length > 0) serialized.masks = el.masks;\n if (el.blendMode) serialized.blendMode = el.blendMode;\n if (el.knockoutParts) serialized.knockoutParts = el.knockoutParts;\n if (el.stroke) serialized.stroke = el.stroke;\n\n // Quantize geometry so idle render-loop float drift doesn't churn the wire state.\n return quantizeGeometry(serialized);\n}\n","/**\n * Declarative design builder — compose a multi-element design by *intent*\n * (full-bleed background, anchored text, anchored shapes) and never by raw\n * coordinates.\n *\n * WHY THIS EXISTS\n * ---------------\n * The canvas/render contract positions every element by its CENTER point\n * (`x, y` is the centre, not the top-left — see CLAUDE.md). That convention is\n * correct for the interactive editor (rotation/scale pivot around centre) but is\n * a foot-gun for anyone hand-constructing a `canvas_state`: the natural\n * \"fill the placement\" attempt (`x:0, y:0, width:full`) centres the element on\n * the artboard's top-left CORNER, so it renders into the top-left quadrant. An\n * expert integrator hit exactly this and mis-diagnosed it as a server bug.\n *\n * This builder makes that class of mistake impossible: you describe *what* you\n * want (a background that fills, a title anchored top-centre, a star bottom-left)\n * and it emits a correct editor-shape canvas state. The centre-coordinate maths\n * lives here, once, instead of in every integrator's head.\n *\n * THREE RENDER TARGETS, ONE DESIGN\n * --------------------------------\n * The builder emits the EDITOR shape (`transformType` + nested `transformData`,\n * centre coords), which is exactly what both consumers already accept:\n * - `design.toJSON()` → `<SnowconeCanvas initialElements=… />` (interactive)\n * - `design.toServerRequest()` → `session.renderState(placement, …)` (realtime)\n * - (single asset on a placement → use `buildMockupUrl` in the SDK instead)\n *\n * So a developer can author a design headlessly, render it live, AND hand the\n * same design to the editor for the end user to drag/scale/rotate from there.\n */\n\nimport type {\n AnyElementConfig,\n ArtboardRole,\n ImageElementConfig,\n CustomElementConfig,\n ShapeElementConfig,\n ShapeTransformData,\n} from '../types/index.js';\nimport { serializeStateForServer, type ServerRenderRequest } from '../rendering/serialize-for-server.js';\nimport { EditorError } from '../errors/editorError.js';\n\n/** A nine-point anchor: where the element sits relative to the artboard. */\nexport type Anchor =\n | 'top-left' | 'top' | 'top-right'\n | 'left' | 'center' | 'right'\n | 'bottom-left' | 'bottom' | 'bottom-right';\n\nexport type ShapeKind =\n | 'rectangle' | 'circle' | 'ellipse' | 'triangle' | 'polygon' | 'star' | 'line';\n\n/** The artboard a design is composed on — one print placement. `width`/`height`\n * are the placement's pixel dimensions (e.g. 1480×2328 for a phone-case Front).\n * `name` MUST match the placement label you render (case-sensitive). */\nexport interface DesignArtboard {\n /** Placement label (case-sensitive — e.g. 'Front'). Defaults to 'Front'. */\n name?: string;\n width: number;\n height: number;\n /** Optional solid background fill behind all elements. Rendered everywhere\n * the design is — editor, exports, and server renders (realtime mockups).\n * NOTE: the background is not an element, so it is NOT repaintable by\n * `useColorBinding` — designs that want a rebindable backdrop paint a\n * full-bleed `shape('rectangle', { fill: … })` instead. */\n backgroundColor?: string | ColorToken;\n /** Named color ROLES for value-matched rebinding (`useColorBinding`).\n * Mint tokens with `design.color('role')`; use them as `fill`/`color`\n * values so role and element can never drift. Validated at mint:\n * duplicate values across roles throw `E_COLOR_ROLE_COLLISION` (a\n * value-matched binding can't tell colliding roles apart), non-hex\n * values throw `E_COLOR_ROLE_INVALID`. */\n colors?: Record<string, string>;\n /** Pacing role in a multi-page template (ADR-0086 albums). Template\n * metadata only — never sent on the server render wire. */\n role?: ArtboardRole;\n}\n\n/** One artboard of a saved multi-artboard state, as {@link DesignBuilder.fromState}\n * accepts and `toJSON()` emits. */\nexport interface StateArtboard {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n /** Pacing role in a multi-page template (ADR-0086 albums). */\n role?: ArtboardRole;\n}\n\nexport interface FromStateOptions {\n /**\n * Which artboards to load:\n * - `'active'` (default) — today's behavior: the active artboard becomes the\n * builder's artboard; elements tagged for other artboards are skipped.\n * - `'all'` — multi-page templates (ADR-0086): every artboard and its\n * elements are kept, with `artboard` membership tags preserved, so\n * `toJSON()` / `toServerRequest()` stay multi-artboard. Template fields\n * scope per page via `TemplateField.artboard`.\n */\n artboards?: 'active' | 'all';\n}\n\n/** A stable layer name. Set it so you can bind the layer in an interactive\n * `<SnowconeCanvas/>` editor — `useImageBinding(name)` / `useTextBinding(name)`\n * find the element by `name`. Keep names unique per design. */\ntype NamedOption = { name?: string };\n\nexport interface BackgroundOptions extends NamedOption {\n /** Source image aspect ratio (w/h). When given, the image is COVER-fit (filled\n * + centre-cropped) with no distortion. When omitted, the image fills the\n * artboard box directly (correct when it's already the placement's aspect,\n * e.g. an AI image generated at the placement size). */\n aspectRatio?: number;\n opacity?: number;\n}\n\nexport interface ImageOptions extends NamedOption {\n anchor?: Anchor;\n /** Display width in artboard px. Defaults to a third of the artboard width. */\n width?: number;\n /** Display height in px. Defaults to `width / aspectRatio` (or square). */\n height?: number;\n aspectRatio?: number;\n opacity?: number;\n rotation?: number;\n /** Inset from the artboard edge for non-centre anchors, in px. Default 0. */\n padding?: number;\n}\n\nexport interface TextOptions extends NamedOption {\n anchor?: Anchor;\n fontSize?: number;\n fontFamily?: string;\n /** Hex string, or a `design.color('role')` token (resolved at build). */\n color?: string | ColorToken;\n /** Horizontal text alignment. Named `textAlign` to match the canvas editor's\n * element field (not `align`), so the builder and the editor read the same. */\n textAlign?: 'left' | 'center' | 'right';\n bold?: boolean;\n italic?: boolean;\n rotation?: number;\n padding?: number;\n}\n\nexport interface ShapeOptions extends NamedOption {\n anchor?: Anchor;\n /** Square size, or explicit box. Default 200×200. */\n size?: number | { width: number; height: number };\n /** Hex string, or a `design.color('role')` token (resolved at build). */\n fill?: string | ColorToken;\n rotation?: number;\n opacity?: number;\n /** star points / polygon sides. */\n points?: number;\n sides?: number;\n padding?: number;\n}\n\nconst DEFAULT_ANCHOR: Anchor = 'center';\n\n// ── Typed layer tokens (ADR-0085 §1 / T1) ────────────────────────────────────\n\n/** The bindable layer kinds. Shapes are not bindable in v1. */\nexport type LayerKind = 'text' | 'image';\n\n/**\n * Any design builder, regardless of its compile-time layer-name unions.\n * CONSUMERS of a builder (props, context) must use this — `DesignBuilder`\n * (= `DesignBuilder<string, string>`) is not a supertype of a statically\n * typed builder because `layer()`'s name constraint narrows with the design.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance escape: `any` here means \"accept every name union\", checked at mint/mount instead\nexport type AnyDesign = DesignBuilder<any, any>;\n\ndeclare const LAYER_TOKEN_BRAND: unique symbol;\n/** Runtime marker carried by every minted token (survives serialization\n * boundaries within one realm; `Symbol.for` so duplicated module instances\n * still agree). */\nconst LAYER_TOKEN_MARKER = Symbol.for('snowcone.canvas.layerToken');\n\n/**\n * A validated claim that layer `N` of kind `K` exists in the design that\n * minted it. The ONLY currency `Editor.TextField` (and future `ImageField`)\n * accepts — bare strings are rejected (`E_LAYER_TOKEN_REQUIRED`). Mint with\n * {@link DesignBuilder.layer}; not constructible by hand (branded).\n *\n * Stable since ADR-0085 P4 (the default editor is rebuilt from this public\n * surface, CI-gated); additive-only from here.\n */\nexport interface LayerToken<K extends LayerKind = LayerKind, N extends string = string> {\n readonly kind: K;\n readonly name: N;\n /** @internal */\n readonly [LAYER_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by parts before trusting a `layer` prop. */\nexport function isLayerToken(value: unknown): value is LayerToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[LAYER_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n ((value as { kind?: unknown }).kind === 'text' || (value as { kind?: unknown }).kind === 'image')\n );\n}\n\nfunction mintLayerToken(kind: LayerKind, name: string): LayerToken {\n return Object.freeze({ kind, name, [LAYER_TOKEN_MARKER]: true }) as unknown as LayerToken;\n}\n\n// ── Color-role tokens ────────────────────────────────────────────────────────\n\ndeclare const COLOR_TOKEN_BRAND: unique symbol;\nconst COLOR_TOKEN_MARKER = Symbol.for('snowcone.canvas.colorToken');\n\n/**\n * A validated claim that color role `N` exists in the design that minted it,\n * carrying the role's (normalized) hex value. `useColorBinding` accepts it in\n * place of a bare hex, so the chip and the painted elements can never drift —\n * and `design({ colors })` rejects role-value collisions at mint time, which a\n * value-matched binding cannot survive (one role's repaint would capture the\n * other role's elements). Mint with {@link DesignBuilder.color}; not\n * constructible by hand (branded).\n */\nexport interface ColorToken<N extends string = string> {\n readonly name: N;\n /** Normalized uppercase `#RRGGBB`. */\n readonly value: string;\n /** @internal */\n readonly [COLOR_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by `useColorBinding` (and the builder's value slots)\n * before trusting a color value. */\nexport function isColorToken(value: unknown): value is ColorToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[COLOR_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { value?: unknown }).value === 'string'\n );\n}\n\nfunction mintColorToken(name: string, value: string): ColorToken {\n return Object.freeze({ name, value, [COLOR_TOKEN_MARKER]: true }) as unknown as ColorToken;\n}\n\n/** Normalize `#RGB`/`#RRGGBB` (any case) to uppercase `#RRGGBB` — the same\n * normalization `useColorBinding` matches with. Returns null when the value\n * is not a parsable hex color. */\nexport function normalizeRoleHex(color: string): string | null {\n let hex = color.trim().toUpperCase();\n if (/^#[0-9A-F]{3}$/.test(hex)) {\n hex = '#' + hex[1]! + hex[1]! + hex[2]! + hex[2]! + hex[3]! + hex[3]!;\n }\n return /^#[0-9A-F]{6}$/.test(hex) ? hex : null;\n}\n\n/** A color value as builder options accept it: a hex string or a minted role\n * token. Resolved to the plain hex at element-build time. */\nfunction resolveColor(value: string | ColorToken): string;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined {\n return isColorToken(value) ? value.value : value;\n}\n\n/** The token kind inferred for name `N` against the builder's accumulated\n * per-kind name unions. A dynamically-loaded design (both unions degraded to\n * `string`) yields the full `'text' | 'image'` union — typing degrades, the\n * runtime validation in `layer()` is the net that still fires (0G#2). */\ntype InferKind<N extends string, TText extends string, TImage extends string> =\n [N] extends [TText] ? ([N] extends [TImage] ? LayerKind : 'text') : 'image';\n\n/** Kind of an element config, for token validation. `null` = not bindable. */\nfunction elementLayerKind(el: AnyElementConfig): LayerKind | null {\n const t = (el as { transformType?: string }).transformType;\n if (t === 'image') return 'image';\n if (t === 'shape' || t === 'group' || t === 'path') return null;\n // Every text transform ('custom', 'circle', 'arch', …) is a text layer.\n return 'text';\n}\n\nfunction hPart(a: Anchor): 'left' | 'center' | 'right' {\n if (a === 'top-left' || a === 'left' || a === 'bottom-left') return 'left';\n if (a === 'top-right' || a === 'right' || a === 'bottom-right') return 'right';\n return 'center';\n}\n\nfunction vPart(a: Anchor): 'top' | 'middle' | 'bottom' {\n if (a === 'top-left' || a === 'top' || a === 'top-right') return 'top';\n if (a === 'bottom-left' || a === 'bottom' || a === 'bottom-right') return 'bottom';\n return 'middle';\n}\n\n/**\n * Resolve an element's CENTRE (x, y) so its bounding box sits at `anchor` on the\n * artboard, inset by `padding`. This is the whole point of the builder: a\n * `center` anchor on a full-bleed element returns the artboard centre, never\n * (0, 0).\n */\nfunction anchorToCenter(\n anchor: Anchor,\n elW: number,\n elH: number,\n artW: number,\n artH: number,\n padding: number,\n): { x: number; y: number } {\n const h = hPart(anchor);\n const v = vPart(anchor);\n const x = h === 'left' ? elW / 2 + padding\n : h === 'right' ? artW - elW / 2 - padding\n : artW / 2;\n const y = v === 'top' ? elH / 2 + padding\n : v === 'bottom' ? artH - elH / 2 - padding\n : artH / 2;\n return { x, y };\n}\n\n/** Compute the centred cover-crop (fractions, 0–1) for a source of aspect `ar`\n * filling an `artW×artH` box, plus the full (uncropped) display size the editor\n * stores in `transformData`. Mirrors the editor's cover behaviour but needs only\n * the aspect ratio, not the pixel dimensions. */\nfunction coverCrop(ar: number, artW: number, artH: number) {\n const artAR = artW / artH;\n if (ar >= artAR) {\n // Source is wider than the box → crop the sides.\n const fullW = artH * ar;\n const cropWidth = artW / fullW; // = artAR / ar\n return {\n width: fullW, height: artH,\n cropX: (1 - cropWidth) / 2, cropY: 0, cropWidth, cropHeight: 1,\n };\n }\n // Source is taller than the box → crop top/bottom.\n const fullH = artW / ar;\n const cropHeight = artH / fullH; // = ar / artAR\n return {\n width: artW, height: fullH,\n cropX: 0, cropY: (1 - cropHeight) / 2, cropWidth: 1, cropHeight,\n };\n}\n\n/**\n * A composed design. Add elements by intent, then emit it for the editor or the\n * render path. Methods are chainable.\n *\n * The two type parameters accumulate the design's NAMED layer literals per\n * kind (ADR-0085 §1): naming a layer in `.text()` / `.image()` /\n * `.background()` widens the corresponding union, so `design.layer(\"naem\")`\n * is a compile error listing the valid names. Designs loaded from JSON\n * ({@link DesignBuilder.fromState}) degrade both unions to `string` — same\n * API, runtime-validated instead (the degradation is deliberate and LOUD in\n * docs; never block the dynamic case).\n */\nexport class DesignBuilder<\n out TText extends string = string,\n out TImage extends string = string,\n out TColor extends string = string,\n> {\n /** The CURRENT authoring artboard — anchor math and new elements target it.\n * Reassigned by {@link DesignBuilder.artboard}; the initially-active\n * placement stays {@link activeName}. */\n private current: Required<Pick<DesignArtboard, 'name' | 'width' | 'height'>> & {\n backgroundColor?: string;\n role?: ArtboardRole;\n };\n /** The placement that is active when the design mounts — always the FIRST\n * artboard (or the saved state's active one), never moved by `.artboard()`. */\n private readonly activeName: string;\n private readonly els: AnyElementConfig[] = [];\n /** Declared color roles, name → normalized uppercase `#RRGGBB`. */\n private readonly colorRoles = new Map<string, string>();\n private idCounter = 0;\n /** Every artboard of a multi-artboard design — set by `fromState({ artboards:\n * 'all' })` or the first `.artboard()` call; unset for single-artboard\n * builders. */\n private allArtboards?: StateArtboard[];\n\n constructor(artboard: DesignArtboard) {\n if (!(artboard.width > 0) || !(artboard.height > 0)) {\n throw new Error('design(): artboard width and height must be positive numbers');\n }\n for (const [roleName, raw] of Object.entries(artboard.colors ?? {})) {\n const value = normalizeRoleHex(raw);\n if (value === null) {\n throw new EditorError(\n 'E_COLOR_ROLE_INVALID',\n `color role \"${roleName}\" is ${JSON.stringify(raw)} — roles must be #RGB/#RRGGBB hex ` +\n `(useColorBinding matches by normalized hex value; other syntaxes can't bind).`,\n { roleName, value: raw, docSlug: 'embed-mode' },\n );\n }\n const collision = [...this.colorRoles.entries()].find(([, v]) => v === value);\n if (collision) {\n throw new EditorError(\n 'E_COLOR_ROLE_COLLISION',\n `color roles \"${collision[0]}\" and \"${roleName}\" both normalize to ${value}. ` +\n `useColorBinding matches elements by VALUE, so colliding roles would repaint ` +\n `each other's elements — give every role a distinct hex.`,\n { roles: [collision[0], roleName], value, docSlug: 'embed-mode' },\n );\n }\n this.colorRoles.set(roleName, value);\n }\n this.current = {\n name: artboard.name || 'Front',\n width: artboard.width,\n height: artboard.height,\n backgroundColor: resolveColor(artboard.backgroundColor),\n role: artboard.role,\n };\n this.activeName = this.current.name;\n }\n\n /**\n * Seed a builder from a previously saved canvas state — the editor shape\n * `<SnowconeCanvas onChange>` emits (and `toJSON()` produces).\n *\n * This is how a saved editable template re-enters the turnkey embed path:\n * load the template's state JSON, lift it back into a `DesignBuilder`, and\n * hand it to `<RealtimeEmbed design={…}>`. By default the active artboard\n * becomes the builder's artboard; elements tagged for other artboards are\n * skipped (untagged elements are assumed to belong to the active one).\n *\n * Pass `{ artboards: 'all' }` to load a multi-page template (ADR-0086)\n * whole: every artboard (with its pacing `role`) and every element are\n * kept, membership tags preserved, so `toJSON()` / `toServerRequest()`\n * emit all pages and `TemplateField.artboard` can scope fields per page.\n */\n static fromState(\n state: {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n },\n options: FromStateOptions = {},\n ): DesignBuilder {\n const artboard =\n state.artboards.find((a) => a.name === state.activeArtboard) ?? state.artboards[0];\n if (!artboard) {\n throw new Error('DesignBuilder.fromState(): state has no artboards');\n }\n const builder = new DesignBuilder({\n name: artboard.name,\n width: artboard.width,\n height: artboard.height,\n backgroundColor: artboard.backgroundColor,\n role: artboard.role,\n });\n if (options.artboards === 'all') {\n builder.allArtboards = state.artboards.map((a) => ({ ...a }));\n for (const el of state.elements) {\n // Untagged elements belong to the active artboard (same assumption as\n // the active-only mode) — stamp them so the multi-artboard state keeps\n // them there instead of collapsing onto the first artboard.\n builder.els.push(el.artboard ? { ...el } : { ...el, artboard: artboard.name });\n }\n return builder;\n }\n for (const el of state.elements) {\n if (el.artboard && el.artboard !== artboard.name) continue;\n builder.els.push({ ...el });\n }\n return builder;\n }\n\n private nextId(prefix: string): string {\n return `${prefix}-${++this.idCounter}`;\n }\n\n /** A wire-shape snapshot of an authoring artboard, applying the same\n * background-export rule everywhere (a design background is part of the\n * design — exports and server renders paint it). */\n private static snapshotArtboard(a: {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n role?: ArtboardRole;\n }): StateArtboard {\n return {\n name: a.name,\n width: a.width,\n height: a.height,\n ...(a.backgroundColor\n ? {\n backgroundColor: a.backgroundColor,\n exportBackground: a.backgroundColor !== 'transparent',\n }\n : {}),\n ...(a.role ? { role: a.role } : {}),\n };\n }\n\n /**\n * Open a NEW artboard (placement) and make it the authoring context: layer\n * calls after this target the new artboard — anchor math uses its\n * dimensions, and the elements carry its membership tag (the public\n * `artboard` field, PR #482). The FIRST artboard (from `design({ name })`)\n * remains the initially-active placement when the design mounts; switch at\n * runtime with `usePlacements()` / `<Editor.Placements/>` (ADR-0088).\n *\n * `label` must be the product's placement label (case-sensitive) — the same\n * artboard-name-is-placement-label rule as `design({ name })`.\n *\n * ```ts\n * const seed = design({ name: 'Front', width: 1480, height: 2328 })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .artboard('Back', { width: 1480, height: 2328 })\n * .text('EST. 2026', { anchor: 'center', name: 'backline' });\n * ```\n *\n * @throws EditorError `E_DUPLICATE_ARTBOARD` — the design already has an\n * artboard with this label.\n */\n artboard(\n label: string,\n dims: { width: number; height: number; backgroundColor?: string | ColorToken; role?: ArtboardRole },\n ): this {\n if (!(dims.width > 0) || !(dims.height > 0)) {\n throw new Error('design().artboard(): width and height must be positive numbers');\n }\n if (!this.allArtboards) {\n this.allArtboards = [DesignBuilder.snapshotArtboard(this.current)];\n }\n if (this.allArtboards.some((a) => a.name === label)) {\n throw new EditorError(\n 'E_DUPLICATE_ARTBOARD',\n `the design already has an artboard \"${label}\" — artboard names are placement ` +\n `labels and must be unique. Existing: ${JSON.stringify(this.allArtboards.map((a) => a.name))}.`,\n { artboardName: label, validNames: this.allArtboards.map((a) => a.name), docSlug: 'embed-mode' },\n );\n }\n // Elements authored so far belong to the artboard that was current when\n // they were added — stamp the untagged ones before switching context.\n for (const el of this.els) {\n if (!el.artboard) el.artboard = this.current.name;\n }\n this.current = {\n name: label,\n width: dims.width,\n height: dims.height,\n backgroundColor: resolveColor(dims.backgroundColor),\n role: dims.role,\n };\n this.allArtboards.push(DesignBuilder.snapshotArtboard(this.current));\n return this;\n }\n\n /** Full-bleed background image (fills the whole artboard). Cover-fit (no\n * distortion) when `aspectRatio` is supplied. Inserted at the bottom of the\n * stack regardless of call order. The default layer name `'Background'`\n * joins the IMAGE-layer token union (auto-injected names are deliberately\n * not one union with text — ADR-0085 §1). */\n background<const N extends string>(\n imageUrl: string,\n opts: BackgroundOptions & { name: N },\n ): DesignBuilder<TText, TImage | N, TColor>;\n background(imageUrl: string, opts?: BackgroundOptions): DesignBuilder<TText, TImage | 'Background', TColor>;\n background(imageUrl: string, opts: BackgroundOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n let td: ImageElementConfig['transformData'];\n if (opts.aspectRatio && opts.aspectRatio > 0) {\n const c = coverCrop(opts.aspectRatio, artW, artH);\n td = {\n type: 'image', width: c.width, height: c.height,\n cropX: c.cropX, cropY: c.cropY, cropWidth: c.cropWidth, cropHeight: c.cropHeight,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n } else {\n td = {\n type: 'image', width: artW, height: artH,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n }\n const el: ImageElementConfig = {\n id: this.nextId('bg'),\n transformType: 'image',\n x: artW / 2,\n y: artH / 2,\n rotation: 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: td,\n name: opts.name ?? 'Background',\n };\n // Background goes underneath everything else.\n this.els.unshift(el);\n return this;\n }\n\n /** An anchored image element (not full-bleed). */\n image<const N extends string>(\n imageUrl: string,\n opts: ImageOptions & { name: N },\n ): DesignBuilder<TText, TImage | N, TColor>;\n image(imageUrl: string, opts?: ImageOptions): DesignBuilder<TText, TImage, TColor>;\n image(imageUrl: string, opts: ImageOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n const w = opts.width ?? Math.round(artW / 3);\n const h = opts.height ?? (opts.aspectRatio ? Math.round(w / opts.aspectRatio) : w);\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const el: ImageElementConfig = {\n id: this.nextId('img'),\n transformType: 'image',\n x, y,\n rotation: opts.rotation ?? 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: {\n type: 'image', width: w, height: h,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** An anchored text layer. Horizontal centring is exact (textAlign); vertical\n * anchoring uses the font size as the line-height estimate. */\n text<const N extends string>(\n text: string,\n opts: TextOptions & { name: N },\n ): DesignBuilder<TText | N, TImage, TColor>;\n text(text: string, opts?: TextOptions): DesignBuilder<TText, TImage, TColor>;\n text(text: string, opts: TextOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n const fontSize = opts.fontSize ?? Math.round(this.current.height / 12);\n // Text width is metric-dependent (unknown headless); horizontal placement is\n // handled by textAlign, so anchor only drives the vertical centre + the\n // horizontal *reference* point. Use a zero-width box horizontally so the\n // centre sits exactly on the anchor's x line.\n const { x, y } = anchorToCenter(opts.anchor ?? 'top', 0, fontSize, artW, artH, opts.padding ?? Math.round(artH / 20));\n const el: CustomElementConfig = {\n id: this.nextId('text'),\n transformType: 'custom',\n x, y,\n rotation: opts.rotation ?? 0,\n text,\n fontSize,\n fontFamily: opts.fontFamily ?? 'Anton',\n color: resolveColor(opts.color) ?? '#ffffff',\n textAlign: opts.textAlign ?? 'center',\n bold: opts.bold ?? false,\n italic: opts.italic ?? false,\n transformData: { type: 'custom' },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /**\n * Mint a {@link LayerToken} for a named layer — the only currency\n * `Editor.TextField` accepts (ADR-0085 §1). Compile time: `name` is\n * constrained to this design's declared layer names, kind inferred from\n * which per-kind union it falls in. Runtime: validated EAGERLY against the\n * built elements (nearest the mistake), so CMS/JSON-loaded designs get the\n * same protection with `string` typing.\n *\n * @throws EditorError `E_LAYER_NOT_FOUND` — no layer with this name; lists\n * the valid names per kind.\n * @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that\n * is not bindable (shape/group/path).\n */\n layer<N extends TText | TImage>(name: N): LayerToken<InferKind<N, TText, TImage>, N> {\n const el = this.els.find((e) => (e as { name?: string }).name === name);\n if (!el) {\n const textNames = this.layerNamesOfKind('text');\n const imageNames = this.layerNamesOfKind('image');\n throw new EditorError(\n 'E_LAYER_NOT_FOUND',\n `no layer \"${name}\" in this design. Text layers: ${JSON.stringify(textNames)}; ` +\n `image layers: ${JSON.stringify(imageNames)}. Fix the name, or add the layer ` +\n `first — e.g. design(…).text('…', { name: '${name}' }).`,\n { layerName: name, validNames: [...textNames, ...imageNames], textNames, imageNames, docSlug: 'embed-mode' },\n );\n }\n const kind = elementLayerKind(el);\n if (!kind) {\n throw new EditorError(\n 'E_LAYER_WRONG_KIND',\n `layer \"${name}\" is a ${(el as { transformType?: string }).transformType} element — ` +\n `only text and image layers are bindable. Bindable layers: ` +\n `${JSON.stringify([...this.layerNamesOfKind('text'), ...this.layerNamesOfKind('image')])}.`,\n { layerName: name, actualType: (el as { transformType?: string }).transformType, docSlug: 'embed-mode' },\n );\n }\n return mintLayerToken(kind, name) as LayerToken<InferKind<N, TText, TImage>, N>;\n }\n\n /**\n * Mint a {@link ColorToken} for a declared color role. Compile time: `name`\n * is constrained to the roles declared in `design({ colors })`. Runtime:\n * validated against the declared roles (CMS/JSON-built designs degrade to\n * `string` typing and rely on this throw).\n *\n * Use the token wherever the design paints the role (`fill`, `color`) AND\n * as the `useColorBinding` argument — one source of truth, no hand-synced\n * hex pairs.\n *\n * @throws EditorError `E_COLOR_ROLE_NOT_FOUND` — no role with this name;\n * lists the declared roles.\n */\n color<N extends TColor>(name: N): ColorToken<N> {\n const value = this.colorRoles.get(name);\n if (value === undefined) {\n throw new EditorError(\n 'E_COLOR_ROLE_NOT_FOUND',\n `no color role \"${name}\" in this design. Declared roles: ` +\n `${JSON.stringify([...this.colorRoles.keys()])}. Declare roles up front — ` +\n `design({ …, colors: { ${name}: '#FF2D8A' } }).`,\n { roleName: name, validNames: [...this.colorRoles.keys()], docSlug: 'embed-mode' },\n );\n }\n return mintColorToken(name, value) as ColorToken<N>;\n }\n\n /** Names of bindable layers of one kind, in stack order. @internal */\n layerNamesOfKind(kind: LayerKind): string[] {\n return this.els\n .filter((e) => elementLayerKind(e) === kind)\n .map((e) => (e as { name?: string }).name)\n .filter((n): n is string => Boolean(n));\n }\n\n /** An anchored vector shape. */\n shape(kind: ShapeKind, opts: ShapeOptions = {}): this {\n const { width: artW, height: artH } = this.current;\n const size = opts.size ?? Math.round(artW / 6);\n const w = typeof size === 'number' ? size : size.width;\n const h = typeof size === 'number' ? size : size.height;\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const transformData: ShapeTransformData = {\n type: 'shape',\n shapeType: kind,\n width: w,\n height: h,\n fillColor: resolveColor(opts.fill) ?? '#000000',\n fillOpacity: opts.opacity ?? 1,\n ...(opts.points !== undefined ? { points: opts.points } : {}),\n ...(opts.sides !== undefined ? { sides: opts.sides } : {}),\n };\n const el: ShapeElementConfig = {\n id: this.nextId('shape'),\n transformType: 'shape',\n x, y,\n rotation: opts.rotation ?? 0,\n transformData,\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** The composed elements, in render order (background first). Editor shape. */\n get elements(): AnyElementConfig[] {\n return this.els.map((e) => ({ ...e }));\n }\n\n /** Editor-shape canvas state for `<SnowconeCanvas initialElements=… />` and\n * `serializeStateForServer`. `activeArtboard` is the artboard name. A\n * builder loaded with `fromState(…, { artboards: 'all' })` emits every\n * page (artboard `role`s and element membership tags included). */\n toJSON(): {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n } {\n if (this.allArtboards) {\n return {\n // Elements added via builder methods since the last context switch are\n // untagged — they belong to the CURRENT authoring artboard; stamp them\n // so the multi-artboard state groups them correctly.\n elements: this.els.map((e) =>\n e.artboard ? { ...e } : { ...e, artboard: this.current.name },\n ),\n artboards: this.allArtboards.map((a) => ({ ...a })),\n // The initially-active placement (first artboard / saved state's\n // active) — NOT the last `.artboard()` authoring context.\n activeArtboard: this.activeName,\n };\n }\n return {\n elements: this.elements,\n // A background set on a design is part of the design: flag it renderable\n // so exports and server renders paint it, matching the editor (the\n // export worker skips it without `exportBackground`) — see snapshotArtboard.\n artboards: [DesignBuilder.snapshotArtboard(this.current)],\n activeArtboard: this.current.name,\n };\n }\n\n /** Server-render payload for `RenderSession.renderState(placement, …)`.\n * Equivalent to `serializeStateForServer(design.toJSON())`. */\n toServerRequest(): ServerRenderRequest {\n return serializeStateForServer(this.toJSON());\n }\n}\n\n/**\n * Start a design on an artboard (one print placement).\n *\n * @example\n * const d = design({ name: 'Front', width: 1480, height: 2328 })\n * .background(aiImageUrl, { aspectRatio: 1480 / 2328, name: 'art' })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .shape('star', { anchor: 'bottom-left', fill: '#000' });\n *\n * session.renderState('Front', d.toServerRequest()); // live\n * // …or hand d.toJSON() to <SnowconeCanvas/> for interactive editing — the\n * // `name`s let you bind layers with useImageBinding('art') / useTextBinding('headline').\n */\nexport function design<const C extends Record<string, string>>(\n artboard: DesignArtboard & { colors: C },\n): DesignBuilder<never, never, Extract<keyof C, string>>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, never>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, string> {\n return new DesignBuilder<never, never, string>(artboard);\n}\n\n/**\n * Resolve a layer's kind against a live element/config list — the mount-time\n * revalidation `Editor.TextField` runs (the token is a validated claim; the\n * mounted Root's design state is the authority). @internal\n */\nexport function layerKindOfConfig(el: AnyElementConfig): LayerKind | null {\n return elementLayerKind(el);\n}\n"],"names":["BoundLayerGuardContext","createContext","noopSubscribe","zeroVersion","useIsLayerBound","guard","useContext","version","useSyncExternalStore","useCallback","name","logger","createLogger","useCommandHistory","elements","setElements","artboardManager","onArtboardsChange","historyManagerRef","useRef","HybridHistoryManager","boundLayerGuard","assertLayerMutable","element","operation","bound","EditorError","historyState","setHistoryState","useState","updateHistoryState","manager","activeArtboardId","executeElementUpdate","oldElement","newElement","command","UpdateElementCommand","prevElements","s","artboardId","executeAddElement","insertAfterElementId","insertBeforeElementId","targetArtboardId","AddElementCommand","addedElement","insertIndex","e","newElements","i","GroupElement","childIndex","child","updatedGroup","id","executeRemoveElement","RemoveElementCommand","executeCreateArtboard","artboard","CreateArtboardCommand","executeDeleteArtboard","DeleteArtboardCommand","executeUpdateArtboard","oldProperties","newProperties","UpdateArtboardCommand","executeCommandBatch","commands","wrappedCommands","cmd","undo","redo","undoActiveArtboard","redoActiveArtboard","clearHistory","clearArtboardHistory","activeArtboardIdRef","useEffect","currentActiveId","executeReorderElement","draggedId","targetId","position","currentOrder","ReorderElementCommand","newOrder","elementMap","hasTextProperties","useElementProperties","selectedElement","onElementUpdate","fontSize","setFontSize","fontColor","setFontColor","fontFamily","setFontFamily","textAlign","setTextAlign","newFontSize","newFontColor","newFontFamily","newTextAlign","prev","updateFontSize","delta","newSize","MIN_FONT_SIZE","MAX_FONT_SIZE","updatedElement","setFontSizeValue","updateFontColor","newColor","span","updateFontFamily","oldFontFamily","FontAnalyzer","updateTextAlign","alignment","PRESERVE_SELECTION_ATTR","shouldPreserveSelection","target","path","classes","role","dataSlot","preserveSelectionProps","isInkDark","css","luma","inkLuminance","isInkLight","trimmed","hex","r","g","b","relLuma","m","lin","c","cs","DEFAULT_TEXT_INK","DEFAULT_TEXT_INK_ON_DARK","defaultTextColorForBackground","backgroundColor","ViewportContext","MIN_ZOOM","MAX_ZOOM","ZOOM_STEP","ViewportProvider","children","canvasRef","viewPadding","zoom","setZoom","panOffset","setPanOffset","isPanning","setIsPanning","userZoom","setUserZoom","resetUserView","zoomIn","zoomOut","zoomToFit","container","containerWidth","containerHeight","fitZoom","resetView","value","useMemo","jsx","useViewportContext","context","SelectionContext","SelectionProvider","selectedId","setSelectedId","multiSelection","setMultiSelection","activeChildElement","setActiveChildElement","hoveredElementId","setHoveredElementId","hideHandles","setHideHandles","handleSelectionChange","handleActiveChildChange","useSelectionContext","HistoryContext","HistoryProvider","memoizedValue","useHistoryContext","ToolStateContext","ToolStateProvider","expandedPanelType","setExpandedPanelType","isToolbarMenuOpen","setIsToolbarMenuOpen","textSelectionVersion","setTextSelectionVersion","isCanvasReady","setCanvasReady","isRotating","setIsRotating","useToolStateContext","ElementsContext","ElementsProvider","useElementsContext","CommandContext","CommandProvider","useCommandContext","EditorContext","EditorHandlesContext","useEditorHandles","InternalEditorProvider","canvasEditorRef","handlesRef","handles","viewport","selection","toolState","artboards","setArtboards","artboardVersion","setArtboardVersion","elementStore","setElementStore","ElementStore","action","prevStore","prevArray","nextArray","getElementById","clipboard","setClipboard","refreshArtboards","v","historyManager","canUndo","canRedo","canUndoActiveArtboard","canRedoActiveArtboard","handleElementUpdate","handleChildElementUpdate","updatedChild","elementForProperties","elementUpdateHandler","addElement","spec","elementType","opts","newId","activeArtboard","centerX","centerY","currentZoom","_a","placeholderImage","boxSize","src","newImageElement","ImageElement","loadedElement","w","h","scale","transformType","transformDef","getTransformById","baseFontSize","transformWidth","shapeType","shapeW","shapeH","ShapeElement","ARCH_DEFAULTS","WAVE_DEFAULTS","handleAddElement","handleCopyElements","elementsToCopy","selectedElements","clonedElements","el","handlePasteElements","pastedIds","offsetX","offsetY","clonedElement","originalElement","baseX","baseY","elementConfig","Constructor","pastedElement","insertAfterId","clearExpandedPanel","imageElement","handleDocumentPointerDown","activeElement","handleLoadWorkspace","loadedArtboards","loadedElements","EditorProvider","initialArtboardConfig","ArtboardManager","config","DEFAULT_ARTBOARD_COLOR","useEditor","recorded","recordContainedEditorError","error","isDevOrTest","getContainedEditorErrors","resetContainedEditorErrors","expectNoEditorErrors","summary","isEditorError","CANVAS_STATE_SCHEMA_VERSION","q","n","quantizeGeometry","serializeStateForServer","state","srcArtboards","elementsByArtboard","tag","idx","ab","serializeElement","srcArtboard","_b","type","serializeImageElement","_artboard","rest","td","hasMasks","fullWidth","fullHeight","visibleWidth","visibleHeight","serialized","isNeutralAdjustments","DEFAULT_ANCHOR","LAYER_TOKEN_MARKER","isLayerToken","mintLayerToken","kind","COLOR_TOKEN_MARKER","isColorToken","mintColorToken","normalizeRoleHex","color","resolveColor","elementLayerKind","hPart","a","vPart","anchorToCenter","anchor","elW","elH","artW","artH","padding","x","y","coverCrop","ar","artAR","fullW","cropWidth","fullH","cropHeight","DesignBuilder","roleName","raw","collision","options","builder","prefix","label","dims","imageUrl","text","textNames","imageNames","size","transformData","design","layerKindOfConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"design-builder-CoCtjIwj.js","sources":["../src/contexts/BoundLayerGuardContext.ts","../src/hooks/useCommandHistory.ts","../src/hooks/useElementProperties.ts","../src/utils/selectionPreservation.ts","../src/utils/inkLuminance.ts","../src/contexts/ViewportContext.tsx","../src/contexts/SelectionContext.tsx","../src/contexts/HistoryContext.tsx","../src/contexts/ToolStateContext.tsx","../src/contexts/ElementsContext.tsx","../src/contexts/CommandContext.tsx","../src/contexts/EditorContext.tsx","../src/testing/editorErrors.ts","../src/rendering/serialize-for-server.ts","../src/design/design-builder.ts"],"sourcesContent":["/**\n * Bound-layer mutation guard (ADR-0085 §3, enforced in P1).\n *\n * While an `Editor.TextField` is registered for layer N, renaming or deleting\n * layer N must be impossible: the editor UI suppresses the affordances, and\n * the programmatic command path throws `E_BOUND_LAYER_MUTATION` naming the\n * bound part — so an end-user gesture can never detonate the prod error card\n * (the invalid state is unreachable from the UI), while agent/host code gets\n * a loud structured throw.\n *\n * `Editor.Root` provides its parts registry through this context ABOVE\n * `EditorProvider`, so the command layer (`useCommandHistory`) and the chrome\n * (LayersPanel, keyboard shortcuts) can consult it. Without a Root (legacy\n * standalone `SnowconeCanvas`) the context is null and nothing changes.\n *\n * This module is deliberately dependency-free: it sits in the always-mounted\n * Editor.Root graph (renderer-free, CI-gated) and is imported by both the\n * contexts layer and the editor parts.\n */\n\nimport { createContext, useCallback, useContext, useSyncExternalStore } from 'react';\n\nexport interface BoundLayerGuard {\n /** Subscribe to registry changes (TextFields mount/unmount). */\n subscribe(listener: () => void): () => void;\n /** Monotonic version for `useSyncExternalStore`. */\n getVersion(): number;\n /** The part bound to a layer name, or null when the layer is unbound. */\n getBoundLayer(name: string): { part: string } | null;\n}\n\nexport const BoundLayerGuardContext = createContext<BoundLayerGuard | null>(null);\n\nconst noopSubscribe = (): (() => void) => () => {};\nconst zeroVersion = (): number => 0;\n\n/**\n * Reactive bound-layer test for UI suppression: re-renders when TextFields\n * mount/unmount, so rename/delete affordances appear the moment a layer\n * stops being bound. Always returns false outside an `<Editor.Root>`.\n */\nexport function useIsLayerBound(): (name: string | undefined) => boolean {\n const guard = useContext(BoundLayerGuardContext);\n const version = useSyncExternalStore(\n guard ? guard.subscribe : noopSubscribe,\n guard ? guard.getVersion : zeroVersion,\n guard ? guard.getVersion : zeroVersion,\n );\n return useCallback(\n (name: string | undefined) => Boolean(guard && name && guard.getBoundLayer(name)),\n // `version` is the reactivity key: a registry change must mint a new\n // callback so memoized consumers re-evaluate.\n [guard, version],\n );\n}\n","/**\n * useCommandHistory - React hook for hybrid undo/redo functionality\n * Now supports artboard-level and element-level operations\n */\n\nimport { useState, useCallback, useContext, useRef, useEffect } from 'react';\nimport { createLogger } from '../utils/logger.js';\nimport { BoundLayerGuardContext } from '../contexts/BoundLayerGuardContext.js';\nimport { EditorError } from '../errors/editorError.js';\nimport {\n UpdateElementCommand,\n AddElementCommand,\n RemoveElementCommand,\n CreateArtboardCommand,\n DeleteArtboardCommand,\n UpdateArtboardCommand,\n ReorderElementCommand,\n} from '../core/CommandHistory.js';\nimport { HybridHistoryManager } from '../core/HybridHistoryManager.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\n\nconst logger = createLogger('useCommandHistory');\n\n/** Union of all element types */\ntype AnyElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\n/**\n * Hook to manage hybrid command history for undo/redo\n * Supports both global (artboard) and per-artboard (element) operations\n */\nexport function useCommandHistory(\n elements: AnyElement[],\n setElements: React.Dispatch<React.SetStateAction<AnyElement[]>>,\n artboardManager: ArtboardManager,\n onArtboardsChange?: () => void\n) {\n const historyManagerRef = useRef(new HybridHistoryManager(artboardManager));\n\n // Bound-layer mutation guard (ADR-0085 §3 / E_BOUND_LAYER_MUTATION): while\n // an <Editor.TextField> is mounted for layer N, renaming or deleting N via\n // the command path throws. Null outside an <Editor.Root> — the legacy\n // standalone editor is unchanged. The editor UI additionally suppresses the\n // affordances (LayersPanel, keyboard delete) so an end-user gesture can\n // never reach this throw; it exists for the programmatic path.\n const boundLayerGuard = useContext(BoundLayerGuardContext);\n const assertLayerMutable = useCallback(\n (element: AnyElement | undefined | null, operation: 'rename' | 'delete') => {\n const name = element?.name;\n if (!boundLayerGuard || !name) return;\n const bound = boundLayerGuard.getBoundLayer(name);\n if (bound) {\n throw new EditorError(\n 'E_BOUND_LAYER_MUTATION',\n `cannot ${operation} layer \"${name}\" — a mounted <${bound.part} layer={design.layer(\"${name}\")}> ` +\n `is bound to it. Unmount that field first, or keep the layer and set its text through the ` +\n `field / its binding instead.`,\n { layer: name, part: bound.part, operation, docSlug: 'embed-mode' },\n );\n }\n },\n [boundLayerGuard],\n );\n const [historyState, setHistoryState] = useState({\n canUndo: false,\n canRedo: false,\n // Per-artboard state for the active artboard\n canUndoActiveArtboard: false,\n canRedoActiveArtboard: false,\n });\n\n // Update history state\n const updateHistoryState = useCallback(() => {\n const manager = historyManagerRef.current;\n const activeArtboardId = artboardManager.getActiveArtboardId();\n setHistoryState({\n canUndo: manager.canUndo(),\n canRedo: manager.canRedo(),\n // Per-artboard state - only check active artboard's history\n canUndoActiveArtboard: activeArtboardId ? manager.canUndoArtboard(activeArtboardId) : false,\n canRedoActiveArtboard: activeArtboardId ? manager.canRedoArtboard(activeArtboardId) : false,\n });\n }, [artboardManager]);\n\n /**\n * Execute an element update with undo support (on active artboard)\n */\n const executeElementUpdate = useCallback(\n (\n oldElement: AnyElement | undefined | null,\n newElement: AnyElement\n ) => {\n // A name change through the update path is a layer RENAME.\n if (oldElement && oldElement.name !== newElement.name) {\n assertLayerMutable(oldElement, 'rename');\n }\n const command = new UpdateElementCommand(\n newElement.id,\n oldElement || null,\n newElement,\n (element) => {\n setElements((prevElements) => prevElements.map((s) => (s.id === element.id ? element as AnyElement : s)));\n }\n );\n\n // Execute on the artboard that contains this element\n const artboardId = artboardManager.getArtboardIdForElement(newElement.id);\n if (artboardId) {\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n } else {\n // Fallback: execute on active artboard\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId) {\n historyManagerRef.current.executeOnArtboard(activeArtboardId, command);\n }\n }\n\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState, assertLayerMutable]\n );\n\n /**\n * Execute an add element command\n */\n const executeAddElement = useCallback(\n (element: AnyElement, artboardId?: string, insertAfterElementId?: string, insertBeforeElementId?: string) => {\n const targetArtboardId = artboardId || artboardManager.getActiveArtboardId();\n if (!targetArtboardId) {\n logger.warn('No active artboard to add element to');\n return;\n }\n\n const command = new AddElementCommand(\n element,\n (addedElement) => {\n const newElement = addedElement as AnyElement;\n setElements((prevElements) => {\n // Insert before a specific element (e.g., promoting from group to appear below it visually)\n if (insertBeforeElementId) {\n const insertIndex = prevElements.findIndex((e) => e.id === insertBeforeElementId);\n if (insertIndex !== -1) {\n const newElements = [...prevElements];\n newElements.splice(insertIndex, 0, newElement);\n return newElements;\n }\n }\n // If insertAfterElementId is provided, insert after that element\n if (insertAfterElementId) {\n // First check if it's a top-level element\n const insertIndex = prevElements.findIndex((e) => e.id === insertAfterElementId);\n if (insertIndex !== -1) {\n // Insert after the found element (layers are displayed in reverse)\n const newElements = [...prevElements];\n newElements.splice(insertIndex + 1, 0, newElement);\n return newElements;\n }\n\n // If not found at top level, check if it's a child within a group\n // In this case, add the new element as a child of that group\n for (let i = 0; i < prevElements.length; i++) {\n const element = prevElements[i];\n if (element instanceof GroupElement && element.children) {\n const childIndex = element.children.findIndex((child) => child.id === insertAfterElementId);\n if (childIndex !== -1) {\n // Found the parent group - add new element as a child after the selected child\n const updatedGroup = element.clone();\n updatedGroup.children.splice(childIndex + 1, 0, newElement);\n\n const newElements = [...prevElements];\n newElements[i] = updatedGroup;\n return newElements;\n }\n }\n }\n }\n // Default: append to end\n return [...prevElements, newElement];\n });\n artboardManager.addElementToArtboard(addedElement.id, targetArtboardId);\n },\n (id: string) => {\n setElements((prevElements) => prevElements.filter((s) => s.id !== id));\n artboardManager.removeElementFromArtboard(id);\n }\n );\n\n historyManagerRef.current.executeOnArtboard(targetArtboardId, command);\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState]\n );\n\n /**\n * Execute a remove element command\n */\n const executeRemoveElement = useCallback(\n (element: AnyElement) => {\n assertLayerMutable(element, 'delete');\n const artboardId = artboardManager.getArtboardIdForElement(element.id);\n if (!artboardId) {\n logger.warn('Element not found in any artboard');\n return;\n }\n\n const command = new RemoveElementCommand(\n element,\n (addedElement) => {\n setElements((prevElements) => [...prevElements, addedElement as AnyElement]);\n artboardManager.addElementToArtboard(addedElement.id, artboardId);\n },\n (id: string) => {\n setElements((prevElements) => prevElements.filter((s) => s.id !== id));\n artboardManager.removeElementFromArtboard(id);\n }\n );\n\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n updateHistoryState();\n },\n [setElements, artboardManager, updateHistoryState, assertLayerMutable]\n );\n\n /**\n * Execute a create artboard command (global operation)\n */\n const executeCreateArtboard = useCallback(\n (artboard: ArtboardElement) => {\n const command = new CreateArtboardCommand(artboard, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute a delete artboard command (global operation)\n */\n const executeDeleteArtboard = useCallback(\n (artboard: ArtboardElement) => {\n const command = new DeleteArtboardCommand(artboard, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute an update artboard command (global operation)\n */\n const executeUpdateArtboard = useCallback(\n (artboardId: string, oldProperties: Partial<ArtboardElement>, newProperties: Partial<ArtboardElement>) => {\n const command = new UpdateArtboardCommand(artboardId, oldProperties, newProperties, artboardManager);\n historyManagerRef.current.executeGlobal(command);\n updateHistoryState();\n onArtboardsChange?.();\n },\n [artboardManager, updateHistoryState, onArtboardsChange]\n );\n\n /**\n * Execute a batch of commands as a single undo entry on the active artboard.\n * All commands are grouped into one CompoundCommand so that undo/redo\n * treats the entire batch as a single atomic operation.\n *\n * No-op if commands array is empty.\n */\n const executeCommandBatch = useCallback(\n (commands: Array<{ execute: () => void; undo: () => void }>) => {\n if (commands.length === 0) {\n return;\n }\n\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n logger.warn('No active artboard for batch execution');\n return;\n }\n\n // Wrap plain objects as Command instances\n const wrappedCommands = commands.map((cmd) => {\n const command = new (class extends Object {\n execute() { cmd.execute(); }\n undo() { cmd.undo(); }\n })();\n return command as import('../core/CommandHistory.js').Command;\n });\n\n historyManagerRef.current.executeBatchOnArtboard(activeArtboardId, wrappedCommands);\n updateHistoryState();\n },\n [artboardManager, updateHistoryState]\n );\n\n /**\n * Smart undo - handles both global and artboard operations\n */\n const undo = useCallback(() => {\n if (historyManagerRef.current.undo()) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [updateHistoryState, onArtboardsChange]);\n\n /**\n * Smart redo - handles both global and artboard operations\n */\n const redo = useCallback(() => {\n if (historyManagerRef.current.redo()) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [updateHistoryState, onArtboardsChange]);\n\n /**\n * Undo only on the active artboard (element operations only)\n */\n const undoActiveArtboard = useCallback(() => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId && historyManagerRef.current.undoArtboard(activeArtboardId)) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [artboardManager, updateHistoryState, onArtboardsChange]);\n\n /**\n * Redo only on the active artboard (element operations only)\n */\n const redoActiveArtboard = useCallback(() => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (activeArtboardId && historyManagerRef.current.redoArtboard(activeArtboardId)) {\n updateHistoryState();\n onArtboardsChange?.();\n }\n }, [artboardManager, updateHistoryState, onArtboardsChange]);\n\n /**\n * Clear all history\n */\n const clearHistory = useCallback(() => {\n historyManagerRef.current.clear();\n updateHistoryState();\n }, [updateHistoryState]);\n\n /**\n * Clear history for a specific artboard\n */\n const clearArtboardHistory = useCallback(\n (artboardId: string) => {\n historyManagerRef.current.clearArtboardHistory(artboardId);\n updateHistoryState();\n },\n [updateHistoryState]\n );\n\n // Track active artboard changes to update undo/redo state\n // This is needed because canUndoActiveArtboard/canRedoActiveArtboard depend on which artboard is active\n const activeArtboardIdRef = useRef<string | null>(null);\n useEffect(() => {\n const currentActiveId = artboardManager.getActiveArtboardId();\n if (currentActiveId !== activeArtboardIdRef.current) {\n activeArtboardIdRef.current = currentActiveId;\n updateHistoryState();\n }\n });\n\n /**\n * Execute a reorder elements command\n */\n const executeReorderElement = useCallback(\n (draggedId: string, targetId: string, position: 'before' | 'after') => {\n // Get current element order\n const currentOrder = elements.map((e) => e.id);\n\n // Find which artboard these elements belong to\n const artboardId = artboardManager.getArtboardIdForElement(draggedId);\n if (!artboardId) {\n logger.warn('Element not found in any artboard');\n return;\n }\n\n const command = new ReorderElementCommand(draggedId, targetId, position, currentOrder, (newOrder: string[]) => {\n // Reorder elements based on the new order\n setElements((prevElements) => {\n const elementMap = new Map(prevElements.map((e) => [e.id, e]));\n return newOrder.map((id) => elementMap.get(id)).filter(Boolean) as AnyElement[];\n });\n });\n\n historyManagerRef.current.executeOnArtboard(artboardId, command);\n updateHistoryState();\n },\n [elements, setElements, artboardManager, updateHistoryState]\n );\n\n // Keyboard shortcuts for undo/redo (removed - will be handled in App.tsx)\n // This allows App.tsx to handle all keyboard shortcuts in one place\n\n return {\n // Element operations\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n\n // Batch operations\n executeCommandBatch,\n\n // Artboard operations\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n\n // Undo/redo (smart - considers last operation and active artboard)\n undo,\n redo,\n // Undo/redo for active artboard only (element operations)\n undoActiveArtboard,\n redoActiveArtboard,\n clearHistory,\n clearArtboardHistory,\n\n // State (smart - considers last operation)\n canUndo: historyState.canUndo,\n canRedo: historyState.canRedo,\n // State for active artboard only\n canUndoActiveArtboard: historyState.canUndoActiveArtboard,\n canRedoActiveArtboard: historyState.canRedoActiveArtboard,\n historyManager: historyManagerRef.current,\n };\n}\n\nexport default useCommandHistory;\n","/**\n * useElementProperties - Custom hook for managing element property state\n * Reduces state management complexity in App.jsx\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { MIN_FONT_SIZE, MAX_FONT_SIZE } from '../constants.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { CircleTransform } from '../transforms/CircleTransform.js';\nimport { FontAnalyzer } from '../utils/FontAnalyzer.js';\nimport type { TextAlign } from '../types/index.js';\n\ntype EditorElement = TextElement | ImageElement | GroupElement | ShapeElement | PathElement;\n\n/** Type guard to check if element has text properties */\nfunction hasTextProperties(element: EditorElement): element is TextElement {\n return element && 'fontSize' in element && 'color' in element && 'fontFamily' in element;\n}\n\n/**\n * Hook to manage element properties and UI synchronization\n * @param selectedElement - Currently selected element\n * @param onElementUpdate - Callback to update element\n * @returns Property getters and setters\n */\nexport function useElementProperties(\n selectedElement: EditorElement | undefined,\n onElementUpdate: (element: EditorElement) => void\n) {\n const [fontSize, setFontSize] = useState<number>(32);\n const [fontColor, setFontColor] = useState<string>('#333333');\n const [fontFamily, setFontFamily] = useState<string>('Arial');\n const [textAlign, setTextAlign] = useState<TextAlign>('center');\n\n // Sync UI state with selected element\n useEffect(() => {\n if (selectedElement && hasTextProperties(selectedElement)) {\n // Handle circle mode's effective font size\n const newFontSize = selectedElement.transformType === 'circle' && 'getEffectiveFontSize' in selectedElement\n ? Math.round((selectedElement as CircleTransform).getEffectiveFontSize())\n : Math.round(selectedElement.fontSize);\n\n const newFontColor = selectedElement.color;\n const newFontFamily = selectedElement.fontFamily;\n const newTextAlign = selectedElement.textAlign;\n\n // Only update state if values actually changed to prevent unnecessary re-renders\n setFontSize(prev => prev !== newFontSize ? newFontSize : prev);\n setFontColor(prev => prev !== newFontColor ? newFontColor : prev);\n setFontFamily(prev => prev !== newFontFamily ? newFontFamily : prev);\n setTextAlign(prev => prev !== newTextAlign ? newTextAlign : prev);\n }\n }, [selectedElement]);\n\n // Update font size\n const updateFontSize = useCallback(\n (delta: number) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n // Allow any size increment, not restricted to FONT_SIZES array\n const newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, fontSize + delta));\n setFontSize(newSize);\n\n const updatedElement = selectedElement.clone();\n if (updatedElement.transformType === 'circle' && 'setEffectiveFontSize' in updatedElement) {\n (updatedElement as CircleTransform).setEffectiveFontSize(newSize);\n } else {\n updatedElement.setFontSize(newSize);\n }\n\n onElementUpdate(updatedElement);\n },\n [selectedElement, fontSize, onElementUpdate]\n );\n\n // Set font size directly\n const setFontSizeValue = useCallback(\n (newSize: number) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setFontSize(newSize);\n\n const updatedElement = selectedElement.clone();\n if (updatedElement.transformType === 'circle' && 'setEffectiveFontSize' in updatedElement) {\n (updatedElement as CircleTransform).setEffectiveFontSize(newSize);\n } else {\n updatedElement.setFontSize(newSize);\n }\n\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update font color\n const updateFontColor = useCallback(\n (newColor: string) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setFontColor(newColor);\n\n const updatedElement = selectedElement.clone();\n // Set element-level color and clear all span colors so they inherit\n // This ensures ALL text changes color, not just text matching the old color\n updatedElement.color = newColor;\n if (updatedElement.richText) {\n for (const span of updatedElement.richText.spans) {\n span.style.color = undefined;\n }\n }\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update font family\n const updateFontFamily = useCallback(\n (newFontFamily: string) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n // Clear old font from cache if switching fonts (prevents iOS Safari memory crashes)\n const oldFontFamily = selectedElement.fontFamily;\n if (oldFontFamily !== newFontFamily) {\n FontAnalyzer.clearCache(oldFontFamily);\n }\n\n setFontFamily(newFontFamily);\n\n const updatedElement = selectedElement.clone();\n updatedElement.fontFamily = newFontFamily;\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n // Update text alignment\n const updateTextAlign = useCallback(\n (alignment: TextAlign) => {\n if (!selectedElement || !hasTextProperties(selectedElement)) return;\n\n setTextAlign(alignment);\n\n const updatedElement = selectedElement.clone();\n updatedElement.textAlign = alignment;\n onElementUpdate(updatedElement);\n },\n [selectedElement, onElementUpdate]\n );\n\n return {\n // State\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n // State setters (for UI only)\n setFontSize,\n setFontColor,\n setFontFamily,\n setTextAlign,\n // Update functions (update element + UI)\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n };\n}\n\nexport default useElementProperties;\n","/**\n * Selection Preservation Utility\n *\n * Provides a declarative way to mark UI elements that should NOT\n * trigger canvas deselection when clicked.\n *\n * Usage:\n * 1. Add data-preserve-selection attribute to any UI container\n * 2. The document-level handler in EditorContext checks for this attribute\n *\n * @example\n * // Direct attribute\n * <div data-preserve-selection>\n * <MyToolbar />\n * </div>\n *\n * // Spread helper\n * <div {...preserveSelectionProps}>\n * <MySettings />\n * </div>\n */\n\n/** Data attribute name for marking selection-preserving elements */\nexport const PRESERVE_SELECTION_ATTR = 'data-preserve-selection';\n\n/**\n * Check if a click event should preserve the current selection.\n * Walks up the DOM tree from the click target checking for the\n * data-preserve-selection attribute.\n *\n * Performance: O(depth) where depth is typically 10-20 nodes.\n * This is negligible compared to other click handling operations.\n *\n * @param target - The event target element\n * @returns true if selection should be preserved, false otherwise\n */\nexport function shouldPreserveSelection(target: EventTarget | null): boolean {\n // Handle both HTMLElement and SVGElement (icons inside buttons)\n if (!(target instanceof Element)) {\n return false;\n }\n\n // Start from the target, walking up through SVG and HTML elements\n let element: Element | null = target;\n const path: string[] = [];\n\n while (element) {\n // Build path for logging\n const id = element.id ? `#${element.id}` : '';\n const classes = element.className ? `.${String(element.className).split(' ').slice(0, 2).join('.')}` : '';\n path.push(`${element.tagName.toLowerCase()}${id}${classes}`);\n\n // Check for our custom attribute\n if (element.hasAttribute(PRESERVE_SELECTION_ATTR)) {\n return true;\n }\n\n // Check for common dialog/popover/menu patterns (third-party components)\n const role = element.getAttribute('role');\n if (role === 'dialog' || role === 'alertdialog' || role === 'menu' || role === 'listbox') {\n return true;\n }\n\n // Check for Radix/HeroUI popover patterns\n const dataSlot = element.getAttribute('data-slot');\n if (dataSlot && (dataSlot.includes('popover') || dataSlot.includes('dialog'))) {\n return true;\n }\n\n // Check for Radix data attributes (portaled content)\n if (element.hasAttribute('data-radix-popper-content-wrapper')) {\n return true;\n }\n\n element = element.parentElement;\n }\n\n return false;\n}\n\n/**\n * React props helper - spread this on containers that should preserve selection\n * @example <div {...preserveSelectionProps}>...</div>\n */\nexport const preserveSelectionProps = {\n [PRESERVE_SELECTION_ATTR]: '',\n} as const;\n","/**\n * inkLuminance - Shared colour-luminance helpers.\n *\n * Quick \"is this ink dark / light\" checks used to decide whether to\n * flip an author-supplied colour for the current theme/background. We\n * only need to handle hex literals (`#rgb`, `#rrggbb`) and `rgb(...)` —\n * the formats Loom emits. CSS colour names / `hsl()` fall through to\n * `null` (\"neither\"), which lets callers skip any flip — the safe\n * choice (no surprising visual change for unfamiliar inputs).\n *\n * Originally lived in PieceGuideRenderer; extracted so element-creation\n * defaults (artboard-aware ink) can reuse the same luminance math.\n *\n * @internal — exposed for tests; not part of the package's public\n * API. Don't import these from outside the canvas package.\n *\n * @module\n */\n\n/** @internal */\nexport function isInkDark(css: string): boolean {\n const luma = inkLuminance(css);\n return luma !== null && luma < 0.5;\n}\n\n/** @internal */\nexport function isInkLight(css: string): boolean {\n const luma = inkLuminance(css);\n return luma !== null && luma >= 0.5;\n}\n\n/** @internal */\nexport function inkLuminance(css: string): number | null {\n const trimmed = css.trim();\n // #rgb / #rrggbb\n if (trimmed.startsWith('#')) {\n const hex = trimmed.slice(1);\n let r: number, g: number, b: number;\n if (hex.length === 3) {\n r = parseInt(hex[0] + hex[0], 16);\n g = parseInt(hex[1] + hex[1], 16);\n b = parseInt(hex[2] + hex[2], 16);\n } else if (hex.length === 6) {\n r = parseInt(hex.slice(0, 2), 16);\n g = parseInt(hex.slice(2, 4), 16);\n b = parseInt(hex.slice(4, 6), 16);\n } else return null;\n if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;\n return relLuma(r, g, b);\n }\n // rgb(...) / rgba(...)\n const m = trimmed.match(/^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/);\n if (m) return relLuma(Number(m[1]), Number(m[2]), Number(m[3]));\n return null;\n}\n\nfunction relLuma(r: number, g: number, b: number): number {\n const lin = (c: number) => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : Math.pow((cs + 0.055) / 1.055, 2.4);\n };\n return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);\n}\n\n// ── Element-creation default ink ─────────────────────────────────────────────\n\n/** Default text ink for light artboard backgrounds. */\nexport const DEFAULT_TEXT_INK = '#333333';\n/** Default text ink for dark artboard backgrounds. */\nexport const DEFAULT_TEXT_INK_ON_DARK = '#f4f4f5';\n\n/**\n * Pick the default text colour for a newly created element based on the\n * artboard background it lands on: dark backgrounds get light ink so new\n * text is visible; everything else (light, transparent, unknown formats)\n * keeps the classic dark ink.\n *\n * Used ONLY by the element-creation default path (handleAddElement) —\n * bindings, templates, and deserialization always honour explicit colours.\n */\nexport function defaultTextColorForBackground(\n backgroundColor: string | null | undefined\n): string {\n if (backgroundColor && isInkDark(backgroundColor)) {\n return DEFAULT_TEXT_INK_ON_DARK;\n }\n return DEFAULT_TEXT_INK;\n}\n","/**\n * ViewportContext - Manages zoom and pan state for the canvas viewport.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on viewport state (e.g., layers panel, effects panel).\n * Components that only need zoom/pan information should use `useViewport()`\n * instead of `useEditor()`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\n\n/** Pan offset for infinite canvas viewport */\nexport interface PanOffset {\n x: number;\n y: number;\n}\n\n/** Value exposed by ViewportContext. */\nexport interface ViewportContextValue {\n /** Current zoom level (1.0 = 100%) */\n zoom: number;\n /** Current pan offset in world coordinates */\n panOffset: PanOffset;\n /** Whether the user is currently panning */\n isPanning: boolean;\n /** Increment zoom by one step */\n zoomIn: () => void;\n /** Decrement zoom by one step */\n zoomOut: () => void;\n /** Zoom to fit the active artboard in the viewport */\n zoomToFit: () => void;\n /** Reset zoom to 100% and pan offset to origin */\n resetView: () => void;\n /** Set zoom to an exact value */\n setZoom: React.Dispatch<React.SetStateAction<number>>;\n /** Set pan offset to an exact value */\n setPanOffset: React.Dispatch<React.SetStateAction<PanOffset>>;\n /** Set whether the user is currently panning */\n setIsPanning: React.Dispatch<React.SetStateAction<boolean>>;\n\n /**\n * User-driven zoom multiplier applied on top of the auto-fit zoom\n * the embedded canvas computes from container size. 1.0 = exactly\n * fit-zoom (no extra zoom). >1 = zoomed in for fine-detail editing.\n * Composes with `zoom` (which is the *effective* zoom = fit × user)\n * so legacy consumers reading `zoom` see one value.\n */\n userZoom: number;\n setUserZoom: React.Dispatch<React.SetStateAction<number>>;\n /** Reset userZoom to 1.0 and panOffset to origin (returns to fit). */\n resetUserView: () => void;\n}\n\nconst ViewportContext = createContext<ViewportContextValue | null>(null);\n\n// Zoom limits\nconst MIN_ZOOM = 0.1;\nconst MAX_ZOOM = 4.0;\nconst ZOOM_STEP = 0.25;\n\n/** Props for {@link ViewportProvider}. */\nexport interface ViewportProviderProps {\n children: ReactNode;\n /** ArtboardManager instance, used by zoomToFit to read active artboard dimensions. */\n artboardManager: ArtboardManager;\n /** Ref to the canvas element, used by zoomToFit to measure the container. */\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n /**\n * Controls how much of the container the artboard fills when using zoomToFit.\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 */\n viewPadding?: number;\n}\n\n/**\n * Provides viewport state (zoom, pan) and control functions to all descendants.\n *\n * Must be composed inside EditorProvider or used standalone for viewport-only\n * scenarios. Most consumers should use the `useViewport()` hook.\n */\nexport const ViewportProvider: React.FC<ViewportProviderProps> = ({\n children,\n artboardManager,\n canvasRef,\n viewPadding = 0.9,\n}) => {\n const [zoom, setZoom] = useState<number>(1.0);\n const [panOffset, setPanOffset] = useState<PanOffset>({ x: 0, y: 0 });\n const [isPanning, setIsPanning] = useState<boolean>(false);\n const [userZoom, setUserZoom] = useState<number>(1.0);\n\n const resetUserView = useCallback(() => {\n setUserZoom(1.0);\n setPanOffset({ x: 0, y: 0 });\n }, []);\n\n const zoomIn = useCallback(() => {\n setZoom((prev) => Math.min(prev + ZOOM_STEP, MAX_ZOOM));\n }, []);\n\n const zoomOut = useCallback(() => {\n setZoom((prev) => Math.max(prev - ZOOM_STEP, MIN_ZOOM));\n }, []);\n\n const zoomToFit = useCallback(() => {\n const artboard = artboardManager.getActiveArtboard();\n if (!artboard || !canvasRef.current) return;\n\n // Walk up the DOM tree to find a container with actual dimensions\n let container = canvasRef.current.parentElement;\n let containerWidth = 0;\n let containerHeight = 0;\n\n while (container) {\n containerWidth = container.clientWidth;\n containerHeight = container.clientHeight;\n if (containerWidth > 0 && containerHeight > 0) {\n break;\n }\n container = container.parentElement;\n }\n\n if (!containerWidth || !containerHeight) return;\n\n const fitZoom = Math.min(\n (containerWidth * viewPadding) / artboard.width,\n (containerHeight * viewPadding) / artboard.height,\n 1.0 // Never zoom in beyond 100%\n );\n\n setZoom(fitZoom);\n setPanOffset({ x: 0, y: 0 });\n }, [artboardManager, canvasRef, viewPadding]);\n\n const resetView = useCallback(() => {\n setZoom(1.0);\n setPanOffset({ x: 0, y: 0 });\n }, []);\n\n const value = useMemo<ViewportContextValue>(\n () => ({\n zoom,\n panOffset,\n isPanning,\n zoomIn,\n zoomOut,\n zoomToFit,\n resetView,\n setZoom,\n setPanOffset,\n setIsPanning,\n userZoom,\n setUserZoom,\n resetUserView,\n }),\n [zoom, panOffset, isPanning, zoomIn, zoomOut, zoomToFit, resetView, userZoom, resetUserView]\n );\n\n return <ViewportContext.Provider value={value}>{children}</ViewportContext.Provider>;\n};\n\n/**\n * Access viewport state: zoom, pan, and control functions.\n *\n * Prefer this over `useEditor()` in components that only need viewport information\n * (e.g., zoom controls, minimap overlays). Changes to non-viewport state won't\n * trigger re-renders in components using this hook.\n *\n * @throws {Error} If called outside of a `ViewportProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function ZoomBar() {\n * const { zoom, zoomIn, zoomOut, zoomToFit } = useViewportContext();\n * return (\n * <div>\n * <button onClick={zoomOut}>-</button>\n * <span>{Math.round(zoom * 100)}%</span>\n * <button onClick={zoomIn}>+</button>\n * <button onClick={zoomToFit}>Fit</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useViewportContext(): ViewportContextValue {\n const context = useContext(ViewportContext);\n if (!context) {\n throw new Error('useViewportContext must be used within a ViewportProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * SelectionContext - Manages element selection state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on selection state (e.g., zoom controls, export panel).\n * Components that only need selection information should use `useSelectionContext()`\n * instead of `useEditor()`.\n *\n * Note: `selectedElement` (the derived element object) is NOT in this context\n * because it depends on `elements` state which lives in EditorContext. The\n * `useEditor()` facade computes it from `selectedId` + `elements`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useCallback, useMemo, type ReactNode } from 'react';\nimport type { EditorElement } from './EditorContext.js';\n\n/** Value exposed by SelectionContext. */\nexport interface SelectionContextValue {\n /** ID of the currently selected element, or null if nothing is selected */\n selectedId: string | null;\n /** IDs of elements in the current multi-selection */\n multiSelection: string[];\n /** The child element being directly edited inside a group */\n activeChildElement: EditorElement | null;\n /** ID of the element currently being hovered */\n hoveredElementId: string | null;\n /** Whether to hide selection handles (e.g., during text editing) */\n hideHandles: boolean;\n\n /** Set the selected element ID */\n setSelectedId: React.Dispatch<React.SetStateAction<string | null>>;\n /** Set multi-selection IDs */\n setMultiSelection: React.Dispatch<React.SetStateAction<string[]>>;\n /** Set the hovered element ID */\n setHoveredElementId: React.Dispatch<React.SetStateAction<string | null>>;\n /** Set whether to hide selection handles */\n setHideHandles: React.Dispatch<React.SetStateAction<boolean>>;\n\n /** Handler for element selection changes */\n handleSelectionChange: (id: string | null) => void;\n /** Handler for active child element changes (group editing) */\n handleActiveChildChange: (child: EditorElement | null) => void;\n}\n\nconst SelectionContext = createContext<SelectionContextValue | null>(null);\n\n/** Props for {@link SelectionProvider}. */\nexport interface SelectionProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides selection state and handlers to all descendants.\n *\n * Manages which element is selected, multi-selection, hover state,\n * and active child element for group editing.\n */\nexport const SelectionProvider: React.FC<SelectionProviderProps> = ({ children }) => {\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [multiSelection, setMultiSelection] = useState<string[]>([]);\n const [activeChildElement, setActiveChildElement] = useState<EditorElement | null>(null);\n const [hoveredElementId, setHoveredElementId] = useState<string | null>(null);\n const [hideHandles, setHideHandles] = useState<boolean>(false);\n\n const handleSelectionChange = useCallback((id: string | null) => {\n setSelectedId(id);\n }, []);\n\n const handleActiveChildChange = useCallback((child: EditorElement | null) => {\n setActiveChildElement(child);\n }, []);\n\n const value = useMemo<SelectionContextValue>(\n () => ({\n selectedId,\n multiSelection,\n activeChildElement,\n hoveredElementId,\n hideHandles,\n setSelectedId,\n setMultiSelection,\n setHoveredElementId,\n setHideHandles,\n handleSelectionChange,\n handleActiveChildChange,\n }),\n [\n selectedId,\n multiSelection,\n activeChildElement,\n hoveredElementId,\n hideHandles,\n handleSelectionChange,\n handleActiveChildChange,\n ]\n );\n\n return <SelectionContext.Provider value={value}>{children}</SelectionContext.Provider>;\n};\n\n/**\n * Access selection state: selected element ID, multi-selection, hover, and handlers.\n *\n * Prefer this over `useEditor()` in components that only need selection information\n * (e.g., layers panel highlighting, toolbar enable/disable logic).\n *\n * Note: This does NOT provide `selectedElement` (the resolved element object).\n * Use `useEditor()` or `useSelectedElement()` for that, as it requires\n * cross-referencing with element state.\n *\n * @throws {Error} If called outside of a `SelectionProvider` (or `EditorProvider`)\n */\nexport function useSelectionContext(): SelectionContextValue {\n const context = useContext(SelectionContext);\n if (!context) {\n throw new Error('useSelectionContext must be used within a SelectionProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * HistoryContext - Exposes undo/redo state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on history state (e.g., most rendering components).\n * Components that only need undo/redo controls should use `useHistoryContext()`\n * instead of `useEditor()`.\n *\n * Note: The heavy `useCommandHistory` hook and `execute*` functions remain in\n * EditorContext because they depend on `elements` and `artboardManager`. This\n * context only exposes the undo/redo actions and their availability flags.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\n\n/** Value exposed by HistoryContext. */\nexport interface HistoryContextValue {\n /** Undo the last operation (global scope) */\n undo: () => void;\n /** Redo the last undone operation (global scope) */\n redo: () => void;\n /** Whether there is an operation to undo (global scope) */\n canUndo: boolean;\n /** Whether there is an operation to redo (global scope) */\n canRedo: boolean;\n /** Undo the last operation on the active artboard */\n undoActiveArtboard: () => void;\n /** Redo the last undone operation on the active artboard */\n redoActiveArtboard: () => void;\n /** Whether there is an operation to undo on the active artboard */\n canUndoActiveArtboard: boolean;\n /** Whether there is an operation to redo on the active artboard */\n canRedoActiveArtboard: boolean;\n}\n\nconst HistoryContext = createContext<HistoryContextValue | null>(null);\n\n/** Props for {@link HistoryProvider}. */\nexport interface HistoryProviderProps {\n children: ReactNode;\n /**\n * The actual undo/redo functions and state from the command history system.\n * These are provided by EditorProvider which owns the command history.\n */\n value: HistoryContextValue;\n}\n\n/**\n * Provides undo/redo state and actions to all descendants.\n *\n * This is a \"pass-through\" provider: the actual history state is computed\n * by EditorProvider (via useCommandHistory) and passed in as props. This\n * allows components to subscribe to history changes without subscribing\n * to all of EditorContext.\n */\nexport const HistoryProvider: React.FC<HistoryProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but history state hasn't actually changed\n const memoizedValue = useMemo<HistoryContextValue>(\n () => ({\n undo: value.undo,\n redo: value.redo,\n canUndo: value.canUndo,\n canRedo: value.canRedo,\n undoActiveArtboard: value.undoActiveArtboard,\n redoActiveArtboard: value.redoActiveArtboard,\n canUndoActiveArtboard: value.canUndoActiveArtboard,\n canRedoActiveArtboard: value.canRedoActiveArtboard,\n }),\n [\n value.undo,\n value.redo,\n value.canUndo,\n value.canRedo,\n value.undoActiveArtboard,\n value.redoActiveArtboard,\n value.canUndoActiveArtboard,\n value.canRedoActiveArtboard,\n ]\n );\n\n return <HistoryContext.Provider value={memoizedValue}>{children}</HistoryContext.Provider>;\n};\n\n/**\n * Access undo/redo state and actions.\n *\n * Prefer this over `useEditor()` in components that only need undo/redo\n * controls (e.g., undo/redo buttons in a toolbar).\n *\n * @throws {Error} If called outside of a `HistoryProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function UndoRedoButtons() {\n * const { undo, redo, canUndo, canRedo } = useHistoryContext();\n * return (\n * <>\n * <button onClick={undo} disabled={!canUndo}>Undo</button>\n * <button onClick={redo} disabled={!canRedo}>Redo</button>\n * </>\n * );\n * }\n * ```\n */\nexport function useHistoryContext(): HistoryContextValue {\n const context = useContext(HistoryContext);\n if (!context) {\n throw new Error('useHistoryContext must be used within a HistoryProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * ToolStateContext - Manages UI tool state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on tool state (e.g., canvas rendering, layers panel).\n * Components that only need tool UI state should use `useToolStateContext()`\n * instead of `useEditor()`.\n *\n * Includes: expanded panel type, toolbar menu state, text selection version,\n * canvas ready state, and rotation state.\n *\n * Note: `clearExpandedPanel` is NOT in this context because it depends on\n * `selectedElement` and `executeElementUpdate` (cross-context dependency).\n * It remains in EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useMemo, type ReactNode } from 'react';\n\n/** Expanded panel types for contextual toolbars */\nexport type ExpandedPanelType = 'cornerRadius' | 'effects' | 'crop' | 'rotation' | null;\n\n/** Value exposed by ToolStateContext. */\nexport interface ToolStateContextValue {\n /** Which contextual panel is currently expanded, or null */\n expandedPanelType: ExpandedPanelType;\n /** Set the expanded panel type */\n setExpandedPanelType: React.Dispatch<React.SetStateAction<ExpandedPanelType>>;\n\n /** Whether any toolbar dropdown/menu/panel is open */\n isToolbarMenuOpen: boolean;\n /** Set toolbar menu open state */\n setIsToolbarMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n\n /** Version counter for text selection changes (forces re-render on selection) */\n textSelectionVersion: number;\n /** Increment text selection version */\n setTextSelectionVersion: React.Dispatch<React.SetStateAction<number>>;\n\n /** Whether the canvas has mounted and is ready for operations */\n isCanvasReady: boolean;\n /** Set the canvas ready state */\n setCanvasReady: (ready: boolean) => void;\n\n /** Whether the user is currently rotating an element */\n isRotating: boolean;\n /** Set the rotation state */\n setIsRotating: React.Dispatch<React.SetStateAction<boolean>>;\n}\n\nconst ToolStateContext = createContext<ToolStateContextValue | null>(null);\n\n/** Props for {@link ToolStateProvider}. */\nexport interface ToolStateProviderProps {\n children: ReactNode;\n}\n\n/**\n * Provides UI tool state to all descendants.\n *\n * Manages which panel is expanded, whether toolbar menus are open,\n * text selection tracking, canvas readiness, and rotation state.\n */\nexport const ToolStateProvider: React.FC<ToolStateProviderProps> = ({ children }) => {\n const [expandedPanelType, setExpandedPanelType] = useState<ExpandedPanelType>(null);\n const [isToolbarMenuOpen, setIsToolbarMenuOpen] = useState<boolean>(false);\n const [textSelectionVersion, setTextSelectionVersion] = useState<number>(0);\n const [isCanvasReady, setCanvasReady] = useState<boolean>(false);\n const [isRotating, setIsRotating] = useState<boolean>(false);\n\n const value = useMemo<ToolStateContextValue>(\n () => ({\n expandedPanelType,\n setExpandedPanelType,\n isToolbarMenuOpen,\n setIsToolbarMenuOpen,\n textSelectionVersion,\n setTextSelectionVersion,\n isCanvasReady,\n setCanvasReady,\n isRotating,\n setIsRotating,\n }),\n [expandedPanelType, isToolbarMenuOpen, textSelectionVersion, isCanvasReady, isRotating]\n );\n\n return <ToolStateContext.Provider value={value}>{children}</ToolStateContext.Provider>;\n};\n\n/**\n * Access UI tool state: expanded panels, toolbar menus, canvas readiness, etc.\n *\n * Prefer this over `useEditor()` in components that only need tool UI state\n * (e.g., contextual toolbars checking which panel is expanded).\n *\n * @throws {Error} If called outside of a `ToolStateProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function PanelToggle() {\n * const { expandedPanelType, setExpandedPanelType } = useToolStateContext();\n * return (\n * <button onClick={() => setExpandedPanelType(\n * expandedPanelType === 'effects' ? null : 'effects'\n * )}>\n * {expandedPanelType === 'effects' ? 'Close' : 'Effects'}\n * </button>\n * );\n * }\n * ```\n */\nexport function useToolStateContext(): ToolStateContextValue {\n const context = useContext(ToolStateContext);\n if (!context) {\n throw new Error('useToolStateContext must be used within a ToolStateProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * ElementsContext - Provides element and artboard state for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on element state (e.g., zoom controls, undo/redo buttons).\n * Components that only need element/artboard information should use\n * `useElementsContext()` instead of `useEditor()`.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via InternalEditorProvider) and passed in as props. This\n * allows components to subscribe to element changes without subscribing\n * to all of EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport type { ArtboardElement } from '../core/ArtboardElement.js';\nimport type { ArtboardManager } from '../core/ArtboardManager.js';\nimport type { ElementStore } from '../core/ElementStore.js';\nimport type { CanvasEditorHandle } from '../components/CanvasEditor.jsx';\nimport type { EditorElement } from './EditorContext.js';\nimport type { TextAlign } from '../types/index.js';\n\n/** Value exposed by ElementsContext. */\nexport interface ElementsContextValue {\n /** All elements in the editor */\n elements: EditorElement[];\n /** Normalized element store for O(1) lookups */\n elementStore: ElementStore;\n /** All artboards */\n artboards: ArtboardElement[];\n /** Artboard manager instance */\n artboardManager: ArtboardManager;\n /**\n * Version counter that increments on every artboard mutation.\n * Components can depend on this to re-render when artboard state changes.\n */\n artboardVersion: number;\n\n /** Set elements (same SetStateAction signature as before) */\n setElements: React.Dispatch<React.SetStateAction<EditorElement[]>>;\n /** Refresh artboards from the artboard manager */\n refreshArtboards: () => void;\n /** O(1) element lookup by ID. Returns undefined if not found. */\n getElementById: (id: string) => EditorElement | undefined;\n\n /** Add an element by type (text, image, shape, etc.) */\n handleAddElement: (elementType: string) => Promise<void>;\n /** Update an element */\n handleElementUpdate: (updatedElement: EditorElement) => void;\n /** Load a workspace (artboards + elements) */\n handleLoadWorkspace: (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => void;\n /** Copy selected elements to clipboard */\n handleCopyElements: () => void;\n /** Paste elements from clipboard */\n handlePasteElements: () => void;\n\n // Font property helpers\n /** Current font size of selected element */\n fontSize: number;\n /** Current font color of selected element */\n fontColor: string;\n /** Current font family of selected element */\n fontFamily: string;\n /** Current text alignment of selected element */\n textAlign: TextAlign;\n /** Update font size by delta */\n updateFontSize: (delta: number) => void;\n /** Set font size to exact value */\n setFontSizeValue: (size: number) => void;\n /** Update font color */\n updateFontColor: (color: string) => void;\n /** Update font family */\n updateFontFamily: (fontFamily: string) => void;\n /** Update text alignment */\n updateTextAlign: (textAlign: TextAlign) => void;\n\n // Refs\n /** Ref to the canvas HTML element */\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n /** Ref to the imperative canvas editor handle */\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n}\n\nconst ElementsContext = createContext<ElementsContextValue | null>(null);\n\n/** Props for {@link ElementsProvider}. */\nexport interface ElementsProviderProps {\n children: ReactNode;\n /**\n * The actual element/artboard state from the editor.\n * Provided by InternalEditorProvider which owns all state.\n */\n value: ElementsContextValue;\n}\n\n/**\n * Provides element and artboard state to all descendants.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via InternalEditorProvider) and passed in as props.\n * This allows components to subscribe to element changes without\n * subscribing to all of EditorContext.\n */\nexport const ElementsProvider: React.FC<ElementsProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but element state hasn't actually changed\n const memoizedValue = useMemo<ElementsContextValue>(\n () => ({\n elements: value.elements,\n elementStore: value.elementStore,\n artboards: value.artboards,\n artboardManager: value.artboardManager,\n artboardVersion: value.artboardVersion,\n setElements: value.setElements,\n refreshArtboards: value.refreshArtboards,\n getElementById: value.getElementById,\n handleAddElement: value.handleAddElement,\n handleElementUpdate: value.handleElementUpdate,\n handleLoadWorkspace: value.handleLoadWorkspace,\n handleCopyElements: value.handleCopyElements,\n handlePasteElements: value.handlePasteElements,\n fontSize: value.fontSize,\n fontColor: value.fontColor,\n fontFamily: value.fontFamily,\n textAlign: value.textAlign,\n updateFontSize: value.updateFontSize,\n setFontSizeValue: value.setFontSizeValue,\n updateFontColor: value.updateFontColor,\n updateFontFamily: value.updateFontFamily,\n updateTextAlign: value.updateTextAlign,\n canvasRef: value.canvasRef,\n canvasEditorRef: value.canvasEditorRef,\n }),\n [\n value.elements,\n value.elementStore,\n value.artboards,\n value.artboardManager,\n value.artboardVersion,\n value.setElements,\n value.refreshArtboards,\n value.getElementById,\n value.handleAddElement,\n value.handleElementUpdate,\n value.handleLoadWorkspace,\n value.handleCopyElements,\n value.handlePasteElements,\n value.fontSize,\n value.fontColor,\n value.fontFamily,\n value.textAlign,\n value.updateFontSize,\n value.setFontSizeValue,\n value.updateFontColor,\n value.updateFontFamily,\n value.updateTextAlign,\n value.canvasRef,\n value.canvasEditorRef,\n ]\n );\n\n return <ElementsContext.Provider value={memoizedValue}>{children}</ElementsContext.Provider>;\n};\n\n/**\n * Access element and artboard state: elements, artboards, element operations,\n * font properties, and canvas refs.\n *\n * Prefer this over `useEditor()` in components that only need element/artboard\n * information (e.g., layers panel, effects panel, property inspectors).\n * Changes to non-element state (viewport, selection, tool state) won't\n * trigger re-renders in components using this hook.\n *\n * @throws {Error} If called outside of an `ElementsProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function ElementCount() {\n * const { elements, artboards } = useElementsContext();\n * return (\n * <div>\n * <span>{elements.length} elements</span>\n * <span>{artboards.length} artboards</span>\n * </div>\n * );\n * }\n * ```\n */\nexport function useElementsContext(): ElementsContextValue {\n const context = useContext(ElementsContext);\n if (!context) {\n throw new Error('useElementsContext must be used within an ElementsProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * CommandContext - Provides command execution and undo/redo for the canvas editor.\n *\n * Extracted from EditorContext to prevent unnecessary re-renders in components\n * that don't depend on command execution (e.g., layers panel, zoom controls).\n * Components that only need command execution should use `useCommandContext()`\n * instead of `useEditor()`.\n *\n * This is a \"pass-through\" provider: the actual command state is computed by\n * EditorProvider (via InternalEditorProvider / useCommandHistory) and passed\n * in as props. This allows components to subscribe to command-related changes\n * without subscribing to all of EditorContext.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useMemo, type ReactNode } from 'react';\nimport type { useCommandHistory } from '../hooks/useCommandHistory.js';\n\n/** Value exposed by CommandContext. */\nexport interface CommandContextValue {\n /** Execute an element update (old -> new) through command history */\n executeElementUpdate: ReturnType<typeof useCommandHistory>['executeElementUpdate'];\n /** Execute adding a new element through command history */\n executeAddElement: ReturnType<typeof useCommandHistory>['executeAddElement'];\n /** Execute removing an element through command history */\n executeRemoveElement: ReturnType<typeof useCommandHistory>['executeRemoveElement'];\n /** Execute reordering an element through command history */\n executeReorderElement: ReturnType<typeof useCommandHistory>['executeReorderElement'];\n /** Execute creating an artboard through command history */\n executeCreateArtboard: ReturnType<typeof useCommandHistory>['executeCreateArtboard'];\n /** Execute deleting an artboard through command history */\n executeDeleteArtboard: ReturnType<typeof useCommandHistory>['executeDeleteArtboard'];\n /** Execute updating an artboard through command history */\n executeUpdateArtboard: ReturnType<typeof useCommandHistory>['executeUpdateArtboard'];\n /** Execute a batch of commands as a single undo entry */\n executeCommandBatch: ReturnType<typeof useCommandHistory>['executeCommandBatch'];\n\n /** Undo the last operation (global scope) */\n undo: () => void;\n /** Redo the last undone operation (global scope) */\n redo: () => void;\n /** Whether there is an operation to undo (global scope) */\n canUndo: boolean;\n /** Whether there is an operation to redo (global scope) */\n canRedo: boolean;\n\n /** Undo the last operation on the active artboard */\n undoActiveArtboard: () => void;\n /** Redo the last undone operation on the active artboard */\n redoActiveArtboard: () => void;\n /** Whether there is an operation to undo on the active artboard */\n canUndoActiveArtboard: boolean;\n /** Whether there is an operation to redo on the active artboard */\n canRedoActiveArtboard: boolean;\n\n /** The underlying history manager instance */\n historyManager: ReturnType<typeof useCommandHistory>['historyManager'];\n}\n\nconst CommandContext = createContext<CommandContextValue | null>(null);\n\n/** Props for {@link CommandProvider}. */\nexport interface CommandProviderProps {\n children: ReactNode;\n /**\n * The actual command execution functions and undo/redo state.\n * Provided by InternalEditorProvider which owns the command history.\n */\n value: CommandContextValue;\n}\n\n/**\n * Provides command execution and undo/redo state to all descendants.\n *\n * This is a \"pass-through\" provider: the actual state is computed by\n * EditorProvider (via useCommandHistory) and passed in as props. This\n * allows components to subscribe to command state without subscribing\n * to all of EditorContext.\n */\nexport const CommandProvider: React.FC<CommandProviderProps> = ({ children, value }) => {\n // Memoize to avoid unnecessary re-renders when the parent re-renders\n // but command state hasn't actually changed\n const memoizedValue = useMemo<CommandContextValue>(\n () => ({\n executeElementUpdate: value.executeElementUpdate,\n executeAddElement: value.executeAddElement,\n executeRemoveElement: value.executeRemoveElement,\n executeReorderElement: value.executeReorderElement,\n executeCreateArtboard: value.executeCreateArtboard,\n executeDeleteArtboard: value.executeDeleteArtboard,\n executeUpdateArtboard: value.executeUpdateArtboard,\n executeCommandBatch: value.executeCommandBatch,\n undo: value.undo,\n redo: value.redo,\n canUndo: value.canUndo,\n canRedo: value.canRedo,\n undoActiveArtboard: value.undoActiveArtboard,\n redoActiveArtboard: value.redoActiveArtboard,\n canUndoActiveArtboard: value.canUndoActiveArtboard,\n canRedoActiveArtboard: value.canRedoActiveArtboard,\n historyManager: value.historyManager,\n }),\n [\n value.executeElementUpdate,\n value.executeAddElement,\n value.executeRemoveElement,\n value.executeReorderElement,\n value.executeCreateArtboard,\n value.executeDeleteArtboard,\n value.executeUpdateArtboard,\n value.executeCommandBatch,\n value.undo,\n value.redo,\n value.canUndo,\n value.canRedo,\n value.undoActiveArtboard,\n value.redoActiveArtboard,\n value.canUndoActiveArtboard,\n value.canRedoActiveArtboard,\n value.historyManager,\n ]\n );\n\n return <CommandContext.Provider value={memoizedValue}>{children}</CommandContext.Provider>;\n};\n\n/**\n * Access command execution and undo/redo state.\n *\n * Prefer this over `useEditor()` in components that only need to execute\n * commands or check undo/redo availability (e.g., toolbar buttons that\n * add/remove elements, undo/redo controls).\n *\n * Note: For undo/redo-only access (without execute functions), prefer\n * `useHistoryContext()` which is even more focused.\n *\n * @throws {Error} If called outside of a `CommandProvider` (or `EditorProvider`)\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { executeRemoveElement } = useCommandContext();\n * const { selectedElement } = useEditor();\n *\n * return (\n * <button\n * onClick={() => selectedElement && executeRemoveElement(selectedElement)}\n * disabled={!selectedElement}\n * >\n * Delete\n * </button>\n * );\n * }\n * ```\n */\nexport function useCommandContext(): CommandContextValue {\n const context = useContext(CommandContext);\n if (!context) {\n throw new Error('useCommandContext must be used within a CommandProvider (or EditorProvider)');\n }\n return context;\n}\n","/**\n * EditorContext - Central state management for the canvas editor.\n *\n * Composes focused sub-contexts (ViewportContext, SelectionContext,\n * HistoryContext, ToolStateContext, ElementsContext, CommandContext) and\n * provides a backwards-compatible `useEditor()` facade that aggregates\n * all sub-context values.\n *\n * Every hook and UI component in `@snowcone-app/canvas` requires an `EditorProvider`\n * ancestor in the component tree. The `SnowconeCanvas` component creates one\n * automatically; use `EditorProvider` directly only when building custom layouts.\n *\n * ## Sub-Context Architecture\n *\n * State that was previously all in one context is now split into focused contexts\n * for better render performance:\n *\n * - **ViewportContext** - zoom, panOffset, isPanning, zoomIn/Out/ToFit/ResetView\n * - **SelectionContext** - selectedId, multiSelection, hoveredElementId, hideHandles\n * - **HistoryContext** - undo, redo, canUndo, canRedo (per-artboard variants)\n * - **ToolStateContext** - expandedPanelType, isToolbarMenuOpen, isCanvasReady, etc.\n * - **ElementsContext** - elements, artboards, element operations, font properties, refs\n * - **CommandContext** - command execution (execute*), undo/redo, historyManager\n * - **EditorContext (internal)** - full combined value for backwards compatibility\n *\n * Components can use focused hooks (useViewportContext, useSelectionContext,\n * useElementsContext, useCommandContext, etc.) to subscribe to only the state\n * they need. `useEditor()` still works as before and returns the full combined value.\n *\n * @module\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n useRef,\n useMemo,\n ReactNode,\n useEffect,\n} from 'react';\nimport { ArtboardElement } from '../core/ArtboardElement.js';\nimport { ArtboardManager } from '../core/ArtboardManager.js';\nimport { TextElement } from '../core/TextElement.js';\nimport { ImageElement } from '../core/ImageElement.js';\nimport { GroupElement } from '../core/GroupElement.js';\nimport { ShapeElement } from '../core/ShapeElement.js';\nimport { PathElement } from '../core/PathElement.js';\nimport { ElementStore } from '../core/ElementStore.js';\nimport { useCommandHistory } from '../hooks/useCommandHistory.js';\nimport { useElementProperties } from '../hooks/useElementProperties.js';\nimport { DEFAULT_ARTBOARD_COLOR } from '../constants.js';\nimport { getTransformById } from '../transforms/registry.js';\nimport { WAVE_DEFAULTS, ARCH_DEFAULTS } from '../transforms/defaults.js';\nimport { shouldPreserveSelection } from '../utils/selectionPreservation.js';\nimport { EditorError } from '../errors/editorError.js';\nimport { defaultTextColorForBackground } from '../utils/inkLuminance.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ViewportProvider, useViewportContext } from './ViewportContext.js';\nimport { SelectionProvider, useSelectionContext } from './SelectionContext.js';\nimport { HistoryProvider } from './HistoryContext.js';\nimport { ToolStateProvider, useToolStateContext } from './ToolStateContext.js';\nimport { ElementsProvider } from './ElementsContext.js';\nimport { CommandProvider } from './CommandContext.js';\nimport type { CanvasEditorHandle } from '../components/CanvasEditor.jsx';\nimport type { TransformType, TextAlign, ShapeType } from '../types/index.js';\n\nconst logger = createLogger('EditorContext');\n\n// Re-export sub-context types for backwards compatibility.\n// Consumers that import PanOffset or ExpandedPanelType from EditorContext\n// will continue to work without changes.\nexport type { PanOffset } from './ViewportContext.js';\nexport type { ExpandedPanelType } from './ToolStateContext.js';\n\nexport type EditorElement =\n | TextElement\n | ImageElement\n | GroupElement\n | ShapeElement\n | PathElement;\n\n/**\n * Spec for {@link EditorContextValue.addElement}. `name` makes the created\n * element addressable afterwards (bindings, `updateElement`, layer tokens).\n */\nexport interface AddElementSpec {\n /** What to create. Text seeds editable \"New Text\"; image without `src` gets a placeholder. */\n type: 'text' | 'shape' | 'image';\n /** Shape variant (default `'rectangle'`). Shape type only. */\n shapeType?: ShapeType;\n /** Image source URL — the element loads it and sizes to the real aspect ratio. Image type only. */\n src?: string;\n /** Layer name for the new element, so it can be targeted afterwards. */\n name?: string;\n}\n\n/**\n * Handle to an element created by {@link EditorContextValue.addElement}:\n * the generated `id` plus the layer `name` it answers to (empty string when\n * no `name` was given). The new element arrives selected.\n */\nexport interface ElementHandle {\n id: string;\n name: string;\n}\n\n/**\n * Full value exposed by EditorContext.\n *\n * Contains core state (elements, selection, artboards, zoom), refs to the\n * canvas DOM and imperative editor handle, setter functions, command history\n * operations (undo/redo), and element property helpers.\n */\nexport interface EditorContextValue {\n // Core state\n artboardManager: ArtboardManager;\n artboards: ArtboardElement[];\n elements: EditorElement[];\n /** Normalized element store for O(1) lookups. Use getElementById() for convenience. */\n elementStore: ElementStore;\n selectedId: string | null;\n multiSelection: string[];\n selectedElement: EditorElement | undefined;\n activeChildElement: EditorElement | null;\n hoveredElementId: string | null;\n hideHandles: boolean;\n zoom: number;\n panOffset: import('./ViewportContext.js').PanOffset;\n isPanning: boolean;\n isRotating: boolean;\n\n /** O(1) element lookup by ID. Returns undefined if not found. */\n getElementById: (id: string) => EditorElement | undefined;\n\n // Expanded panel state (shared with ContextualToolbars for two-level deselect)\n expandedPanelType: import('./ToolStateContext.js').ExpandedPanelType;\n setExpandedPanelType: React.Dispatch<\n React.SetStateAction<import('./ToolStateContext.js').ExpandedPanelType>\n >;\n clearExpandedPanel: () => void;\n\n // Toolbar menu open state - tracks if any dropdown/menu/panel is open in the toolbar\n // Used to disable keyboard shortcuts like Delete when interacting with toolbar\n isToolbarMenuOpen: boolean;\n setIsToolbarMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n\n // Refs\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n\n // Setters\n setElements: React.Dispatch<React.SetStateAction<EditorElement[]>>;\n setSelectedId: React.Dispatch<React.SetStateAction<string | null>>;\n setZoom: React.Dispatch<React.SetStateAction<number>>;\n setPanOffset: React.Dispatch<\n React.SetStateAction<import('./ViewportContext.js').PanOffset>\n >;\n setIsPanning: React.Dispatch<React.SetStateAction<boolean>>;\n setHoveredElementId: React.Dispatch<React.SetStateAction<string | null>>;\n setHideHandles: React.Dispatch<React.SetStateAction<boolean>>;\n setMultiSelection: React.Dispatch<React.SetStateAction<string[]>>;\n setTextSelectionVersion: React.Dispatch<React.SetStateAction<number>>;\n setIsRotating: React.Dispatch<React.SetStateAction<boolean>>;\n\n // Viewport control helpers\n zoomIn: () => void;\n zoomOut: () => void;\n zoomToFit: () => void;\n resetView: () => void;\n\n /**\n * Version counter that increments on every artboard mutation.\n * Components can depend on this to re-render when artboard state changes,\n * even though ArtboardManager is a mutable class instance.\n */\n artboardVersion: number;\n\n // Handlers\n refreshArtboards: () => void;\n handleSelectionChange: (id: string | null) => void;\n handleActiveChildChange: (child: EditorElement | null) => void;\n handleElementUpdate: (updatedElement: EditorElement) => void;\n /**\n * Create an element on the active artboard and get a handle back. The new\n * element arrives SELECTED. Throws `E_NO_ACTIVE_ARTBOARD` (an\n * {@link EditorError}, dev AND prod) when there is no artboard to add to.\n */\n addElement: (spec: AddElementSpec) => Promise<ElementHandle>;\n /**\n * @deprecated Use {@link addElement} — it returns an `ElementHandle`,\n * accepts a `name`, and throws `E_NO_ACTIVE_ARTBOARD` instead of the old\n * `window.alert`. This wrapper now propagates that throw too.\n */\n handleAddElement: (\n elementType: string,\n opts?: { shapeType?: ShapeType; src?: string }\n ) => Promise<void>;\n handleLoadWorkspace: (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => void;\n handleCopyElements: () => void;\n handlePasteElements: () => void;\n /**\n * Whether the editor's internal copy/paste clipboard holds anything.\n * `handlePasteElements` is a silent no-op when this is false — surfaces\n * offering a Paste affordance (canvas context menu) use this to disable\n * the entry instead of presenting an action that does nothing.\n */\n hasClipboardContent: boolean;\n\n // Command history\n historyManager: ReturnType<typeof useCommandHistory>['historyManager'];\n executeElementUpdate: ReturnType<typeof useCommandHistory>['executeElementUpdate'];\n executeAddElement: ReturnType<typeof useCommandHistory>['executeAddElement'];\n executeRemoveElement: ReturnType<typeof useCommandHistory>['executeRemoveElement'];\n executeReorderElement: ReturnType<typeof useCommandHistory>['executeReorderElement'];\n executeCreateArtboard: ReturnType<typeof useCommandHistory>['executeCreateArtboard'];\n executeDeleteArtboard: ReturnType<typeof useCommandHistory>['executeDeleteArtboard'];\n executeUpdateArtboard: ReturnType<typeof useCommandHistory>['executeUpdateArtboard'];\n executeCommandBatch: ReturnType<typeof useCommandHistory>['executeCommandBatch'];\n undo: () => void;\n redo: () => void;\n canUndo: boolean;\n canRedo: boolean;\n // Per-artboard undo/redo (for active artboard only)\n undoActiveArtboard: () => void;\n redoActiveArtboard: () => void;\n canUndoActiveArtboard: boolean;\n canRedoActiveArtboard: boolean;\n\n // Element properties\n fontSize: number;\n fontColor: string;\n fontFamily: string;\n textAlign: TextAlign;\n updateFontSize: (delta: number) => void;\n setFontSizeValue: (size: number) => void;\n updateFontColor: (color: string) => void;\n updateFontFamily: (fontFamily: string) => void;\n updateTextAlign: (textAlign: TextAlign) => void;\n\n // Text selection version\n textSelectionVersion: number;\n\n // Canvas ready state - gates exports until canvas is mounted\n isCanvasReady: boolean;\n setCanvasReady: (ready: boolean) => void;\n}\n\nconst EditorContext = createContext<EditorContextValue | null>(null);\n\n/**\n * Stable lazy access to the latest {@link EditorContextValue} — re-render\n * containment for chrome that needs editor state only inside EVENT HANDLERS\n * (save, export, add-element), never at render time (ADR-0085 / D3:\n * subscribing to the fat context re-renders on every editor change, including\n * each keystroke routed through the command path).\n *\n * The provided object's identity NEVER changes; `get()` returns the value as\n * of the provider's latest render. Do not render from it — anything read at\n * render time will go stale without a re-render. For reactive reads use the\n * narrow sub-context hooks instead.\n */\nexport interface EditorHandles {\n /** The latest full editor context value (lazy — call inside handlers). */\n get(): EditorContextValue;\n}\n\nconst EditorHandlesContext = createContext<EditorHandles | null>(null);\n\n/**\n * Read the stable editor handles. Same provider requirement as `useEditor()`.\n *\n * @throws {Error} If called outside of an `EditorProvider`\n */\nexport const useEditorHandles = (): EditorHandles => {\n const context = useContext(EditorHandlesContext);\n if (!context) {\n throw new Error('useEditorHandles must be used within EditorProvider');\n }\n return context;\n};\n\n/** Props for {@link EditorProvider}. */\nexport interface EditorProviderProps {\n children: ReactNode;\n /** Configuration for the default artboard created on mount. */\n initialArtboardConfig?: {\n name?: string;\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n backgroundColor?: string;\n };\n /**\n * Controls how much of the container the artboard fills when using fitToArtboard.\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 */\n viewPadding?: number;\n}\n\n/**\n * Internal provider that holds element/artboard state and composes with sub-contexts.\n *\n * This is the \"inner\" provider that has access to all sub-context values via hooks.\n * It reads from ViewportContext, SelectionContext, and ToolStateContext,\n * then combines everything into the full EditorContextValue for backwards compatibility.\n */\nconst InternalEditorProvider: React.FC<{\n children: ReactNode;\n artboardManager: ArtboardManager;\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n canvasEditorRef: React.RefObject<CanvasEditorHandle | null>;\n}> = ({ children, artboardManager, canvasRef, canvasEditorRef }) => {\n // Backing ref + literally-stable value for EditorHandlesContext (see\n // useEditorHandles). Declared first so the assignment after the big value\n // memo below is the last write before render.\n const handlesRef = useRef<EditorContextValue | null>(null);\n const handles = useMemo<EditorHandles>(() => ({ get: () => handlesRef.current! }), []);\n\n // --- Read sub-context values ---\n const viewport = useViewportContext();\n const selection = useSelectionContext();\n const toolState = useToolStateContext();\n\n // --- Artboard state ---\n const [artboards, setArtboards] = useState<ArtboardElement[]>(() =>\n artboardManager.getAllArtboards()\n );\n // Version counter for artboard mutations - incremented by refreshArtboards\n // so React can detect changes to the mutable ArtboardManager class instance.\n const [artboardVersion, setArtboardVersion] = useState(0);\n\n // ---------------------------------------------------------------------------\n // Normalized element storage (ElementStore)\n //\n // Internally we store elements in an ElementStore (Map-based, O(1) lookups).\n // Externally we still expose `elements: EditorElement[]` and\n // `setElements: Dispatch<SetStateAction<EditorElement[]>>` so every existing\n // consumer continues to work unchanged.\n // ---------------------------------------------------------------------------\n const [elementStore, setElementStore] = useState<ElementStore>(\n () => new ElementStore()\n );\n\n // Derived plain array - recomputed only when the store reference changes.\n const elements = useMemo(() => elementStore.toArray(), [elementStore]);\n\n // Wrapper that accepts the same SetStateAction<EditorElement[]> signature\n // that all consumers already use, but updates the ElementStore internally.\n const setElements: React.Dispatch<React.SetStateAction<EditorElement[]>> =\n useCallback((action: React.SetStateAction<EditorElement[]>) => {\n setElementStore((prevStore) => {\n const prevArray = prevStore.toArray();\n const nextArray =\n typeof action === 'function' ? action(prevArray) : action;\n // Avoid unnecessary re-creation when the array is the same reference\n if (nextArray === prevArray) return prevStore;\n return ElementStore.fromArray(nextArray);\n });\n }, []);\n\n // O(1) element lookup exposed to consumers\n const getElementById = useCallback(\n (id: string): EditorElement | undefined => elementStore.get(id),\n [elementStore]\n );\n\n const [clipboard, setClipboard] = useState<EditorElement[]>([]);\n\n // --- Derived state (cross-context: needs elements + selectedId) ---\n // O(1) lookup via ElementStore instead of O(n) elements.find()\n const selectedElement = selection.selectedId\n ? elementStore.get(selection.selectedId)\n : undefined;\n\n // Callback to refresh artboards state.\n // Also bumps artboardVersion so any component depending on artboard state re-renders,\n // even though ArtboardManager is a mutable class instance (fixes React integration).\n const refreshArtboards = useCallback(() => {\n setArtboards([...artboardManager.getAllArtboards()]);\n setArtboardVersion((v) => v + 1);\n }, [artboardManager]);\n\n // --- Command history ---\n const {\n historyManager,\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n } = useCommandHistory(\n elements,\n setElements,\n artboardManager,\n refreshArtboards\n );\n\n // --- Element update handlers ---\n // Update element handler - uses O(1) lookup via ElementStore\n const handleElementUpdate = useCallback(\n (updatedElement: EditorElement) => {\n const oldElement = elementStore.get(updatedElement.id);\n executeElementUpdate(oldElement, updatedElement);\n },\n [elementStore, executeElementUpdate]\n );\n\n // Update child element within a group\n const handleChildElementUpdate = useCallback(\n (updatedChild: EditorElement) => {\n if (\n !selectedElement ||\n !(selectedElement instanceof GroupElement)\n ) {\n return;\n }\n\n const updatedGroup = selectedElement.clone();\n const childIndex = updatedGroup.children.findIndex(\n (c) => c.id === updatedChild.id\n );\n\n if (childIndex >= 0) {\n // Cast is safe because GroupElement.children accepts the same element types\n updatedGroup.children[childIndex] =\n updatedChild as Parameters<GroupElement['addChild']>[0];\n handleElementUpdate(updatedGroup);\n }\n },\n [selectedElement, handleElementUpdate]\n );\n\n // --- Element properties ---\n const elementForProperties = selection.activeChildElement || selectedElement;\n const elementUpdateHandler = selection.activeChildElement\n ? handleChildElementUpdate\n : handleElementUpdate;\n const {\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n } = useElementProperties(elementForProperties, elementUpdateHandler);\n\n // --- Add element handler ---\n const addElement = useCallback(\n async (spec: AddElementSpec): Promise<ElementHandle> => {\n const elementType = spec.type;\n const opts = spec;\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n throw new EditorError(\n 'E_NO_ACTIVE_ARTBOARD',\n `addElement({ type: \"${elementType}\" }) needs an active artboard and none exists. ` +\n `<Editor.Root> seeds one automatically; with a raw EditorProvider create one first ` +\n `(executeCreateArtboard) or select one (artboardManager.setActiveArtboard).`,\n { type: elementType, docSlug: 'embed-mode' },\n );\n }\n\n const newId = `element-${Date.now()}`;\n const activeArtboard = artboardManager.getActiveArtboard();\n const centerX = activeArtboard\n ? activeArtboard.x + activeArtboard.width / 2\n : window.innerWidth / 2;\n const centerY = activeArtboard\n ? activeArtboard.y + activeArtboard.height / 2\n : window.innerHeight / 2;\n\n // Get current zoom level\n const currentZoom =\n canvasEditorRef.current?.getZoom() || viewport.zoom || 1.0;\n\n // Image elements: with `opts.src`, create the element pointed at that\n // URL and run the real load path (mirrors useImageBinding's swap logic)\n // so the element sizes to the loaded image instead of the placeholder\n // box. Without `src`, keep the 1x1 transparent placeholder — the caller\n // is responsible for replacing the image URL after creation.\n if (elementType === 'image') {\n const placeholderImage =\n 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';\n const boxSize = 200 / currentZoom;\n const src = opts?.src;\n const newImageElement = new ImageElement({\n id: newId,\n name: spec.name,\n x: centerX,\n y: centerY,\n // Defer loading `src` until after the element is in the store so a\n // synchronously-cached image can't fire onLoadCallback too early.\n imageUrl: src ? '' : placeholderImage,\n transformData: {\n type: 'image',\n width: boxSize,\n height: boxSize,\n cropX: 0,\n cropY: 0,\n cropWidth: 1,\n cropHeight: 1,\n flipHorizontal: false,\n flipVertical: false,\n borderRadius: 0,\n },\n });\n\n if (src) {\n // Same reset + load sequence as useImageBinding's setImageUrl\n // (\"contain\" fit): mark unloaded, set the URL, and once the image\n // loads, size to the real aspect ratio within the default box.\n newImageElement.imageLoaded = false;\n newImageElement.imageElement = null;\n newImageElement.isCropping = false;\n newImageElement.imageUrl = src;\n newImageElement.preserveDimensions = false;\n newImageElement.onLoadCallback = (loadedElement) => {\n // loadImage already set height = width / aspectRatio; scale the\n // whole frame down so it fits the default box, full image visible.\n const w = loadedElement.transformData.width;\n const h = loadedElement.transformData.height;\n const scale = Math.min(boxSize / w, boxSize / h, 1);\n if (scale < 1) {\n loadedElement.transformData.width = w * scale;\n loadedElement.transformData.height = h * scale;\n }\n loadedElement.transformData.cropX = 0;\n loadedElement.transformData.cropY = 0;\n loadedElement.transformData.cropWidth = 1;\n loadedElement.transformData.cropHeight = 1;\n // Single atomic update: command history + element store see only\n // the final loaded state.\n executeElementUpdate(newImageElement, loadedElement);\n };\n }\n\n executeAddElement(newImageElement, activeArtboardId);\n selection.setSelectedId(newImageElement.id);\n\n if (src) {\n newImageElement.loadImage(src);\n }\n return { id: newImageElement.id, name: spec.name ?? '' };\n }\n\n let newElement: EditorElement;\n\n // Map element type to transform type\n const typeMap: Record<string, TransformType> = {\n text: 'custom',\n circle: 'circle',\n wave: 'wave',\n arch: 'arch',\n shape: 'shape',\n };\n\n const transformType: TransformType = typeMap[elementType] || 'custom';\n const transformDef = getTransformById(transformType);\n\n if (!transformDef || !transformDef.Component) {\n // Unreachable for the public AddElementSpec union (typeMap falls back\n // to 'custom', which is always registered) — loud anyway, never silent.\n throw new Error(`[EditorContext] no transform registered for element type \"${elementType}\"`);\n }\n\n // Calculate appropriate sizes based on artboard\n const artboard = artboardManager.getActiveArtboard();\n const baseFontSize = artboard\n ? Math.min(artboard.width, artboard.height) * 0.1\n : 80;\n const transformWidth = artboard ? artboard.width * 0.5 : 300;\n\n if (elementType === 'shape') {\n const shapeType: ShapeType = opts?.shapeType ?? 'rectangle';\n // Sensible default dims per shape type — ellipses and lines\n // read better at non-square aspect ratios.\n let shapeW = 200 / currentZoom;\n let shapeH = 200 / currentZoom;\n if (shapeType === 'ellipse') {\n shapeW = 150 / currentZoom;\n shapeH = 250 / currentZoom;\n } else if (shapeType === 'line') {\n shapeW = 300 / currentZoom;\n shapeH = 8 / currentZoom;\n }\n newElement = new ShapeElement({\n id: newId,\n name: spec.name,\n x: centerX,\n y: centerY,\n transformData: {\n type: 'shape',\n shapeType,\n width: shapeW,\n height: shapeH,\n fillColor: '#3b82f6',\n fillOpacity: 1,\n },\n });\n } else {\n // Text-based elements. Default ink is artboard-aware: dark\n // backgrounds get light ink so new text isn't invisible. Applies to\n // the creation default only — bindings, templates, and\n // deserialization always honour explicit colours.\n newElement = new transformDef.Component({\n id: newId,\n name: spec.name,\n text: 'New Text',\n x: centerX,\n y: centerY,\n fontSize: baseFontSize,\n fontFamily: 'Poppins',\n color: defaultTextColorForBackground(activeArtboard?.backgroundColor),\n transformData:\n transformType === 'arch'\n ? ({\n type: 'arch',\n width: transformWidth,\n archHeight: ARCH_DEFAULTS.archHeight,\n } as const)\n : transformType === 'wave'\n ? ({\n type: 'wave',\n width: transformWidth,\n amplitude: WAVE_DEFAULTS.amplitude,\n frequency: WAVE_DEFAULTS.frequency,\n } as const)\n : transformType === 'circle'\n ? ({\n type: 'circle',\n radius: transformWidth / 2,\n } as const)\n : undefined,\n } as Record<string, unknown>);\n }\n\n artboardManager.addElementToArtboard(newId, activeArtboardId);\n executeAddElement(\n newElement,\n activeArtboardId,\n selection.selectedId || undefined\n );\n selection.setSelectedId(newId);\n return { id: newId, name: spec.name ?? '' };\n },\n [artboardManager, executeAddElement, executeElementUpdate, selection, viewport.zoom, canvasEditorRef]\n );\n\n /**\n * @deprecated Thin wrapper over {@link addElement} kept for the legacy\n * chrome (string element types incl. 'circle'/'wave'/'arch'). No more\n * `window.alert`: the `E_NO_ACTIVE_ARTBOARD` throw propagates.\n */\n const handleAddElement = useCallback(\n async (elementType: string, opts?: { shapeType?: ShapeType; src?: string }): Promise<void> => {\n await addElement({ ...opts, type: elementType as AddElementSpec['type'] });\n },\n [addElement]\n );\n\n // --- Copy/Paste ---\n // Copy selected element(s) to clipboard\n const handleCopyElements = useCallback(() => {\n const elementsToCopy: EditorElement[] = [];\n\n // Handle multi-selection\n if (selection.multiSelection.length > 0) {\n const selectedElements = elements.filter((s) =>\n selection.multiSelection.includes(s.id)\n );\n elementsToCopy.push(...selectedElements);\n } else if (selectedElement) {\n // Handle single selection\n elementsToCopy.push(selectedElement);\n }\n\n if (elementsToCopy.length > 0) {\n // Clone elements for clipboard (they keep their IDs for reference)\n const clonedElements = elementsToCopy.map((el) => el.clone());\n setClipboard(clonedElements);\n }\n }, [selectedElement, selection.multiSelection, elements]);\n\n // Paste elements from clipboard with diagonal offset\n const handlePasteElements = useCallback(() => {\n if (clipboard.length === 0) return;\n\n const pastedIds: string[] = [];\n const offsetX = 30; // Diagonal offset\n const offsetY = 30;\n\n clipboard.forEach((clonedElement) => {\n // O(1) lookup for the original element to get its current position and artboard\n const originalElement = elementStore.get(clonedElement.id);\n\n // Use original element's position if it still exists, otherwise use cloned position\n const baseX = originalElement ? originalElement.x : clonedElement.x;\n const baseY = originalElement ? originalElement.y : clonedElement.y;\n\n // Generate a new ID for the pasted element\n // Clone the element config and remove ID to get a fresh instance\n const elementConfig = clonedElement.toJSON();\n delete (elementConfig as Partial<typeof elementConfig>).id;\n const Constructor = clonedElement.constructor as new (config: Record<string, unknown>) => EditorElement;\n const pastedElement = new Constructor(elementConfig as unknown as Record<string, unknown>);\n\n // Offset position diagonally from the original\n pastedElement.x = baseX + offsetX;\n pastedElement.y = baseY + offsetY;\n\n // Get artboard for the original element (or use active artboard)\n let artboardId: string | null = null;\n\n if (originalElement) {\n artboardId = artboardManager.getArtboardIdForElement(\n originalElement.id\n );\n }\n\n if (!artboardId) {\n artboardId = artboardManager.getActiveArtboardId();\n }\n\n if (!artboardId) {\n logger.warn('[handlePasteElements] No artboard found for pasting');\n return;\n }\n\n // Add pasted element after the original (or at end if original not found)\n const insertAfterId = originalElement ? originalElement.id : undefined;\n executeAddElement(pastedElement, artboardId, insertAfterId);\n pastedIds.push(pastedElement.id);\n });\n\n // Select the pasted element(s)\n if (pastedIds.length === 1) {\n selection.handleSelectionChange(pastedIds[0]);\n } else if (pastedIds.length > 1) {\n selection.setMultiSelection(pastedIds);\n }\n }, [\n clipboard,\n elementStore,\n artboardManager,\n executeAddElement,\n selection,\n ]);\n\n // --- Cross-context: clearExpandedPanel ---\n // Clear expanded panel and exit crop mode if needed\n // This is called by two-level deselect and can be used by ContextualToolbars\n const clearExpandedPanel = useCallback(() => {\n // If closing the crop panel, also exit crop mode on the element\n if (\n toolState.expandedPanelType === 'crop' &&\n selectedElement?.transformType === 'image'\n ) {\n const imageElement = selectedElement as ImageElement;\n if (imageElement.isCropping) {\n const updatedElement = imageElement.clone();\n updatedElement.exitCropMode();\n executeElementUpdate(imageElement, updatedElement);\n }\n }\n toolState.setExpandedPanelType(null);\n }, [\n toolState.expandedPanelType,\n toolState.setExpandedPanelType,\n selectedElement,\n executeElementUpdate,\n ]);\n\n // --- Document-level click handler for deselection ---\n // Implements two-level deselect: first closes expanded panel, then deselects element\n useEffect(() => {\n const handleDocumentPointerDown = (e: PointerEvent) => {\n const target = e.target as HTMLElement;\n\n // Skip canvas clicks - canvas has its own selection logic\n if (target.tagName === 'CANVAS') {\n return;\n }\n\n // Skip if clicking on preserved selection area (toolbars, drawers, etc.)\n const shouldPreserve = shouldPreserveSelection(target);\n if (shouldPreserve) {\n return;\n }\n\n\n // If a data-preserve-selection element is focused (e.g., text edit textarea),\n // blur it to exit edit mode, but don't deselect the element (two-level backout)\n const activeElement = document.activeElement as HTMLElement | null;\n if (activeElement?.hasAttribute('data-preserve-selection')) {\n activeElement.blur(); // Exit edit mode\n return; // Don't deselect - element stays selected\n }\n\n // Two-level deselect: if a panel is expanded, close it first (keep element selected)\n if (toolState.expandedPanelType !== null) {\n clearExpandedPanel();\n return;\n }\n\n // Second level: actually deselect elements.\n // Guarded: an unconditional setMultiSelection([]) allocates a new array\n // every pointerdown, forcing a re-render even when nothing is selected.\n // That re-render rebuilds Iconify SVG internals between mouseup and\n // click dispatch, so the browser suppresses the click — icon-only\n // buttons outside data-preserve-selection zones ate their first click.\n if (selection.selectedId !== null) {\n selection.setSelectedId(null);\n }\n if (selection.multiSelection.length > 0) {\n selection.setMultiSelection([]);\n }\n };\n\n document.addEventListener('pointerdown', handleDocumentPointerDown);\n return () =>\n document.removeEventListener('pointerdown', handleDocumentPointerDown);\n }, [toolState.expandedPanelType, clearExpandedPanel, selection]);\n\n // --- Load workspace handler ---\n const handleLoadWorkspace = useCallback(\n (\n loadedArtboards: ArtboardElement[],\n loadedElements: EditorElement[],\n activeArtboardId: string | null\n ) => {\n artboardManager.clear();\n\n loadedArtboards.forEach((artboard) => {\n artboardManager.createArtboard(artboard.toJSON());\n });\n\n if (activeArtboardId) {\n artboardManager.setActiveArtboard(activeArtboardId);\n }\n\n setArtboards([...artboardManager.getAllArtboards()]);\n setArtboardVersion((v) => v + 1);\n setElements(loadedElements);\n selection.setSelectedId(null);\n },\n [artboardManager, selection, setElements]\n );\n\n // --- Compose the full EditorContextValue ---\n // Memoized so `useEditor()` returns a STABLE object identity across renders\n // that don't change editor state. Without this, every render minted a new\n // value object; any consumer that put `editor` (or a member read off the\n // unstable object) into an effect/memo dep array — then set state — entered\n // an infinite \"Maximum update depth exceeded\" loop on mount. The deps below\n // list every value/state slice this object exposes; the callbacks are\n // already useCallback-stable. Pinned by useTextBinding.loop.repro.test.tsx.\n const value: EditorContextValue = useMemo(() => ({\n // From ViewportContext\n zoom: viewport.zoom,\n panOffset: viewport.panOffset,\n isPanning: viewport.isPanning,\n zoomIn: viewport.zoomIn,\n zoomOut: viewport.zoomOut,\n zoomToFit: viewport.zoomToFit,\n resetView: viewport.resetView,\n setZoom: viewport.setZoom,\n setPanOffset: viewport.setPanOffset,\n setIsPanning: viewport.setIsPanning,\n\n // From SelectionContext\n selectedId: selection.selectedId,\n multiSelection: selection.multiSelection,\n activeChildElement: selection.activeChildElement,\n hoveredElementId: selection.hoveredElementId,\n hideHandles: selection.hideHandles,\n setSelectedId: selection.setSelectedId,\n setMultiSelection: selection.setMultiSelection,\n setHoveredElementId: selection.setHoveredElementId,\n setHideHandles: selection.setHideHandles,\n handleSelectionChange: selection.handleSelectionChange,\n handleActiveChildChange: selection.handleActiveChildChange,\n\n // From ToolStateContext\n expandedPanelType: toolState.expandedPanelType,\n setExpandedPanelType: toolState.setExpandedPanelType,\n isToolbarMenuOpen: toolState.isToolbarMenuOpen,\n setIsToolbarMenuOpen: toolState.setIsToolbarMenuOpen,\n textSelectionVersion: toolState.textSelectionVersion,\n setTextSelectionVersion: toolState.setTextSelectionVersion,\n isCanvasReady: toolState.isCanvasReady,\n setCanvasReady: toolState.setCanvasReady,\n isRotating: toolState.isRotating,\n setIsRotating: toolState.setIsRotating,\n\n // Cross-context handler\n clearExpandedPanel,\n\n // Core state (element/artboard domain)\n artboardManager,\n artboards,\n elements,\n elementStore,\n getElementById,\n\n // Derived state (cross-context)\n selectedElement,\n\n // Refs\n canvasRef,\n canvasEditorRef,\n\n // Element setters\n setElements,\n\n // Artboard version counter\n artboardVersion,\n\n // Handlers\n refreshArtboards,\n handleElementUpdate,\n addElement,\n handleAddElement,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\n hasClipboardContent: clipboard.length > 0,\n\n // Command history (undo/redo actions + execute functions)\n historyManager,\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n\n // Element properties\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n }), [\n // Viewport\n viewport.zoom, viewport.panOffset, viewport.isPanning, viewport.zoomIn,\n viewport.zoomOut, viewport.zoomToFit, viewport.resetView, viewport.setZoom,\n viewport.setPanOffset, viewport.setIsPanning,\n // Selection\n selection.selectedId, selection.multiSelection, selection.activeChildElement,\n selection.hoveredElementId, selection.hideHandles, selection.setSelectedId,\n selection.setMultiSelection, selection.setHoveredElementId,\n selection.setHideHandles, selection.handleSelectionChange,\n selection.handleActiveChildChange,\n // Tool state\n toolState.expandedPanelType, toolState.setExpandedPanelType,\n toolState.isToolbarMenuOpen, toolState.setIsToolbarMenuOpen,\n toolState.textSelectionVersion, toolState.setTextSelectionVersion,\n toolState.isCanvasReady, toolState.setCanvasReady,\n toolState.isRotating, toolState.setIsRotating,\n clearExpandedPanel,\n // Element/artboard domain\n artboardManager, artboards, elements, elementStore, getElementById,\n selectedElement, canvasRef, canvasEditorRef, setElements, artboardVersion,\n refreshArtboards, handleElementUpdate, addElement, handleAddElement,\n handleLoadWorkspace, handleCopyElements, handlePasteElements, clipboard,\n // Command history\n historyManager, executeElementUpdate, executeAddElement, executeRemoveElement,\n executeReorderElement, executeCommandBatch, executeCreateArtboard,\n executeDeleteArtboard, executeUpdateArtboard, undo, redo, canUndo, canRedo,\n undoActiveArtboard, redoActiveArtboard, canUndoActiveArtboard,\n canRedoActiveArtboard,\n // Element properties\n fontSize, fontColor, fontFamily, textAlign, updateFontSize, setFontSizeValue,\n updateFontColor, updateFontFamily, updateTextAlign,\n ]);\n\n // Stable lazy handles (re-render containment, ADR-0085 / D3): the ref\n // tracks the latest fat value; the context VALUE identity never changes, so\n // consumers never re-render from editor state churn. For chrome that only\n // needs editor state inside event handlers (Editor.Menu / Editor.Add —\n // save, export, add-element), not at render time.\n handlesRef.current = value;\n\n return (\n <EditorHandlesContext.Provider value={handles}>\n <HistoryProvider\n value={{\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n }}\n >\n <ElementsProvider\n value={{\n elements,\n elementStore,\n artboards,\n artboardManager,\n artboardVersion,\n setElements,\n refreshArtboards,\n getElementById,\n handleAddElement,\n handleElementUpdate,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\n fontSize,\n fontColor,\n fontFamily,\n textAlign,\n updateFontSize,\n setFontSizeValue,\n updateFontColor,\n updateFontFamily,\n updateTextAlign,\n canvasRef,\n canvasEditorRef,\n }}\n >\n <CommandProvider\n value={{\n executeElementUpdate,\n executeAddElement,\n executeRemoveElement,\n executeReorderElement,\n executeCommandBatch,\n executeCreateArtboard,\n executeDeleteArtboard,\n executeUpdateArtboard,\n undo,\n redo,\n canUndo,\n canRedo,\n undoActiveArtboard,\n redoActiveArtboard,\n canUndoActiveArtboard,\n canRedoActiveArtboard,\n historyManager,\n }}\n >\n <EditorContext.Provider value={value}>{children}</EditorContext.Provider>\n </CommandProvider>\n </ElementsProvider>\n </HistoryProvider>\n </EditorHandlesContext.Provider>\n );\n};\n\n/**\n * Provides editor state and operations to all descendant components.\n *\n * Composes sub-providers (ViewportProvider, SelectionProvider, ToolStateProvider,\n * HistoryProvider) around an internal EditorProvider. This gives each sub-context\n * its own React context, so changes to viewport state (e.g., zoom) won't re-render\n * components that only subscribe to selection state, and vice versa.\n *\n * Must wrap any component that calls `useEditor()` or any of the convenience\n * hooks (`useArtboards`, `useLayers`, `useViewportContext`, etc.).\n *\n * @example\n * ```tsx\n * import { EditorProvider, Canvas, useEditor } from '@snowcone-app/canvas';\n *\n * function MyEditor() {\n * return (\n * <EditorProvider initialArtboardConfig={{ width: 1200, height: 1200 }}>\n * <Canvas style={{ width: '100%' }} />\n * <CustomToolbar />\n * </EditorProvider>\n * );\n * }\n * ```\n */\nexport const EditorProvider: React.FC<EditorProviderProps> = ({\n children,\n initialArtboardConfig = {},\n viewPadding = 0.9,\n}) => {\n // Initialize artboard manager synchronously to avoid race conditions\n const [artboardManager] = useState(() => {\n const manager = new ArtboardManager();\n const config = {\n name: initialArtboardConfig.name || 'Artboard 1',\n x: initialArtboardConfig.x ?? 0,\n y: initialArtboardConfig.y ?? 0,\n width: initialArtboardConfig.width ?? 1000,\n height: initialArtboardConfig.height ?? 1000,\n backgroundColor:\n initialArtboardConfig.backgroundColor || DEFAULT_ARTBOARD_COLOR,\n };\n manager.createArtboard(config);\n return manager;\n });\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const canvasEditorRef = useRef<CanvasEditorHandle>(null);\n\n return (\n <ViewportProvider\n artboardManager={artboardManager}\n canvasRef={canvasRef}\n viewPadding={viewPadding}\n >\n <SelectionProvider>\n <ToolStateProvider>\n <InternalEditorProvider\n artboardManager={artboardManager}\n canvasRef={canvasRef}\n canvasEditorRef={canvasEditorRef}\n >\n {children}\n </InternalEditorProvider>\n </ToolStateProvider>\n </SelectionProvider>\n </ViewportProvider>\n );\n};\n\n/**\n * Access the full editor context: elements, selection, artboards, history, and operations.\n *\n * ## Stability contract (ADR-0085)\n *\n * `useEditor()` is the headless floor of the `Editor.*` primitives. Its v1\n * STABLE subset — pinned by the type-level snapshot in\n * `components/editor/__tests__/useEditorStableSubset.test.ts` — is:\n *\n * - **Elements**: `elements`, `elementStore`, `getElementById`\n * - **Selection**: `selectedId`, `multiSelection`, `selectedElement`,\n * `setSelectedId`, `setMultiSelection`, `handleSelectionChange`\n * - **Command execution**: `executeElementUpdate`, `executeAddElement`,\n * `executeRemoveElement`, `executeReorderElement`, `executeCommandBatch`\n * - **Undo/redo**: `undo`, `redo`, `canUndo`, `canRedo`\n *\n * Everything else on the returned value is `@experimental` and may change in\n * minors — the stable subset above is the documented headless contract.\n *\n * Must be called inside an `EditorProvider`. For focused access, prefer the\n * sub-context hooks which expose smaller slices of state and cause fewer re-renders:\n *\n * - `useViewportContext()` - zoom, pan\n * - `useSelectionContext()` - selection, hover\n * - `useHistoryContext()` - undo, redo\n * - `useToolStateContext()` - panel state, toolbar menus\n * - `useElementsContext()` - elements, artboards, element operations, font properties\n * - `useCommandContext()` - command execution (execute*), undo/redo, historyManager\n *\n * Or the convenience hooks:\n * - `useArtboards()`, `useLayers()`, `useExport()`, `useCommands()`\n * - `useSelectedElement()`, `useElementById()`, `useCanvasReady()`, `useViewport()`\n *\n * @returns The complete editor context value\n * @throws {Error} If called outside of an `EditorProvider`\n *\n * @example\n * ```tsx\n * function StatusBar() {\n * const { elements, selectedId, zoom, canUndo, canRedo, undo, redo } = useEditor();\n *\n * return (\n * <div>\n * <span>{elements.length} elements</span>\n * <span>Zoom: {Math.round(zoom * 100)}%</span>\n * <button onClick={undo} disabled={!canUndo}>Undo</button>\n * <button onClick={redo} disabled={!canRedo}>Redo</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useEditor = (): EditorContextValue => {\n const context = useContext(EditorContext);\n if (!context) {\n throw new Error('useEditor must be used within EditorProvider');\n }\n return context;\n};\n","/**\n * Test-side hooks into the `Editor.*` error contract (ADR-0085 / D11).\n *\n * Containment is a PROD feature: under `NODE_ENV=test`, Root's boundary\n * rethrows render-time composition errors, so a broken composition fails the\n * test naturally. The remaining blind spot is the NON-throwing channel —\n * runtime failures routed straight to the error card (e.g. the lazy renderer\n * chunk failing to load, `E_CHUNK_LOAD_FAILED`) and prod-routed handler\n * invariants. Those are recorded here in test envs so\n * {@link expectNoEditorErrors} can fail a green-looking test that actually\n * rendered an error card.\n *\n * ```ts\n * import { expectNoEditorErrors } from '@snowcone-app/canvas/testing';\n *\n * afterEach(() => expectNoEditorErrors());\n * ```\n */\n\nimport { isEditorError, isDevOrTest } from '../errors/editorError.js';\n\nconst recorded: Error[] = [];\n\n/** Record a contained editor error (test/dev envs only). @internal */\nexport function recordContainedEditorError(error: Error): void {\n if (isDevOrTest()) recorded.push(error);\n}\n\n/** Errors contained since the last drain, without draining. */\nexport function getContainedEditorErrors(): readonly Error[] {\n return [...recorded];\n}\n\n/** Drop all recorded errors (e.g. after asserting an EXPECTED failure). */\nexport function resetContainedEditorErrors(): void {\n recorded.length = 0;\n}\n\n/**\n * Assert that no editor error was contained (rendered as an error card)\n * since the last reset/call. Drains the record either way so one leaked\n * error doesn't fail every later test in the file.\n */\nexport function expectNoEditorErrors(): void {\n if (recorded.length === 0) return;\n const summary = recorded\n .map((e) => (isEditorError(e) ? `${e.code}: ${e.message}` : `${e.name}: ${e.message}`))\n .join('\\n - ');\n recorded.length = 0;\n throw new Error(\n `expectNoEditorErrors: ${summary.includes('\\n') ? 'errors were' : 'an error was'} contained by ` +\n `<Editor.Root>'s boundary during this test:\\n - ${summary}\\n` +\n 'A contained error renders an error card in prod — this composition is broken even if the test rendered \"successfully\".',\n );\n}\n","/**\n * Serialize canvas state (from onChange/toJSON) to the format the export worker expects.\n *\n * This bridges the gap between SnowconeCanvas.onChange() output (element configs with\n * transformType and nested transformData) and the export worker's expected format\n * (elements with 'type' field and flattened image properties).\n *\n * Use this when sending canvas state to a server-side renderer that runs the export\n * worker bundle. The client-side serializeForWorkerExport() requires live element\n * instances; this function works on plain JSON from onChange/toJSON.\n */\n\n// Pure value helper from the MODEL side (core) — serialize-for-server is part\n// of the always-mounted state graph (D9 exemption), so it must not pull the\n// heavy pixel pipeline in `rendering/image-adjustments.ts`.\nimport { isNeutralAdjustments } from '../core/image-adjustments.js';\n\n/**\n * Wire-schema version of the server-render `canvas_state` payload (ADR-0079).\n *\n * This is a PUBLIC contract: third parties send `ServerRenderRequest` (produced\n * by {@link serializeStateForServer}) to Snowcone's realtime renderer. Bump the\n * MAJOR whenever the element/artboard shape changes incompatibly so a renderer\n * can reject states it cannot render instead of silently mis-rendering. Servers\n * treat a MISSING `schemaVersion` as `1` — legacy clients that predate the\n * stamp keep working.\n */\nexport const CANVAS_STATE_SCHEMA_VERSION = 1;\n\n// NOTE: this is a `type`, not an `interface`, ON PURPOSE. The SDK's\n// `RenderSession.renderState(placement, state)` takes a `RenderState` that has a\n// string index signature (`[key: string]: unknown`). A named `interface` does\n// NOT get TypeScript's implicit index signature, so an `interface` value is not\n// assignable to an indexed type — passing `serializeStateForServer()`'s result\n// straight into `renderState()` (the headline realtime flow) fails with TS2345\n// \"Index signature for type 'string' is missing\". A `type` alias DOES get the\n// implicit signature, so the flow type-checks with no cast. Do not convert this\n// back to `interface`. Pinned by @snowcone-app/contracts\n// (src/realtime-getting-started.contract.ts).\nexport type ServerRenderRequest = {\n /** Wire-schema version — see {@link CANVAS_STATE_SCHEMA_VERSION}. */\n schemaVersion: number;\n artboards: Array<{\n id: string;\n name: string;\n x: number;\n y: number;\n width: number;\n height: number;\n backgroundColor: string;\n exportBackground: boolean;\n elements: any[];\n distressTexture?: any;\n imageMask?: any;\n }>;\n /** Custom (brand) font dependencies — the server worker's loadFonts needs\n * these to fetch non-Google faces. Omitted when the design uses none. */\n customFonts?: Array<{ family: string; url: string; weight?: number; style?: string }>;\n};\n\n/**\n * Quantize a geometry value to a stable precision (0.01px).\n *\n * `SnowconeCanvas.onChange` fires on the render loop and reports element\n * geometry with sub-pixel float drift (e.g. `height: 355.9609375`) even when the\n * design is idle. Serialized verbatim, every frame produces a *different* wire\n * state, so a `onChange → renderState` wiring streams renders continuously while\n * the user does nothing — silently burning the realtime render budget. Rounding\n * geometry to a precision well below a pixel collapses that drift so identical\n * idle frames serialize identically (and the SDK's send-dedupe drops them).\n */\nfunction q(n: unknown): unknown {\n return typeof n === 'number' && Number.isFinite(n) ? Math.round(n * 100) / 100 : n;\n}\n\n/** Return a shallow copy with geometry fields quantized (see {@link q}). */\nfunction quantizeGeometry<T extends Record<string, any>>(el: T): T {\n return {\n ...el,\n ...(el.x !== undefined && { x: q(el.x) }),\n ...(el.y !== undefined && { y: q(el.y) }),\n ...(el.width !== undefined && { width: q(el.width) }),\n ...(el.height !== undefined && { height: q(el.height) }),\n ...(el.rotation !== undefined && { rotation: q(el.rotation) }),\n };\n}\n\n/**\n * Convert onChange canvas state to the server render format.\n *\n * Emits EVERY artboard in the state, each holding its own elements: elements\n * are grouped by their `artboard` tag (the artboard NAME `SnowconeCanvas.onChange`\n * stamps on each element). Elements without a tag — legacy saved states — land\n * on the first artboard, which also keeps single-artboard output byte-identical\n * to before. The tag itself is client-side metadata and is stripped from the\n * wire elements, so the per-placement `{schemaVersion, artboards: [...]}` shape\n * the server consumes is unchanged.\n *\n * For a multi-placement product, slice the result per placement:\n *\n * ```ts\n * const wire = serializeStateForServer(state);\n * for (const ab of wire.artboards) {\n * session.renderState(ab.name, { schemaVersion: wire.schemaVersion, artboards: [ab] });\n * }\n * ```\n *\n * @param state - The state from SnowconeCanvas onChange callback\n * @returns ServerRenderRequest ready to send via sendCanvasState()\n */\nexport function serializeStateForServer(state: {\n elements?: any[];\n artboards?: Array<{\n name?: string;\n width?: number;\n height?: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n clipShape?: any;\n distressTexture?: any;\n imageMask?: any;\n /** Pacing role (multi-page templates, ADR-0086) — client/template\n * metadata, stripped from the wire like the element `artboard` tag. */\n role?: string;\n }>;\n activeArtboard?: string;\n customFonts?: Array<{ family: string; url: string; weight?: number; style?: string }>;\n}): ServerRenderRequest {\n const srcArtboards = state.artboards?.length\n ? state.artboards\n : [{ name: 'Front', width: 800, height: 800 }];\n\n // Group elements by their artboard tag (artboard NAME, stamped by\n // SnowconeCanvas.onChange). Untagged or unknown-tagged elements fall back to\n // the first artboard — today's behavior for legacy flat states.\n const elementsByArtboard = srcArtboards.map<any[]>(() => []);\n for (const el of state.elements || []) {\n const tag = el?.artboard;\n const idx = typeof tag === 'string'\n ? srcArtboards.findIndex((ab) => (ab.name || 'Front') === tag)\n : -1;\n elementsByArtboard[idx >= 0 ? idx : 0].push(serializeElement(el));\n }\n\n // Wire artboards are built field-by-field: template metadata on the source\n // artboards (the `role` pacing tag, ADR-0086) never reaches the server —\n // same rule as the element `artboard` membership tag below.\n return {\n schemaVersion: CANVAS_STATE_SCHEMA_VERSION,\n artboards: srcArtboards.map((srcArtboard, i) => ({\n id: `artboard-${i + 1}`,\n name: srcArtboard.name || 'Front',\n x: 0,\n y: 0,\n width: q(srcArtboard.width || 800) as number,\n height: q(srcArtboard.height || 800) as number,\n backgroundColor: srcArtboard.backgroundColor || 'transparent',\n // Pass the artboard's flag through — the server worker only paints the\n // background when this is true. Hardcoding `false` here silently dropped\n // explicitly-set artboard backgrounds from every realtime mockup while\n // the editor kept showing them (WYSIWYG drift).\n exportBackground: srcArtboard.exportBackground ?? false,\n elements: elementsByArtboard[i],\n distressTexture: srcArtboard.distressTexture,\n imageMask: srcArtboard.imageMask,\n })),\n // Custom (brand) font dependencies ride the wire so the server worker can\n // load them — without this, server renders of custom-font designs fall\n // back to the wrong family even when the bundle supports custom fonts.\n ...(state.customFonts?.length ? { customFonts: state.customFonts } : {}),\n };\n}\n\n/**\n * Serialize a single element from toJSON() format to export worker format.\n */\nfunction serializeElement(el: any): any {\n const type = el.type || el.transformType;\n\n if (type === 'image') {\n return serializeImageElement(el);\n }\n\n // Text, shape, group, path elements: map transformType → type, keep everything else\n // Text, shape, group, path elements: pass through as-is with type mapped.\n // Builtin mask URLs (builtin-mask:*) are kept as-is — the server worker\n // generates them procedurally via generateMaskBitmap().\n // Geometry is quantized (see `q`) so idle render-loop float drift doesn't\n // produce a new wire state every frame.\n // The `artboard` membership tag is CLIENT-side metadata (it already decided\n // which wire artboard this element belongs to) — strip it so the server\n // payload shape stays unchanged.\n const { artboard: _artboard, ...rest } = el;\n return quantizeGeometry({\n ...rest,\n type,\n });\n}\n\n/**\n * Serialize an image element, flattening transformData to top-level fields\n * as expected by SerializedImageElement and renderImageElement.\n */\nfunction serializeImageElement(el: any): any {\n const td = el.transformData || {};\n const hasMasks = el.masks && el.masks.length > 0;\n\n // Visible dimensions depend on crop and masks\n const fullWidth = td.width || 0;\n const fullHeight = td.height || 0;\n const visibleWidth = hasMasks ? fullWidth : fullWidth * (td.cropWidth ?? 1);\n const visibleHeight = hasMasks ? fullHeight : fullHeight * (td.cropHeight ?? 1);\n\n const serialized: any = {\n id: el.id,\n type: 'image' as const,\n x: el.x || 0,\n y: el.y || 0,\n width: visibleWidth,\n height: visibleHeight,\n rotation: el.rotation || 0,\n imageUrl: el.imageUrl,\n imageAspectRatio: el.imageAspectRatio,\n };\n\n // Crop (only if actually cropped and no masks)\n const isCropped = !hasMasks && (td.cropX !== 0 || td.cropY !== 0 || td.cropWidth !== 1 || td.cropHeight !== 1);\n if (isCropped) {\n // Note: crop values stay as fractions here — the server's Chrome tab\n // will convert to pixels using the actual bitmap dimensions after fetch.\n // The client doesn't have the bitmap dimensions available in onChange state.\n serialized.cropX = td.cropX;\n serialized.cropY = td.cropY;\n serialized.cropWidth = td.cropWidth;\n serialized.cropHeight = td.cropHeight;\n serialized.needsCropPixelConversion = true; // Flag for server to convert\n }\n\n // Flip and border radius\n if (td.flipHorizontal) serialized.flipHorizontal = td.flipHorizontal;\n if (td.flipVertical) serialized.flipVertical = td.flipVertical;\n if (td.borderRadius) serialized.borderRadius = td.borderRadius;\n // Drop shadow — non-image elements pass it through via the {...rest} spread;\n // this allowlist is the only place an image element's shadow could get lost.\n if (el.shadow) serialized.shadow = el.shadow;\n // Brightness/contrast/saturation — applied by the server worker's\n // renderImageElement after it fetches the bitmap. Omitted when neutral;\n // older renderers ignore the unknown field (additive wire change).\n if (!isNeutralAdjustments(td.adjustments)) serialized.adjustments = { ...td.adjustments };\n if (el.opacity !== undefined) serialized.opacity = el.opacity;\n\n // Effects\n if (el.distressEffect) serialized.distressEffect = el.distressEffect;\n if (el.masks?.length > 0) serialized.masks = el.masks;\n if (el.blendMode) serialized.blendMode = el.blendMode;\n if (el.knockoutParts) serialized.knockoutParts = el.knockoutParts;\n if (el.stroke) serialized.stroke = el.stroke;\n\n // Quantize geometry so idle render-loop float drift doesn't churn the wire state.\n return quantizeGeometry(serialized);\n}\n","/**\n * Declarative design builder — compose a multi-element design by *intent*\n * (full-bleed background, anchored text, anchored shapes) and never by raw\n * coordinates.\n *\n * WHY THIS EXISTS\n * ---------------\n * The canvas/render contract positions every element by its CENTER point\n * (`x, y` is the centre, not the top-left — see CLAUDE.md). That convention is\n * correct for the interactive editor (rotation/scale pivot around centre) but is\n * a foot-gun for anyone hand-constructing a `canvas_state`: the natural\n * \"fill the placement\" attempt (`x:0, y:0, width:full`) centres the element on\n * the artboard's top-left CORNER, so it renders into the top-left quadrant. An\n * expert integrator hit exactly this and mis-diagnosed it as a server bug.\n *\n * This builder makes that class of mistake impossible: you describe *what* you\n * want (a background that fills, a title anchored top-centre, a star bottom-left)\n * and it emits a correct editor-shape canvas state. The centre-coordinate maths\n * lives here, once, instead of in every integrator's head.\n *\n * THREE RENDER TARGETS, ONE DESIGN\n * --------------------------------\n * The builder emits the EDITOR shape (`transformType` + nested `transformData`,\n * centre coords), which is exactly what both consumers already accept:\n * - `design.toJSON()` → `<SnowconeCanvas initialElements=… />` (interactive)\n * - `design.toServerRequest()` → `session.renderState(placement, …)` (realtime)\n * - (single asset on a placement → use `buildMockupUrl` in the SDK instead)\n *\n * So a developer can author a design headlessly, render it live, AND hand the\n * same design to the editor for the end user to drag/scale/rotate from there.\n */\n\nimport type {\n AnyElementConfig,\n ArtboardRole,\n ImageElementConfig,\n CustomElementConfig,\n ShapeElementConfig,\n ShapeTransformData,\n} from '../types/index.js';\nimport { serializeStateForServer, type ServerRenderRequest } from '../rendering/serialize-for-server.js';\nimport { EditorError } from '../errors/editorError.js';\n\n/** A nine-point anchor: where the element sits relative to the artboard. */\nexport type Anchor =\n | 'top-left' | 'top' | 'top-right'\n | 'left' | 'center' | 'right'\n | 'bottom-left' | 'bottom' | 'bottom-right';\n\nexport type ShapeKind =\n | 'rectangle' | 'circle' | 'ellipse' | 'triangle' | 'polygon' | 'star' | 'line';\n\n/** The artboard a design is composed on — one print placement. `width`/`height`\n * are the placement's pixel dimensions (e.g. 1480×2328 for a phone-case Front).\n * `name` MUST match the placement label you render (case-sensitive). */\nexport interface DesignArtboard {\n /** Placement label (case-sensitive — e.g. 'Front'). Defaults to 'Front'. */\n name?: string;\n width: number;\n height: number;\n /** Optional solid background fill behind all elements. Rendered everywhere\n * the design is — editor, exports, and server renders (realtime mockups).\n * NOTE: the background is not an element, so it is NOT repaintable by\n * `useColorBinding` — designs that want a rebindable backdrop paint a\n * full-bleed `shape('rectangle', { fill: … })` instead. */\n backgroundColor?: string | ColorToken;\n /** Named color ROLES for value-matched rebinding (`useColorBinding`).\n * Mint tokens with `design.color('role')`; use them as `fill`/`color`\n * values so role and element can never drift. Validated at mint:\n * duplicate values across roles throw `E_COLOR_ROLE_COLLISION` (a\n * value-matched binding can't tell colliding roles apart), non-hex\n * values throw `E_COLOR_ROLE_INVALID`. */\n colors?: Record<string, string>;\n /** Pacing role in a multi-page template (ADR-0086 albums). Template\n * metadata only — never sent on the server render wire. */\n role?: ArtboardRole;\n}\n\n/** One artboard of a saved multi-artboard state, as {@link DesignBuilder.fromState}\n * accepts and `toJSON()` emits. */\nexport interface StateArtboard {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n exportBackground?: boolean;\n /** Pacing role in a multi-page template (ADR-0086 albums). */\n role?: ArtboardRole;\n}\n\nexport interface FromStateOptions {\n /**\n * Which artboards to load:\n * - `'active'` (default) — today's behavior: the active artboard becomes the\n * builder's artboard; elements tagged for other artboards are skipped.\n * - `'all'` — multi-page templates (ADR-0086): every artboard and its\n * elements are kept, with `artboard` membership tags preserved, so\n * `toJSON()` / `toServerRequest()` stay multi-artboard. Template fields\n * scope per page via `TemplateField.artboard`.\n */\n artboards?: 'active' | 'all';\n}\n\n/** A stable layer name. Set it so you can bind the layer in an interactive\n * `<SnowconeCanvas/>` editor — `useImageBinding(name)` / `useTextBinding(name)`\n * find the element by `name`. Keep names unique per design. */\ntype NamedOption = { name?: string };\n\nexport interface BackgroundOptions extends NamedOption {\n /** Source image aspect ratio (w/h). When given, the image is COVER-fit (filled\n * + centre-cropped) with no distortion. When omitted, the image fills the\n * artboard box directly (correct when it's already the placement's aspect,\n * e.g. an AI image generated at the placement size). */\n aspectRatio?: number;\n opacity?: number;\n}\n\nexport interface ImageOptions extends NamedOption {\n anchor?: Anchor;\n /** Display width in artboard px. Defaults to a third of the artboard width. */\n width?: number;\n /** Display height in px. Defaults to `width / aspectRatio` (or square). */\n height?: number;\n aspectRatio?: number;\n opacity?: number;\n rotation?: number;\n /** Inset from the artboard edge for non-centre anchors, in px. Default 0. */\n padding?: number;\n}\n\nexport interface TextOptions extends NamedOption {\n anchor?: Anchor;\n fontSize?: number;\n fontFamily?: string;\n /** Hex string, or a `design.color('role')` token (resolved at build). */\n color?: string | ColorToken;\n /** Horizontal text alignment. Named `textAlign` to match the canvas editor's\n * element field (not `align`), so the builder and the editor read the same. */\n textAlign?: 'left' | 'center' | 'right';\n bold?: boolean;\n italic?: boolean;\n rotation?: number;\n padding?: number;\n}\n\nexport interface ShapeOptions extends NamedOption {\n anchor?: Anchor;\n /** Square size, or explicit box. Default 200×200. */\n size?: number | { width: number; height: number };\n /** Hex string, or a `design.color('role')` token (resolved at build). */\n fill?: string | ColorToken;\n rotation?: number;\n opacity?: number;\n /** star points / polygon sides. */\n points?: number;\n sides?: number;\n padding?: number;\n}\n\nconst DEFAULT_ANCHOR: Anchor = 'center';\n\n// ── Typed layer tokens (ADR-0085 §1 / T1) ────────────────────────────────────\n\n/** The bindable layer kinds. Shapes are not bindable in v1. */\nexport type LayerKind = 'text' | 'image';\n\n/**\n * Any design builder, regardless of its compile-time layer-name unions.\n * CONSUMERS of a builder (props, context) must use this — `DesignBuilder`\n * (= `DesignBuilder<string, string>`) is not a supertype of a statically\n * typed builder because `layer()`'s name constraint narrows with the design.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- variance escape: `any` here means \"accept every name union\", checked at mint/mount instead\nexport type AnyDesign = DesignBuilder<any, any>;\n\ndeclare const LAYER_TOKEN_BRAND: unique symbol;\n/** Runtime marker carried by every minted token (survives serialization\n * boundaries within one realm; `Symbol.for` so duplicated module instances\n * still agree). */\nconst LAYER_TOKEN_MARKER = Symbol.for('snowcone.canvas.layerToken');\n\n/**\n * A validated claim that layer `N` of kind `K` exists in the design that\n * minted it. The ONLY currency `Editor.TextField` (and future `ImageField`)\n * accepts — bare strings are rejected (`E_LAYER_TOKEN_REQUIRED`). Mint with\n * {@link DesignBuilder.layer}; not constructible by hand (branded).\n *\n * Stable since ADR-0085 P4 (the default editor is rebuilt from this public\n * surface, CI-gated); additive-only from here.\n */\nexport interface LayerToken<K extends LayerKind = LayerKind, N extends string = string> {\n readonly kind: K;\n readonly name: N;\n /** @internal */\n readonly [LAYER_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by parts before trusting a `layer` prop. */\nexport function isLayerToken(value: unknown): value is LayerToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[LAYER_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n ((value as { kind?: unknown }).kind === 'text' || (value as { kind?: unknown }).kind === 'image')\n );\n}\n\nfunction mintLayerToken(kind: LayerKind, name: string): LayerToken {\n return Object.freeze({ kind, name, [LAYER_TOKEN_MARKER]: true }) as unknown as LayerToken;\n}\n\n// ── Color-role tokens ────────────────────────────────────────────────────────\n\ndeclare const COLOR_TOKEN_BRAND: unique symbol;\nconst COLOR_TOKEN_MARKER = Symbol.for('snowcone.canvas.colorToken');\n\n/**\n * A validated claim that color role `N` exists in the design that minted it,\n * carrying the role's (normalized) hex value. `useColorBinding` accepts it in\n * place of a bare hex, so the chip and the painted elements can never drift —\n * and `design({ colors })` rejects role-value collisions at mint time, which a\n * value-matched binding cannot survive (one role's repaint would capture the\n * other role's elements). Mint with {@link DesignBuilder.color}; not\n * constructible by hand (branded).\n */\nexport interface ColorToken<N extends string = string> {\n readonly name: N;\n /** Normalized uppercase `#RRGGBB`. */\n readonly value: string;\n /** @internal */\n readonly [COLOR_TOKEN_BRAND]: true;\n}\n\n/** Runtime check used by `useColorBinding` (and the builder's value slots)\n * before trusting a color value. */\nexport function isColorToken(value: unknown): value is ColorToken {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as Record<symbol, unknown>)[COLOR_TOKEN_MARKER] === true &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { value?: unknown }).value === 'string'\n );\n}\n\nfunction mintColorToken(name: string, value: string): ColorToken {\n return Object.freeze({ name, value, [COLOR_TOKEN_MARKER]: true }) as unknown as ColorToken;\n}\n\n/** Normalize `#RGB`/`#RRGGBB` (any case) to uppercase `#RRGGBB` — the same\n * normalization `useColorBinding` matches with. Returns null when the value\n * is not a parsable hex color. */\nexport function normalizeRoleHex(color: string): string | null {\n let hex = color.trim().toUpperCase();\n if (/^#[0-9A-F]{3}$/.test(hex)) {\n hex = '#' + hex[1]! + hex[1]! + hex[2]! + hex[2]! + hex[3]! + hex[3]!;\n }\n return /^#[0-9A-F]{6}$/.test(hex) ? hex : null;\n}\n\n/** A color value as builder options accept it: a hex string or a minted role\n * token. Resolved to the plain hex at element-build time. */\nfunction resolveColor(value: string | ColorToken): string;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined;\nfunction resolveColor(value: string | ColorToken | undefined): string | undefined {\n return isColorToken(value) ? value.value : value;\n}\n\n/** The token kind inferred for name `N` against the builder's accumulated\n * per-kind name unions. A dynamically-loaded design (both unions degraded to\n * `string`) yields the full `'text' | 'image'` union — typing degrades, the\n * runtime validation in `layer()` is the net that still fires (0G#2). */\ntype InferKind<N extends string, TText extends string, TImage extends string> =\n [N] extends [TText] ? ([N] extends [TImage] ? LayerKind : 'text') : 'image';\n\n/** Kind of an element config, for token validation. `null` = not bindable. */\nfunction elementLayerKind(el: AnyElementConfig): LayerKind | null {\n const t = (el as { transformType?: string }).transformType;\n if (t === 'image') return 'image';\n if (t === 'shape' || t === 'group' || t === 'path') return null;\n // Every text transform ('custom', 'circle', 'arch', …) is a text layer.\n return 'text';\n}\n\nfunction hPart(a: Anchor): 'left' | 'center' | 'right' {\n if (a === 'top-left' || a === 'left' || a === 'bottom-left') return 'left';\n if (a === 'top-right' || a === 'right' || a === 'bottom-right') return 'right';\n return 'center';\n}\n\nfunction vPart(a: Anchor): 'top' | 'middle' | 'bottom' {\n if (a === 'top-left' || a === 'top' || a === 'top-right') return 'top';\n if (a === 'bottom-left' || a === 'bottom' || a === 'bottom-right') return 'bottom';\n return 'middle';\n}\n\n/**\n * Resolve an element's CENTRE (x, y) so its bounding box sits at `anchor` on the\n * artboard, inset by `padding`. This is the whole point of the builder: a\n * `center` anchor on a full-bleed element returns the artboard centre, never\n * (0, 0).\n */\nfunction anchorToCenter(\n anchor: Anchor,\n elW: number,\n elH: number,\n artW: number,\n artH: number,\n padding: number,\n): { x: number; y: number } {\n const h = hPart(anchor);\n const v = vPart(anchor);\n const x = h === 'left' ? elW / 2 + padding\n : h === 'right' ? artW - elW / 2 - padding\n : artW / 2;\n const y = v === 'top' ? elH / 2 + padding\n : v === 'bottom' ? artH - elH / 2 - padding\n : artH / 2;\n return { x, y };\n}\n\n/** Compute the centred cover-crop (fractions, 0–1) for a source of aspect `ar`\n * filling an `artW×artH` box, plus the full (uncropped) display size the editor\n * stores in `transformData`. Mirrors the editor's cover behaviour but needs only\n * the aspect ratio, not the pixel dimensions. */\nfunction coverCrop(ar: number, artW: number, artH: number) {\n const artAR = artW / artH;\n if (ar >= artAR) {\n // Source is wider than the box → crop the sides.\n const fullW = artH * ar;\n const cropWidth = artW / fullW; // = artAR / ar\n return {\n width: fullW, height: artH,\n cropX: (1 - cropWidth) / 2, cropY: 0, cropWidth, cropHeight: 1,\n };\n }\n // Source is taller than the box → crop top/bottom.\n const fullH = artW / ar;\n const cropHeight = artH / fullH; // = ar / artAR\n return {\n width: artW, height: fullH,\n cropX: 0, cropY: (1 - cropHeight) / 2, cropWidth: 1, cropHeight,\n };\n}\n\n/**\n * A composed design. Add elements by intent, then emit it for the editor or the\n * render path. Methods are chainable.\n *\n * The two type parameters accumulate the design's NAMED layer literals per\n * kind (ADR-0085 §1): naming a layer in `.text()` / `.image()` /\n * `.background()` widens the corresponding union, so `design.layer(\"naem\")`\n * is a compile error listing the valid names. Designs loaded from JSON\n * ({@link DesignBuilder.fromState}) degrade both unions to `string` — same\n * API, runtime-validated instead (the degradation is deliberate and LOUD in\n * docs; never block the dynamic case).\n */\nexport class DesignBuilder<\n out TText extends string = string,\n out TImage extends string = string,\n out TColor extends string = string,\n> {\n /** The CURRENT authoring artboard — anchor math and new elements target it.\n * Reassigned by {@link DesignBuilder.artboard}; the initially-active\n * placement stays {@link activeName}. */\n private current: Required<Pick<DesignArtboard, 'name' | 'width' | 'height'>> & {\n backgroundColor?: string;\n role?: ArtboardRole;\n };\n /** The placement that is active when the design mounts — always the FIRST\n * artboard (or the saved state's active one), never moved by `.artboard()`. */\n private readonly activeName: string;\n private readonly els: AnyElementConfig[] = [];\n /** Declared color roles, name → normalized uppercase `#RRGGBB`. */\n private readonly colorRoles = new Map<string, string>();\n private idCounter = 0;\n /** Every artboard of a multi-artboard design — set by `fromState({ artboards:\n * 'all' })` or the first `.artboard()` call; unset for single-artboard\n * builders. */\n private allArtboards?: StateArtboard[];\n\n constructor(artboard: DesignArtboard) {\n if (!(artboard.width > 0) || !(artboard.height > 0)) {\n throw new Error('design(): artboard width and height must be positive numbers');\n }\n for (const [roleName, raw] of Object.entries(artboard.colors ?? {})) {\n const value = normalizeRoleHex(raw);\n if (value === null) {\n throw new EditorError(\n 'E_COLOR_ROLE_INVALID',\n `color role \"${roleName}\" is ${JSON.stringify(raw)} — roles must be #RGB/#RRGGBB hex ` +\n `(useColorBinding matches by normalized hex value; other syntaxes can't bind).`,\n { roleName, value: raw, docSlug: 'embed-mode' },\n );\n }\n const collision = [...this.colorRoles.entries()].find(([, v]) => v === value);\n if (collision) {\n throw new EditorError(\n 'E_COLOR_ROLE_COLLISION',\n `color roles \"${collision[0]}\" and \"${roleName}\" both normalize to ${value}. ` +\n `useColorBinding matches elements by VALUE, so colliding roles would repaint ` +\n `each other's elements — give every role a distinct hex.`,\n { roles: [collision[0], roleName], value, docSlug: 'embed-mode' },\n );\n }\n this.colorRoles.set(roleName, value);\n }\n this.current = {\n name: artboard.name || 'Front',\n width: artboard.width,\n height: artboard.height,\n backgroundColor: resolveColor(artboard.backgroundColor),\n role: artboard.role,\n };\n this.activeName = this.current.name;\n }\n\n /**\n * Seed a builder from a previously saved canvas state — the editor shape\n * `<SnowconeCanvas onChange>` emits (and `toJSON()` produces).\n *\n * This is how a saved editable template re-enters the turnkey embed path:\n * load the template's state JSON, lift it back into a `DesignBuilder`, and\n * hand it to `<RealtimeEmbed design={…}>`. By default the active artboard\n * becomes the builder's artboard; elements tagged for other artboards are\n * skipped (untagged elements are assumed to belong to the active one).\n *\n * Pass `{ artboards: 'all' }` to load a multi-page template (ADR-0086)\n * whole: every artboard (with its pacing `role`) and every element are\n * kept, membership tags preserved, so `toJSON()` / `toServerRequest()`\n * emit all pages and `TemplateField.artboard` can scope fields per page.\n */\n static fromState(\n state: {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n },\n options: FromStateOptions = {},\n ): DesignBuilder {\n const artboard =\n state.artboards.find((a) => a.name === state.activeArtboard) ?? state.artboards[0];\n if (!artboard) {\n throw new Error('DesignBuilder.fromState(): state has no artboards');\n }\n const builder = new DesignBuilder({\n name: artboard.name,\n width: artboard.width,\n height: artboard.height,\n backgroundColor: artboard.backgroundColor,\n role: artboard.role,\n });\n if (options.artboards === 'all') {\n builder.allArtboards = state.artboards.map((a) => ({ ...a }));\n for (const el of state.elements) {\n // Untagged elements belong to the active artboard (same assumption as\n // the active-only mode) — stamp them so the multi-artboard state keeps\n // them there instead of collapsing onto the first artboard.\n builder.els.push(el.artboard ? { ...el } : { ...el, artboard: artboard.name });\n }\n return builder;\n }\n for (const el of state.elements) {\n if (el.artboard && el.artboard !== artboard.name) continue;\n builder.els.push({ ...el });\n }\n return builder;\n }\n\n private nextId(prefix: string): string {\n return `${prefix}-${++this.idCounter}`;\n }\n\n /** A wire-shape snapshot of an authoring artboard, applying the same\n * background-export rule everywhere (a design background is part of the\n * design — exports and server renders paint it). */\n private static snapshotArtboard(a: {\n name: string;\n width: number;\n height: number;\n backgroundColor?: string;\n role?: ArtboardRole;\n }): StateArtboard {\n return {\n name: a.name,\n width: a.width,\n height: a.height,\n ...(a.backgroundColor\n ? {\n backgroundColor: a.backgroundColor,\n exportBackground: a.backgroundColor !== 'transparent',\n }\n : {}),\n ...(a.role ? { role: a.role } : {}),\n };\n }\n\n /**\n * Open a NEW artboard (placement) and make it the authoring context: layer\n * calls after this target the new artboard — anchor math uses its\n * dimensions, and the elements carry its membership tag (the public\n * `artboard` field, PR #482). The FIRST artboard (from `design({ name })`)\n * remains the initially-active placement when the design mounts; switch at\n * runtime with `usePlacements()` / `<Editor.Placements/>` (ADR-0088).\n *\n * `label` must be the product's placement label (case-sensitive) — the same\n * artboard-name-is-placement-label rule as `design({ name })`.\n *\n * ```ts\n * const seed = design({ name: 'Front', width: 1480, height: 2328 })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .artboard('Back', { width: 1480, height: 2328 })\n * .text('EST. 2026', { anchor: 'center', name: 'backline' });\n * ```\n *\n * @throws EditorError `E_DUPLICATE_ARTBOARD` — the design already has an\n * artboard with this label.\n */\n artboard(\n label: string,\n dims: { width: number; height: number; backgroundColor?: string | ColorToken; role?: ArtboardRole },\n ): this {\n if (!(dims.width > 0) || !(dims.height > 0)) {\n throw new Error('design().artboard(): width and height must be positive numbers');\n }\n if (!this.allArtboards) {\n this.allArtboards = [DesignBuilder.snapshotArtboard(this.current)];\n }\n if (this.allArtboards.some((a) => a.name === label)) {\n throw new EditorError(\n 'E_DUPLICATE_ARTBOARD',\n `the design already has an artboard \"${label}\" — artboard names are placement ` +\n `labels and must be unique. Existing: ${JSON.stringify(this.allArtboards.map((a) => a.name))}.`,\n { artboardName: label, validNames: this.allArtboards.map((a) => a.name), docSlug: 'embed-mode' },\n );\n }\n // Elements authored so far belong to the artboard that was current when\n // they were added — stamp the untagged ones before switching context.\n for (const el of this.els) {\n if (!el.artboard) el.artboard = this.current.name;\n }\n this.current = {\n name: label,\n width: dims.width,\n height: dims.height,\n backgroundColor: resolveColor(dims.backgroundColor),\n role: dims.role,\n };\n this.allArtboards.push(DesignBuilder.snapshotArtboard(this.current));\n return this;\n }\n\n /** Full-bleed background image (fills the whole artboard). Cover-fit (no\n * distortion) when `aspectRatio` is supplied. Inserted at the bottom of the\n * stack regardless of call order. The default layer name `'Background'`\n * joins the IMAGE-layer token union (auto-injected names are deliberately\n * not one union with text — ADR-0085 §1). */\n background<const N extends string>(\n imageUrl: string,\n opts: BackgroundOptions & { name: N },\n ): DesignBuilder<TText, TImage | N, TColor>;\n background(imageUrl: string, opts?: BackgroundOptions): DesignBuilder<TText, TImage | 'Background', TColor>;\n background(imageUrl: string, opts: BackgroundOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n let td: ImageElementConfig['transformData'];\n if (opts.aspectRatio && opts.aspectRatio > 0) {\n const c = coverCrop(opts.aspectRatio, artW, artH);\n td = {\n type: 'image', width: c.width, height: c.height,\n cropX: c.cropX, cropY: c.cropY, cropWidth: c.cropWidth, cropHeight: c.cropHeight,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n } else {\n td = {\n type: 'image', width: artW, height: artH,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n };\n }\n const el: ImageElementConfig = {\n id: this.nextId('bg'),\n transformType: 'image',\n x: artW / 2,\n y: artH / 2,\n rotation: 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: td,\n name: opts.name ?? 'Background',\n };\n // Background goes underneath everything else.\n this.els.unshift(el);\n return this;\n }\n\n /** An anchored image element (not full-bleed). */\n image<const N extends string>(\n imageUrl: string,\n opts: ImageOptions & { name: N },\n ): DesignBuilder<TText, TImage | N, TColor>;\n image(imageUrl: string, opts?: ImageOptions): DesignBuilder<TText, TImage, TColor>;\n image(imageUrl: string, opts: ImageOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n const w = opts.width ?? Math.round(artW / 3);\n const h = opts.height ?? (opts.aspectRatio ? Math.round(w / opts.aspectRatio) : w);\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const el: ImageElementConfig = {\n id: this.nextId('img'),\n transformType: 'image',\n x, y,\n rotation: opts.rotation ?? 0,\n opacity: opts.opacity ?? 1,\n imageUrl,\n imageAspectRatio: opts.aspectRatio,\n transformData: {\n type: 'image', width: w, height: h,\n cropX: 0, cropY: 0, cropWidth: 1, cropHeight: 1,\n flipHorizontal: false, flipVertical: false, borderRadius: 0,\n },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** An anchored text layer. Horizontal centring is exact (textAlign); vertical\n * anchoring uses the font size as the line-height estimate. */\n text<const N extends string>(\n text: string,\n opts: TextOptions & { name: N },\n ): DesignBuilder<TText | N, TImage, TColor>;\n text(text: string, opts?: TextOptions): DesignBuilder<TText, TImage, TColor>;\n text(text: string, opts: TextOptions = {}): AnyDesign {\n const { width: artW, height: artH } = this.current;\n const fontSize = opts.fontSize ?? Math.round(this.current.height / 12);\n // Text width is metric-dependent (unknown headless); horizontal placement is\n // handled by textAlign, so anchor only drives the vertical centre + the\n // horizontal *reference* point. Use a zero-width box horizontally so the\n // centre sits exactly on the anchor's x line.\n const { x, y } = anchorToCenter(opts.anchor ?? 'top', 0, fontSize, artW, artH, opts.padding ?? Math.round(artH / 20));\n const el: CustomElementConfig = {\n id: this.nextId('text'),\n transformType: 'custom',\n x, y,\n rotation: opts.rotation ?? 0,\n text,\n fontSize,\n fontFamily: opts.fontFamily ?? 'Anton',\n color: resolveColor(opts.color) ?? '#ffffff',\n textAlign: opts.textAlign ?? 'center',\n bold: opts.bold ?? false,\n italic: opts.italic ?? false,\n transformData: { type: 'custom' },\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /**\n * Mint a {@link LayerToken} for a named layer — the only currency\n * `Editor.TextField` accepts (ADR-0085 §1). Compile time: `name` is\n * constrained to this design's declared layer names, kind inferred from\n * which per-kind union it falls in. Runtime: validated EAGERLY against the\n * built elements (nearest the mistake), so CMS/JSON-loaded designs get the\n * same protection with `string` typing.\n *\n * @throws EditorError `E_LAYER_NOT_FOUND` — no layer with this name; lists\n * the valid names per kind.\n * @throws EditorError `E_LAYER_WRONG_KIND` — the name matches a layer that\n * is not bindable (shape/group/path).\n */\n layer<N extends TText | TImage>(name: N): LayerToken<InferKind<N, TText, TImage>, N> {\n const el = this.els.find((e) => (e as { name?: string }).name === name);\n if (!el) {\n const textNames = this.layerNamesOfKind('text');\n const imageNames = this.layerNamesOfKind('image');\n throw new EditorError(\n 'E_LAYER_NOT_FOUND',\n `no layer \"${name}\" in this design. Text layers: ${JSON.stringify(textNames)}; ` +\n `image layers: ${JSON.stringify(imageNames)}. Fix the name, or add the layer ` +\n `first — e.g. design(…).text('…', { name: '${name}' }).`,\n { layerName: name, validNames: [...textNames, ...imageNames], textNames, imageNames, docSlug: 'embed-mode' },\n );\n }\n const kind = elementLayerKind(el);\n if (!kind) {\n throw new EditorError(\n 'E_LAYER_WRONG_KIND',\n `layer \"${name}\" is a ${(el as { transformType?: string }).transformType} element — ` +\n `only text and image layers are bindable. Bindable layers: ` +\n `${JSON.stringify([...this.layerNamesOfKind('text'), ...this.layerNamesOfKind('image')])}.`,\n { layerName: name, actualType: (el as { transformType?: string }).transformType, docSlug: 'embed-mode' },\n );\n }\n return mintLayerToken(kind, name) as LayerToken<InferKind<N, TText, TImage>, N>;\n }\n\n /**\n * Mint a {@link ColorToken} for a declared color role. Compile time: `name`\n * is constrained to the roles declared in `design({ colors })`. Runtime:\n * validated against the declared roles (CMS/JSON-built designs degrade to\n * `string` typing and rely on this throw).\n *\n * Use the token wherever the design paints the role (`fill`, `color`) AND\n * as the `useColorBinding` argument — one source of truth, no hand-synced\n * hex pairs.\n *\n * @throws EditorError `E_COLOR_ROLE_NOT_FOUND` — no role with this name;\n * lists the declared roles.\n */\n color<N extends TColor>(name: N): ColorToken<N> {\n const value = this.colorRoles.get(name);\n if (value === undefined) {\n throw new EditorError(\n 'E_COLOR_ROLE_NOT_FOUND',\n `no color role \"${name}\" in this design. Declared roles: ` +\n `${JSON.stringify([...this.colorRoles.keys()])}. Declare roles up front — ` +\n `design({ …, colors: { ${name}: '#FF2D8A' } }).`,\n { roleName: name, validNames: [...this.colorRoles.keys()], docSlug: 'embed-mode' },\n );\n }\n return mintColorToken(name, value) as ColorToken<N>;\n }\n\n /** Names of bindable layers of one kind, in stack order. @internal */\n layerNamesOfKind(kind: LayerKind): string[] {\n return this.els\n .filter((e) => elementLayerKind(e) === kind)\n .map((e) => (e as { name?: string }).name)\n .filter((n): n is string => Boolean(n));\n }\n\n /** An anchored vector shape. */\n shape(kind: ShapeKind, opts: ShapeOptions = {}): this {\n const { width: artW, height: artH } = this.current;\n const size = opts.size ?? Math.round(artW / 6);\n const w = typeof size === 'number' ? size : size.width;\n const h = typeof size === 'number' ? size : size.height;\n const { x, y } = anchorToCenter(opts.anchor ?? DEFAULT_ANCHOR, w, h, artW, artH, opts.padding ?? 0);\n const transformData: ShapeTransformData = {\n type: 'shape',\n shapeType: kind,\n width: w,\n height: h,\n fillColor: resolveColor(opts.fill) ?? '#000000',\n fillOpacity: opts.opacity ?? 1,\n ...(opts.points !== undefined ? { points: opts.points } : {}),\n ...(opts.sides !== undefined ? { sides: opts.sides } : {}),\n };\n const el: ShapeElementConfig = {\n id: this.nextId('shape'),\n transformType: 'shape',\n x, y,\n rotation: opts.rotation ?? 0,\n transformData,\n ...(opts.name ? { name: opts.name } : {}),\n };\n this.els.push(el);\n return this;\n }\n\n /** The composed elements, in render order (background first). Editor shape. */\n get elements(): AnyElementConfig[] {\n return this.els.map((e) => ({ ...e }));\n }\n\n /** Editor-shape canvas state for `<SnowconeCanvas initialElements=… />` and\n * `serializeStateForServer`. `activeArtboard` is the artboard name. A\n * builder loaded with `fromState(…, { artboards: 'all' })` emits every\n * page (artboard `role`s and element membership tags included). */\n toJSON(): {\n elements: AnyElementConfig[];\n artboards: StateArtboard[];\n activeArtboard: string;\n } {\n if (this.allArtboards) {\n return {\n // Elements added via builder methods since the last context switch are\n // untagged — they belong to the CURRENT authoring artboard; stamp them\n // so the multi-artboard state groups them correctly.\n elements: this.els.map((e) =>\n e.artboard ? { ...e } : { ...e, artboard: this.current.name },\n ),\n artboards: this.allArtboards.map((a) => ({ ...a })),\n // The initially-active placement (first artboard / saved state's\n // active) — NOT the last `.artboard()` authoring context.\n activeArtboard: this.activeName,\n };\n }\n return {\n elements: this.elements,\n // A background set on a design is part of the design: flag it renderable\n // so exports and server renders paint it, matching the editor (the\n // export worker skips it without `exportBackground`) — see snapshotArtboard.\n artboards: [DesignBuilder.snapshotArtboard(this.current)],\n activeArtboard: this.current.name,\n };\n }\n\n /** Server-render payload for `RenderSession.renderState(placement, …)`.\n * Equivalent to `serializeStateForServer(design.toJSON())`. */\n toServerRequest(): ServerRenderRequest {\n return serializeStateForServer(this.toJSON());\n }\n}\n\n/**\n * Start a design on an artboard (one print placement).\n *\n * @example\n * const d = design({ name: 'Front', width: 1480, height: 2328 })\n * .background(aiImageUrl, { aspectRatio: 1480 / 2328, name: 'art' })\n * .text('YOUR NAME', { anchor: 'top', name: 'headline' })\n * .shape('star', { anchor: 'bottom-left', fill: '#000' });\n *\n * session.renderState('Front', d.toServerRequest()); // live\n * // …or hand d.toJSON() to <SnowconeCanvas/> for interactive editing — the\n * // `name`s let you bind layers with useImageBinding('art') / useTextBinding('headline').\n */\nexport function design<const C extends Record<string, string>>(\n artboard: DesignArtboard & { colors: C },\n): DesignBuilder<never, never, Extract<keyof C, string>>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, never>;\nexport function design(artboard: DesignArtboard): DesignBuilder<never, never, string> {\n return new DesignBuilder<never, never, string>(artboard);\n}\n\n/**\n * Resolve a layer's kind against a live element/config list — the mount-time\n * revalidation `Editor.TextField` runs (the token is a validated claim; the\n * mounted Root's design state is the authority). @internal\n */\nexport function layerKindOfConfig(el: AnyElementConfig): LayerKind | null {\n return elementLayerKind(el);\n}\n"],"names":["BoundLayerGuardContext","createContext","noopSubscribe","zeroVersion","useIsLayerBound","guard","useContext","version","useSyncExternalStore","useCallback","name","logger","createLogger","useCommandHistory","elements","setElements","artboardManager","onArtboardsChange","historyManagerRef","useRef","HybridHistoryManager","boundLayerGuard","assertLayerMutable","element","operation","bound","EditorError","historyState","setHistoryState","useState","updateHistoryState","manager","activeArtboardId","executeElementUpdate","oldElement","newElement","command","UpdateElementCommand","prevElements","s","artboardId","executeAddElement","insertAfterElementId","insertBeforeElementId","targetArtboardId","AddElementCommand","addedElement","insertIndex","e","newElements","i","GroupElement","childIndex","child","updatedGroup","id","executeRemoveElement","RemoveElementCommand","executeCreateArtboard","artboard","CreateArtboardCommand","executeDeleteArtboard","DeleteArtboardCommand","executeUpdateArtboard","oldProperties","newProperties","UpdateArtboardCommand","executeCommandBatch","commands","wrappedCommands","cmd","undo","redo","undoActiveArtboard","redoActiveArtboard","clearHistory","clearArtboardHistory","activeArtboardIdRef","useEffect","currentActiveId","executeReorderElement","draggedId","targetId","position","currentOrder","ReorderElementCommand","newOrder","elementMap","hasTextProperties","useElementProperties","selectedElement","onElementUpdate","fontSize","setFontSize","fontColor","setFontColor","fontFamily","setFontFamily","textAlign","setTextAlign","newFontSize","newFontColor","newFontFamily","newTextAlign","prev","updateFontSize","delta","newSize","MIN_FONT_SIZE","MAX_FONT_SIZE","updatedElement","setFontSizeValue","updateFontColor","newColor","span","updateFontFamily","oldFontFamily","FontAnalyzer","updateTextAlign","alignment","PRESERVE_SELECTION_ATTR","shouldPreserveSelection","target","path","classes","role","dataSlot","preserveSelectionProps","isInkDark","css","luma","inkLuminance","isInkLight","trimmed","hex","r","g","b","relLuma","m","lin","c","cs","DEFAULT_TEXT_INK","DEFAULT_TEXT_INK_ON_DARK","defaultTextColorForBackground","backgroundColor","ViewportContext","MIN_ZOOM","MAX_ZOOM","ZOOM_STEP","ViewportProvider","children","canvasRef","viewPadding","zoom","setZoom","panOffset","setPanOffset","isPanning","setIsPanning","userZoom","setUserZoom","resetUserView","zoomIn","zoomOut","zoomToFit","container","containerWidth","containerHeight","fitZoom","resetView","value","useMemo","jsx","useViewportContext","context","SelectionContext","SelectionProvider","selectedId","setSelectedId","multiSelection","setMultiSelection","activeChildElement","setActiveChildElement","hoveredElementId","setHoveredElementId","hideHandles","setHideHandles","handleSelectionChange","handleActiveChildChange","useSelectionContext","HistoryContext","HistoryProvider","memoizedValue","useHistoryContext","ToolStateContext","ToolStateProvider","expandedPanelType","setExpandedPanelType","isToolbarMenuOpen","setIsToolbarMenuOpen","textSelectionVersion","setTextSelectionVersion","isCanvasReady","setCanvasReady","isRotating","setIsRotating","useToolStateContext","ElementsContext","ElementsProvider","useElementsContext","CommandContext","CommandProvider","useCommandContext","EditorContext","EditorHandlesContext","useEditorHandles","InternalEditorProvider","canvasEditorRef","handlesRef","handles","viewport","selection","toolState","artboards","setArtboards","artboardVersion","setArtboardVersion","elementStore","setElementStore","ElementStore","action","prevStore","prevArray","nextArray","getElementById","clipboard","setClipboard","refreshArtboards","v","historyManager","canUndo","canRedo","canUndoActiveArtboard","canRedoActiveArtboard","handleElementUpdate","handleChildElementUpdate","updatedChild","elementForProperties","elementUpdateHandler","addElement","spec","elementType","opts","newId","activeArtboard","centerX","centerY","currentZoom","_a","placeholderImage","boxSize","src","newImageElement","ImageElement","loadedElement","w","h","scale","transformType","transformDef","getTransformById","baseFontSize","transformWidth","shapeType","shapeW","shapeH","ShapeElement","ARCH_DEFAULTS","WAVE_DEFAULTS","handleAddElement","handleCopyElements","elementsToCopy","selectedElements","clonedElements","el","handlePasteElements","pastedIds","offsetX","offsetY","clonedElement","originalElement","baseX","baseY","elementConfig","Constructor","pastedElement","insertAfterId","clearExpandedPanel","imageElement","handleDocumentPointerDown","activeElement","handleLoadWorkspace","loadedArtboards","loadedElements","EditorProvider","initialArtboardConfig","ArtboardManager","config","DEFAULT_ARTBOARD_COLOR","useEditor","recorded","recordContainedEditorError","error","isDevOrTest","getContainedEditorErrors","resetContainedEditorErrors","expectNoEditorErrors","summary","isEditorError","CANVAS_STATE_SCHEMA_VERSION","q","n","quantizeGeometry","serializeStateForServer","state","srcArtboards","elementsByArtboard","tag","idx","ab","serializeElement","srcArtboard","_b","type","serializeImageElement","_artboard","rest","td","hasMasks","fullWidth","fullHeight","visibleWidth","visibleHeight","serialized","isNeutralAdjustments","DEFAULT_ANCHOR","LAYER_TOKEN_MARKER","isLayerToken","mintLayerToken","kind","COLOR_TOKEN_MARKER","isColorToken","mintColorToken","normalizeRoleHex","color","resolveColor","elementLayerKind","hPart","a","vPart","anchorToCenter","anchor","elW","elH","artW","artH","padding","x","y","coverCrop","ar","artAR","fullW","cropWidth","fullH","cropHeight","DesignBuilder","roleName","raw","collision","options","builder","prefix","label","dims","imageUrl","text","textNames","imageNames","size","transformData","design","layerKindOfConfig"],"mappings":"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"}
|