@snowcone-app/canvas 0.1.10 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{CanvasStateV1-BmE5V6me.cjs → CanvasStateV1-C4hC1MCe.cjs} +5 -5
  2. package/dist/{CanvasStateV1-BmE5V6me.cjs.map → CanvasStateV1-C4hC1MCe.cjs.map} +1 -1
  3. package/dist/{CanvasStateV1-CD3Q94F4.js → CanvasStateV1-CJU_xYW5.js} +3 -3
  4. package/dist/{CanvasStateV1-CD3Q94F4.js.map → CanvasStateV1-CJU_xYW5.js.map} +1 -1
  5. package/dist/{HybridHistoryManager-BV6XV0nD.js → HybridHistoryManager-jBBnVim8.js} +54 -54
  6. package/dist/{HybridHistoryManager-BV6XV0nD.js.map → HybridHistoryManager-jBBnVim8.js.map} +1 -1
  7. package/dist/{ElementFactory-Ckv6sSev.js → ImportManager-Oqu2yB54.js} +595 -378
  8. package/dist/ImportManager-Oqu2yB54.js.map +1 -0
  9. package/dist/{ElementFactory-DEjwp-Wg.cjs → ImportManager-W1eWhfyM.cjs} +5 -5
  10. package/dist/ImportManager-W1eWhfyM.cjs.map +1 -0
  11. package/dist/ThemeContext-BMNQKl1c.cjs +2 -0
  12. package/dist/{ThemeContext-4mJ_y0Me.cjs.map → ThemeContext-BMNQKl1c.cjs.map} +1 -1
  13. package/dist/ThemeContext-wj-wSO7J.js +1158 -0
  14. package/dist/{ThemeContext-H0Z-MqqR.js.map → ThemeContext-wj-wSO7J.js.map} +1 -1
  15. package/dist/advanced.js +5 -32
  16. package/dist/advanced.js.map +1 -1
  17. package/dist/advanced.mjs +588 -15069
  18. package/dist/advanced.mjs.map +1 -1
  19. package/dist/components/embed/KitLayout.d.ts +22 -0
  20. package/dist/components/embed/UndoRedoControls.d.ts +3 -0
  21. package/dist/compose-Dqh2f8tS.js +22222 -0
  22. package/dist/compose-Dqh2f8tS.js.map +1 -0
  23. package/dist/compose-HDJp4Z_d.cjs +60 -0
  24. package/dist/compose-HDJp4Z_d.cjs.map +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +600 -516
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/internals.js +1 -1
  30. package/dist/internals.js.map +1 -1
  31. package/dist/internals.mjs +101 -102
  32. package/dist/internals.mjs.map +1 -1
  33. package/dist/style.css.d.ts +4 -0
  34. package/dist/testing.js +1 -1
  35. package/dist/testing.mjs +11 -11
  36. package/package.json +8 -4
  37. package/dist/ElementFactory-Ckv6sSev.js.map +0 -1
  38. package/dist/ElementFactory-DEjwp-Wg.cjs.map +0 -1
  39. package/dist/ImportManager-64OYjELO.js +0 -222
  40. package/dist/ImportManager-64OYjELO.js.map +0 -1
  41. package/dist/ImportManager-wSzrR-5a.cjs +0 -2
  42. package/dist/ImportManager-wSzrR-5a.cjs.map +0 -1
  43. package/dist/ThemeContext-4mJ_y0Me.cjs +0 -2
  44. package/dist/ThemeContext-H0Z-MqqR.js +0 -1077
  45. package/dist/compose-DHBRwi_A.cjs +0 -33
  46. package/dist/compose-DHBRwi_A.cjs.map +0 -1
  47. package/dist/compose-DIPiisIw.js +0 -7690
  48. package/dist/compose-DIPiisIw.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ThemeContext-H0Z-MqqR.js","sources":["../src/hooks/useCommandHistory.ts","../src/hooks/useElementProperties.ts","../src/utils/selectionPreservation.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/contexts/ThemeContext.tsx"],"sourcesContent":["/**\n * useCommandHistory - React hook for hybrid undo/redo functionality\n * Now supports artboard-level and element-level operations\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react';\nimport { createLogger } from '../utils/logger.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 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 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]\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 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]\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 * 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 { 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 * 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 handleAddElement: (\n elementType: string,\n opts?: { shapeType?: ShapeType }\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 // 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/** 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 // --- 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 handleAddElement = useCallback(\n async (elementType: string, opts?: { shapeType?: ShapeType }) => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n alert('Please create or select an artboard first');\n return;\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 are created with a 1x1 transparent placeholder.\n // The caller 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 newImageElement = new ImageElement({\n id: newId,\n x: centerX,\n y: centerY,\n imageUrl: placeholderImage,\n transformData: {\n type: 'image',\n width: 200 / currentZoom,\n height: 200 / currentZoom,\n cropX: 0,\n cropY: 0,\n cropWidth: 1,\n cropHeight: 1,\n flipHorizontal: false,\n flipVertical: false,\n borderRadius: 0,\n },\n });\n executeAddElement(newImageElement, activeArtboardId);\n selection.setSelectedId(newImageElement.id);\n return;\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 return;\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 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\n newElement = new transformDef.Component({\n id: newId,\n text: 'New Text',\n x: centerX,\n y: centerY,\n fontSize: baseFontSize,\n fontFamily: 'Poppins',\n color: '#000000',\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 },\n [artboardManager, executeAddElement, selection, viewport.zoom, canvasEditorRef]\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 selection.setSelectedId(null);\n selection.setMultiSelection([]);\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 const value: EditorContextValue = {\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 handleAddElement,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\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\n return (\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 );\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 * 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 * Theme Context - Provides theme management for the canvas editor.\n *\n * Supports light, dark, and auto modes plus named color themes (axis, ocean, sunset).\n * Auto mode respects the system preference (`prefers-color-scheme`).\n *\n * When embedding in a host app that already manages themes, use `passive` mode\n * on `ThemeProvider` or set `inheritTheme` on `SnowconeCanvas` to prevent\n * the canvas from overriding the host app's theme on `document.documentElement`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';\n\nexport type Theme = 'light' | 'dark' | 'axis' | 'ocean' | 'sunset' | 'auto';\ntype ResolvedTheme =\n | 'light'\n | 'dark'\n | 'axis-light'\n | 'axis-dark'\n | 'ocean-light'\n | 'ocean-dark'\n | 'sunset-light'\n | 'sunset-dark';\n\ninterface ThemeContextValue {\n theme: Theme;\n resolvedTheme: ResolvedTheme;\n setTheme: (theme: Theme) => void;\n isDark: boolean;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\ninterface ThemeProviderProps {\n children: ReactNode;\n defaultTheme?: Theme;\n /**\n * When true, the provider reads theme from the document but doesn't modify it.\n * Use this when embedding in an app that already manages themes.\n * This prevents the canvas from overriding the parent app's theme.\n */\n passive?: boolean;\n}\n\n/**\n * Read the current theme from document without modifying it\n */\nfunction readThemeFromDocument(): { theme: Theme; resolvedTheme: ResolvedTheme; isDark: boolean } {\n if (typeof window === 'undefined') {\n return { theme: 'light', resolvedTheme: 'light', isDark: false };\n }\n\n const isDark = document.documentElement.classList.contains('dark');\n const dataTheme = document.documentElement.getAttribute('data-theme');\n\n // Determine resolved theme from document state\n let resolvedTheme: ResolvedTheme = isDark ? 'dark' : 'light';\n if (dataTheme) {\n // Check if it's a valid resolved theme\n const validResolvedThemes: ResolvedTheme[] = [\n 'light', 'dark', 'axis-light', 'axis-dark',\n 'ocean-light', 'ocean-dark', 'sunset-light', 'sunset-dark'\n ];\n if (validResolvedThemes.includes(dataTheme as ResolvedTheme)) {\n resolvedTheme = dataTheme as ResolvedTheme;\n }\n }\n\n // Infer base theme from resolved theme\n let theme: Theme = isDark ? 'dark' : 'light';\n if (resolvedTheme.startsWith('axis')) theme = 'axis';\n else if (resolvedTheme.startsWith('ocean')) theme = 'ocean';\n else if (resolvedTheme.startsWith('sunset')) theme = 'sunset';\n\n return { theme, resolvedTheme, isDark };\n}\n\n/**\n * Provides theme state to all descendant components.\n *\n * In **active** mode (default), applies theme to `document.documentElement` and\n * persists the choice to `localStorage`. In **passive** mode, reads the theme\n * from the document without modifying it -- use this when embedding inside a\n * host app that already manages themes.\n *\n * @example\n * ```tsx\n * // Active mode (standalone)\n * <ThemeProvider defaultTheme=\"auto\">\n * <App />\n * </ThemeProvider>\n *\n * // Passive mode (embedded in host app)\n * <ThemeProvider passive>\n * <SnowconeCanvasInner />\n * </ThemeProvider>\n * ```\n */\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({ children, defaultTheme = 'light', passive = false }) => {\n // Get initial theme from localStorage or use default (only in active mode)\n const getInitialTheme = (): Theme => {\n if (typeof window === 'undefined') return defaultTheme;\n\n if (passive) {\n // In passive mode, read from document\n return readThemeFromDocument().theme;\n }\n\n const stored = localStorage.getItem('snowcone-theme') as Theme | null;\n return stored || defaultTheme;\n };\n\n const getInitialResolvedTheme = (): ResolvedTheme => {\n if (typeof window === 'undefined') return 'light';\n if (passive) {\n return readThemeFromDocument().resolvedTheme;\n }\n return 'light';\n };\n\n const [theme, setThemeState] = useState<Theme>(getInitialTheme);\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(getInitialResolvedTheme);\n\n // In passive mode, observe document changes to stay in sync\n useEffect(() => {\n if (!passive || typeof window === 'undefined') return;\n\n // Read initial state\n const { resolvedTheme: docResolved, theme: docTheme } = readThemeFromDocument();\n setResolvedTheme(docResolved);\n setThemeState(docTheme);\n\n // Observe changes to document.documentElement class and attributes\n const observer = new MutationObserver(() => {\n const { resolvedTheme: newResolved, theme: newTheme } = readThemeFromDocument();\n setResolvedTheme(newResolved);\n setThemeState(newTheme);\n });\n\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme'],\n });\n\n return () => observer.disconnect();\n }, [passive]);\n\n // Listen for system theme changes (for auto mode and colored themes) - only in active mode\n useEffect(() => {\n if (passive) return; // Skip in passive mode\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n const handleChange = (e: MediaQueryListEvent) => {\n let newResolved: ResolvedTheme;\n\n if (theme === 'auto') {\n // Auto mode uses default (blue) theme\n newResolved = e.matches ? 'dark' : 'light';\n } else if (theme === 'light' || theme === 'dark') {\n // Default theme doesn't change with system preference\n return;\n } else {\n // Colored themes follow system preference\n newResolved = e.matches ? (`${theme}-dark` as ResolvedTheme) : (`${theme}-light` as ResolvedTheme);\n }\n\n setResolvedTheme(newResolved);\n document.documentElement.setAttribute('data-theme', newResolved);\n\n // HeroUI v3 requires both data-theme attribute AND dark class\n const isDarkVariant = newResolved === 'dark' || newResolved.endsWith('-dark');\n if (isDarkVariant) {\n document.documentElement.classList.add('dark');\n document.body.classList.add('dark'); // Also add to body for portal rendering\n } else {\n document.documentElement.classList.remove('dark');\n document.body.classList.remove('dark');\n }\n };\n\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, [theme, passive]);\n\n // Update resolved theme and apply to DOM - only in active mode\n useEffect(() => {\n if (passive) return; // Skip in passive mode - don't modify document\n if (typeof window === 'undefined') return;\n\n let resolved: ResolvedTheme;\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n\n if (theme === 'auto') {\n // Use system preference with default (blue) theme\n resolved = prefersDark ? 'dark' : 'light';\n } else if (theme === 'light' || theme === 'dark') {\n // Default HeroUI theme (blue accent)\n resolved = theme;\n } else {\n // Colored themes: axis, ocean, sunset - resolve to light/dark variant\n resolved = prefersDark ? (`${theme}-dark` as ResolvedTheme) : (`${theme}-light` as ResolvedTheme);\n }\n\n setResolvedTheme(resolved);\n document.documentElement.setAttribute('data-theme', resolved);\n\n // HeroUI v3 requires both data-theme attribute AND dark class for dark themes\n const isDarkVariant = resolved === 'dark' || resolved.endsWith('-dark');\n if (isDarkVariant) {\n document.documentElement.classList.add('dark');\n document.body.classList.add('dark'); // Also add to body for portal rendering\n } else {\n document.documentElement.classList.remove('dark');\n document.body.classList.remove('dark');\n }\n\n // Persist to localStorage\n localStorage.setItem('snowcone-theme', theme);\n }, [theme, passive]);\n\n const setTheme = (newTheme: Theme) => {\n if (passive) return; // In passive mode, don't allow setting theme\n setThemeState(newTheme);\n };\n\n const isDark = resolvedTheme === 'dark' || resolvedTheme.endsWith('-dark');\n\n const value: ThemeContextValue = {\n theme,\n resolvedTheme,\n setTheme,\n isDark,\n };\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n};\n\n/**\n * Hook to access theme context\n *\n * @example\n * const { theme, setTheme, isDark } = useTheme();\n *\n * // Change theme\n * setTheme('dark');\n *\n * // Check current theme\n * if (isDark) { ... }\n */\nexport const useTheme = (): ThemeContextValue => {\n const context = useContext(ThemeContext);\n if (!context) {\n throw new Error('useTheme must be used within ThemeProvider');\n }\n return context;\n};\n"],"names":["logger","createLogger","useCommandHistory","elements","setElements","artboardManager","onArtboardsChange","historyManagerRef","useRef","HybridHistoryManager","historyState","setHistoryState","useState","updateHistoryState","useCallback","manager","activeArtboardId","executeElementUpdate","oldElement","newElement","command","UpdateElementCommand","element","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","ViewportContext","createContext","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","useContext","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","InternalEditorProvider","canvasEditorRef","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","c","elementForProperties","elementUpdateHandler","handleAddElement","elementType","opts","newId","activeArtboard","centerX","centerY","currentZoom","_a","placeholderImage","newImageElement","ImageElement","transformType","transformDef","getTransformById","baseFontSize","transformWidth","shapeType","shapeW","shapeH","ShapeElement","ARCH_DEFAULTS","WAVE_DEFAULTS","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","ThemeContext","readThemeFromDocument","isDark","dataTheme","resolvedTheme","theme","ThemeProvider","defaultTheme","passive","getInitialTheme","getInitialResolvedTheme","setThemeState","setResolvedTheme","docResolved","docTheme","observer","newResolved","newTheme","mediaQuery","handleChange","resolved","prefersDark","setTheme","useTheme"],"mappings":"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"}
1
+ {"version":3,"file":"ThemeContext-wj-wSO7J.js","sources":["../src/hooks/useCommandHistory.ts","../src/hooks/useElementProperties.ts","../src/utils/selectionPreservation.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/contexts/ThemeContext.tsx"],"sourcesContent":["/**\n * useCommandHistory - React hook for hybrid undo/redo functionality\n * Now supports artboard-level and element-level operations\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react';\nimport { createLogger } from '../utils/logger.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 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 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]\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 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]\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 * 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 { 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 * 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 handleAddElement: (\n elementType: string,\n opts?: { shapeType?: ShapeType }\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 // 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/** 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 // --- 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 handleAddElement = useCallback(\n async (elementType: string, opts?: { shapeType?: ShapeType }) => {\n const activeArtboardId = artboardManager.getActiveArtboardId();\n if (!activeArtboardId) {\n alert('Please create or select an artboard first');\n return;\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 are created with a 1x1 transparent placeholder.\n // The caller 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 newImageElement = new ImageElement({\n id: newId,\n x: centerX,\n y: centerY,\n imageUrl: placeholderImage,\n transformData: {\n type: 'image',\n width: 200 / currentZoom,\n height: 200 / currentZoom,\n cropX: 0,\n cropY: 0,\n cropWidth: 1,\n cropHeight: 1,\n flipHorizontal: false,\n flipVertical: false,\n borderRadius: 0,\n },\n });\n executeAddElement(newImageElement, activeArtboardId);\n selection.setSelectedId(newImageElement.id);\n return;\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 return;\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 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\n newElement = new transformDef.Component({\n id: newId,\n text: 'New Text',\n x: centerX,\n y: centerY,\n fontSize: baseFontSize,\n fontFamily: 'Poppins',\n color: '#000000',\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 },\n [artboardManager, executeAddElement, selection, viewport.zoom, canvasEditorRef]\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 selection.setSelectedId(null);\n selection.setMultiSelection([]);\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 handleAddElement,\n handleLoadWorkspace,\n handleCopyElements,\n handlePasteElements,\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, handleAddElement, handleLoadWorkspace,\n handleCopyElements, handlePasteElements,\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 return (\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 );\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 * 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 * Theme Context - Provides theme management for the canvas editor.\n *\n * Supports light, dark, and auto modes plus named color themes (axis, ocean, sunset).\n * Auto mode respects the system preference (`prefers-color-scheme`).\n *\n * When embedding in a host app that already manages themes, use `passive` mode\n * on `ThemeProvider` or set `inheritTheme` on `SnowconeCanvas` to prevent\n * the canvas from overriding the host app's theme on `document.documentElement`.\n *\n * @module\n */\n\nimport React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';\n\nexport type Theme = 'light' | 'dark' | 'axis' | 'ocean' | 'sunset' | 'auto';\ntype ResolvedTheme =\n | 'light'\n | 'dark'\n | 'axis-light'\n | 'axis-dark'\n | 'ocean-light'\n | 'ocean-dark'\n | 'sunset-light'\n | 'sunset-dark';\n\ninterface ThemeContextValue {\n theme: Theme;\n resolvedTheme: ResolvedTheme;\n setTheme: (theme: Theme) => void;\n isDark: boolean;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\ninterface ThemeProviderProps {\n children: ReactNode;\n defaultTheme?: Theme;\n /**\n * When true, the provider reads theme from the document but doesn't modify it.\n * Use this when embedding in an app that already manages themes.\n * This prevents the canvas from overriding the parent app's theme.\n */\n passive?: boolean;\n}\n\n/**\n * Read the current theme from document without modifying it\n */\nfunction readThemeFromDocument(): { theme: Theme; resolvedTheme: ResolvedTheme; isDark: boolean } {\n if (typeof window === 'undefined') {\n return { theme: 'light', resolvedTheme: 'light', isDark: false };\n }\n\n const isDark = document.documentElement.classList.contains('dark');\n const dataTheme = document.documentElement.getAttribute('data-theme');\n\n // Determine resolved theme from document state\n let resolvedTheme: ResolvedTheme = isDark ? 'dark' : 'light';\n if (dataTheme) {\n // Check if it's a valid resolved theme\n const validResolvedThemes: ResolvedTheme[] = [\n 'light', 'dark', 'axis-light', 'axis-dark',\n 'ocean-light', 'ocean-dark', 'sunset-light', 'sunset-dark'\n ];\n if (validResolvedThemes.includes(dataTheme as ResolvedTheme)) {\n resolvedTheme = dataTheme as ResolvedTheme;\n }\n }\n\n // Infer base theme from resolved theme\n let theme: Theme = isDark ? 'dark' : 'light';\n if (resolvedTheme.startsWith('axis')) theme = 'axis';\n else if (resolvedTheme.startsWith('ocean')) theme = 'ocean';\n else if (resolvedTheme.startsWith('sunset')) theme = 'sunset';\n\n return { theme, resolvedTheme, isDark };\n}\n\n/**\n * Provides theme state to all descendant components.\n *\n * In **active** mode (default), applies theme to `document.documentElement` and\n * persists the choice to `localStorage`. In **passive** mode, reads the theme\n * from the document without modifying it -- use this when embedding inside a\n * host app that already manages themes.\n *\n * @example\n * ```tsx\n * // Active mode (standalone)\n * <ThemeProvider defaultTheme=\"auto\">\n * <App />\n * </ThemeProvider>\n *\n * // Passive mode (embedded in host app)\n * <ThemeProvider passive>\n * <SnowconeCanvasInner />\n * </ThemeProvider>\n * ```\n */\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({ children, defaultTheme = 'light', passive = false }) => {\n // Get initial theme from localStorage or use default (only in active mode)\n const getInitialTheme = (): Theme => {\n if (typeof window === 'undefined') return defaultTheme;\n\n if (passive) {\n // In passive mode, read from document\n return readThemeFromDocument().theme;\n }\n\n const stored = localStorage.getItem('snowcone-theme') as Theme | null;\n return stored || defaultTheme;\n };\n\n const getInitialResolvedTheme = (): ResolvedTheme => {\n if (typeof window === 'undefined') return 'light';\n if (passive) {\n return readThemeFromDocument().resolvedTheme;\n }\n return 'light';\n };\n\n const [theme, setThemeState] = useState<Theme>(getInitialTheme);\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(getInitialResolvedTheme);\n\n // In passive mode, observe document changes to stay in sync\n useEffect(() => {\n if (!passive || typeof window === 'undefined') return;\n\n // Read initial state\n const { resolvedTheme: docResolved, theme: docTheme } = readThemeFromDocument();\n setResolvedTheme(docResolved);\n setThemeState(docTheme);\n\n // Observe changes to document.documentElement class and attributes\n const observer = new MutationObserver(() => {\n const { resolvedTheme: newResolved, theme: newTheme } = readThemeFromDocument();\n setResolvedTheme(newResolved);\n setThemeState(newTheme);\n });\n\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['class', 'data-theme'],\n });\n\n return () => observer.disconnect();\n }, [passive]);\n\n // Listen for system theme changes (for auto mode and colored themes) - only in active mode\n useEffect(() => {\n if (passive) return; // Skip in passive mode\n if (typeof window === 'undefined') return;\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n\n const handleChange = (e: MediaQueryListEvent) => {\n let newResolved: ResolvedTheme;\n\n if (theme === 'auto') {\n // Auto mode uses default (blue) theme\n newResolved = e.matches ? 'dark' : 'light';\n } else if (theme === 'light' || theme === 'dark') {\n // Default theme doesn't change with system preference\n return;\n } else {\n // Colored themes follow system preference\n newResolved = e.matches ? (`${theme}-dark` as ResolvedTheme) : (`${theme}-light` as ResolvedTheme);\n }\n\n setResolvedTheme(newResolved);\n document.documentElement.setAttribute('data-theme', newResolved);\n\n // HeroUI v3 requires both data-theme attribute AND dark class\n const isDarkVariant = newResolved === 'dark' || newResolved.endsWith('-dark');\n if (isDarkVariant) {\n document.documentElement.classList.add('dark');\n document.body.classList.add('dark'); // Also add to body for portal rendering\n } else {\n document.documentElement.classList.remove('dark');\n document.body.classList.remove('dark');\n }\n };\n\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, [theme, passive]);\n\n // Update resolved theme and apply to DOM - only in active mode\n useEffect(() => {\n if (passive) return; // Skip in passive mode - don't modify document\n if (typeof window === 'undefined') return;\n\n let resolved: ResolvedTheme;\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n\n if (theme === 'auto') {\n // Use system preference with default (blue) theme\n resolved = prefersDark ? 'dark' : 'light';\n } else if (theme === 'light' || theme === 'dark') {\n // Default HeroUI theme (blue accent)\n resolved = theme;\n } else {\n // Colored themes: axis, ocean, sunset - resolve to light/dark variant\n resolved = prefersDark ? (`${theme}-dark` as ResolvedTheme) : (`${theme}-light` as ResolvedTheme);\n }\n\n setResolvedTheme(resolved);\n document.documentElement.setAttribute('data-theme', resolved);\n\n // HeroUI v3 requires both data-theme attribute AND dark class for dark themes\n const isDarkVariant = resolved === 'dark' || resolved.endsWith('-dark');\n if (isDarkVariant) {\n document.documentElement.classList.add('dark');\n document.body.classList.add('dark'); // Also add to body for portal rendering\n } else {\n document.documentElement.classList.remove('dark');\n document.body.classList.remove('dark');\n }\n\n // Persist to localStorage\n localStorage.setItem('snowcone-theme', theme);\n }, [theme, passive]);\n\n const setTheme = (newTheme: Theme) => {\n if (passive) return; // In passive mode, don't allow setting theme\n setThemeState(newTheme);\n };\n\n const isDark = resolvedTheme === 'dark' || resolvedTheme.endsWith('-dark');\n\n const value: ThemeContextValue = {\n theme,\n resolvedTheme,\n setTheme,\n isDark,\n };\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n};\n\n/**\n * Hook to access theme context\n *\n * @example\n * const { theme, setTheme, isDark } = useTheme();\n *\n * // Change theme\n * setTheme('dark');\n *\n * // Check current theme\n * if (isDark) { ... }\n */\nexport const useTheme = (): ThemeContextValue => {\n const context = useContext(ThemeContext);\n if (!context) {\n throw new Error('useTheme must be used within ThemeProvider');\n }\n return context;\n};\n"],"names":["logger","createLogger","useCommandHistory","elements","setElements","artboardManager","onArtboardsChange","historyManagerRef","useRef","HybridHistoryManager","historyState","setHistoryState","useState","updateHistoryState","useCallback","manager","activeArtboardId","executeElementUpdate","oldElement","newElement","command","UpdateElementCommand","element","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","ViewportContext","createContext","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","useContext","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","InternalEditorProvider","canvasEditorRef","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","c","elementForProperties","elementUpdateHandler","handleAddElement","elementType","opts","newId","activeArtboard","centerX","centerY","currentZoom","_a","placeholderImage","newImageElement","ImageElement","transformType","transformDef","getTransformById","baseFontSize","transformWidth","shapeType","shapeW","shapeH","ShapeElement","ARCH_DEFAULTS","WAVE_DEFAULTS","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","ThemeContext","readThemeFromDocument","isDark","dataTheme","resolvedTheme","theme","ThemeProvider","defaultTheme","passive","getInitialTheme","getInitialResolvedTheme","setThemeState","setResolvedTheme","docResolved","docTheme","observer","newResolved","newTheme","mediaQuery","handleChange","resolved","prefersDark","setTheme","useTheme"],"mappings":"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"}