@pixldocs/canvas-renderer 0.3.3

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/shims/app-api.ts","../../../src/types/editor.ts","../../../src/lib/textMeasurement.ts","../../../src/lib/layoutEngine.ts","../../../src/store/editorStore.ts","../../../src/store/fabricCanvasRegistry.ts","../../../src/lib/googleFonts.ts","../../../src/lib/fontLoader.ts","../../../src/lib/fabricUtils.ts","../../../src/lib/canvasImageLoader.ts","../../../src/lib/canvaMaskedImage.ts","../../../src/lib/snapGuides.ts","../../../src/lib/shapeGeometry.ts","../../../src/lib/fabricObjectCreators.ts","../../../shared/smart-elements/renderers/qrcode.ts","../../../shared/smart-elements/renderers/rating.ts","../../../shared/smart-elements/renderers/progress.ts","../../../shared/smart-elements/utils.ts","../../../shared/smart-elements/renderers/table.ts","../../../shared/smart-elements/renderers/avatar.ts","../../../shared/smart-elements/renderers/badge.ts","../../../shared/smart-elements/registry.ts","../../../src/lib/gradientUtils.ts","../../../src/components/editor/PageCanvas.tsx","../../../src/components/PreviewCanvas.tsx","../../../src/lib/inferFormSchemaFromTemplate.ts","../../../src/lib/formSchema.ts","../../../src/lib/applyFormDataToConfig.ts","../../../src/lib/layoutWithContentBounds.ts","../src/data-resolver.ts","../src/PixldocsPreview.tsx","../src/font-loader.ts","../src/renderer.ts","../src/theme.ts"],"sourcesContent":["export let API_URL = '';\n\nexport function setPackageApiUrl(imageProxyUrl?: string): void {\n if (!imageProxyUrl) {\n API_URL = '';\n return;\n }\n\n API_URL = imageProxyUrl.replace(/\\/image-proxy(?:\\?.*)?$/, '');\n}\n\n// --- Type stubs consumed by transitive imports (formSchema.ts, applyFormDataToConfig.ts) ---\n\n/** Form rendering presets */\nexport type FormRenderPreset = 'default' | 'compact' | 'cards' | 'minimal' | 'biodata';\n\n/** Form definition (schema layer) */\nexport type FormSchema = {\n dynamicFields?: unknown[];\n fieldGroups?: unknown[];\n repeatableSections?: { nodeId: string; label: string; minEntries?: number; maxEntries?: number }[];\n defaultData?: Record<string, unknown> | null;\n formRenderPreset?: FormRenderPreset;\n} | null;\n\n/** Mapping entry between a form field and a template element */\nexport interface FormTemplateMappingEntry {\n field_key: string;\n element_id: string;\n property?: string;\n target_property?: string;\n}\n","// ============================================\n// NESTED CHILDREN SCHEMA v2.2\n// Supports true tree structure with groups containing children\n// Project settings separated from page settings\n// Centralized Dynamic Fields system\n// ============================================\n\nexport type ElementType = 'text' | 'shape' | 'image' | 'line';\nexport type NodeType = ElementType | 'group';\n\nexport type ShapeType = \n | 'rectangle' \n | 'circle' \n | 'rounded-rect' \n | 'triangle' \n | 'diamond' \n | 'pentagon' \n | 'hexagon' \n | 'octagon'\n | 'star' \n | 'heart' \n | 'arrow'\n | 'parallelogram'\n | 'trapezoid'\n | 'cross'\n | 'ring'\n | 'badge';\n\nexport type LineType = 'solid' | 'dashed' | 'dotted';\n\n// ============================================\n// CSS-LIKE LAYOUT (canvas units, not px)\n// Padding, margin, position, display (flex/grid), auto width/height\n// ============================================\n\n/** Spacing in canvas units. Number = all sides; object = per side */\nexport type SpacingValue = number | { top?: number; right?: number; bottom?: number; left?: number };\n\n/** Resolve SpacingValue to { top, right, bottom, left } in canvas units */\nexport function resolveSpacing(v: SpacingValue | undefined): { top: number; right: number; bottom: number; left: number } {\n if (v === undefined) return { top: 0, right: 0, bottom: 0, left: 0 };\n if (typeof v === 'number') return { top: v, right: v, bottom: v, left: v };\n return {\n top: v.top ?? 0,\n right: v.right ?? 0,\n bottom: v.bottom ?? 0,\n left: v.left ?? 0,\n };\n}\n\n/** Container display: block (absolute flow), flex, or grid */\nexport type Display = 'block' | 'flex' | 'grid';\n\n/**\n * Position type (CSS-like). Default is static.\n * @see https://www.w3schools.com/css/css_positioning.asp\n * - static: default; normal flow, not affected by top/left (layout engine computes position)\n * - relative: positioned relative to normal position; top/left adjust\n * - absolute: out of flow; positioned relative to nearest positioned ancestor\n * - fixed: relative to viewport/page\n */\nexport type PositionType = 'static' | 'relative' | 'absolute' | 'fixed';\n\n/** Width/height: CSS-like. auto = content-based, initial = default (fallback), inherit = from parent. */\nexport type SizeValue = number | 'auto' | 'initial' | 'inherit';\n\n/** @deprecated Use SizeValue. Kept for backward compatibility. */\nexport type AutoSize = SizeValue;\n\n/** Flexbox alignment (main axis) */\nexport type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';\n\n/** Flexbox alignment (cross axis) */\nexport type AlignItems = 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';\n\n/** Grid template: e.g. \"1fr 1fr\", \"100 200 100\", \"auto 1fr\" */\nexport type GridTemplate = string;\n\n/** Check if width/height is content-based (auto) */\nexport function isAutoSize(v: SizeValue | undefined): v is 'auto' {\n return v === 'auto';\n}\n\n/**\n * Resolve width/height to a number.\n * - number: use as-is\n * - 'auto': use contentSize (content-based)\n * - 'initial': use fallback (default)\n * - 'inherit': use parentSize if provided, else fallback\n */\nexport function resolveSize(\n value: SizeValue | undefined,\n contentSize: number,\n fallback: number,\n parentSize?: number\n): number {\n if (value === undefined) return fallback;\n if (typeof value === 'number') return value;\n if (value === 'auto') return contentSize;\n if (value === 'initial') return fallback;\n if (value === 'inherit') return parentSize ?? fallback;\n return fallback;\n}\n\n// Position helper type (used by editorStore)\nexport interface Position {\n x: number;\n y: number;\n}\n\n// Origin types for transform anchor\nexport type OriginX = 'left' | 'center' | 'right';\nexport type OriginY = 'top' | 'center' | 'bottom';\n\n// ============================================\n// GRADIENT SUPPORT\n// ============================================\n\nexport interface GradientStop {\n color: string; // hex color\n offset: number; // 0–1\n}\n\nexport interface GradientConfig {\n type: 'linear' | 'radial' | 'conic';\n /** Angle in degrees (0–360) for linear gradients. 0 = left→right, 90 = top→bottom */\n angle?: number;\n /** Color stops (2–5) */\n stops: GradientStop[];\n /** Center X for radial (0–1, default 0.5) */\n cx?: number;\n /** Center Y for radial (0–1, default 0.5) */\n cy?: number;\n /** Radius for radial (0–1, default 0.5) */\n r?: number;\n}\n\n/** Check if a fill value is a gradient config or a solid color string */\nexport function isGradientConfig(value: any): value is GradientConfig {\n return value && typeof value === 'object' && 'type' in value && 'stops' in value && Array.isArray(value.stops);\n}\n\n/** Create a default linear gradient */\nexport function createDefaultGradient(color1 = '#3b82f6', color2 = '#8b5cf6'): GradientConfig {\n return {\n type: 'linear',\n angle: 90,\n stops: [\n { color: color1, offset: 0 },\n { color: color2, offset: 1 },\n ],\n };\n}\n\n// ============================================\n// BASE CANVAS ELEMENT - Fabric.js native properties\n// ============================================\nexport interface PercentageBarConfig {\n value?: number;\n max?: number;\n showLabel?: boolean;\n label?: string;\n}\n\nexport interface CanvasElement {\n // === METADATA ===\n id: string;\n type: ElementType; // 'text' | 'shape' | 'image' | 'line' (NOT 'group')\n name: string;\n \n \n // Type-specific metadata\n shapeType?: ShapeType;\n lineType?: LineType;\n\n // === FABRIC.JS NATIVE PROPERTIES ===\n left: number;\n top: number;\n width: number;\n height: number;\n scaleX: number;\n scaleY: number;\n angle: number;\n skewX: number;\n skewY: number;\n flipX: boolean;\n flipY: boolean;\n originX: OriginX;\n originY: OriginY;\n \n // === RAW TRANSFORM MATRIX (optional, for precise transforms) ===\n transformMatrix?: [number, number, number, number, number, number];\n opacity: number;\n fill: string;\n fillGradient?: GradientConfig;\n stroke: string;\n strokeGradient?: GradientConfig;\n strokeWidth: number;\n strokeDashArray?: number[];\n visible: boolean;\n selectable: boolean;\n evented: boolean;\n \n // === TEXT-SPECIFIC ===\n text?: string;\n fontSize?: number;\n fontFamily?: string;\n fontWeight?: number | string;\n fontStyle?: 'normal' | 'italic' | 'oblique';\n textAlign?: 'left' | 'center' | 'right' | 'justify';\n lineHeight?: number;\n charSpacing?: number;\n letterSpacing?: number;\n underline?: boolean;\n linethrough?: boolean;\n \n // === SHAPE-SPECIFIC ===\n rx?: number;\n ry?: number;\n /** Per-corner border radius for rounded-rect */\n rxTL?: number;\n rxTR?: number;\n rxBR?: number;\n rxBL?: number;\n /** Per-corner border radius for triangle (top, bottom-right, bottom-left) */\n triRTop?: number;\n triRBR?: number;\n triRBL?: number;\n \n // === IMAGE-SPECIFIC ===\n src?: string;\n imageUrl?: string;\n clipShape?: 'none' | 'rectangle' | 'rounded' | 'circle';\n imageFit?: 'cover' | 'contain' | 'fill' | 'none';\n maintainResolution?: boolean;\n useCropHandles?: boolean;\n imageNaturalWidth?: number;\n imageNaturalHeight?: number;\n sourceFormat?: 'svg' | 'png';\n /** Map of original SVG color → replacement color for recoloring */\n svgColorMap?: Record<string, string>;\n /** Optional icon-scoped palette used to constrain SVG color editor swatches */\n svgSourceColors?: string[];\n cropPanX?: number;\n cropPanY?: number;\n cropZoom?: number;\n \n // === ALTERNATE PROPERTY NAMES (AI responses may use these) ===\n position?: { x: number; y: number };\n size?: { width: number; height: number };\n rotation?: number;\n content?: string;\n locked?: boolean;\n style?: {\n fill?: string;\n stroke?: string;\n strokeWidth?: number;\n opacity?: number;\n fontSize?: number;\n fontFamily?: string;\n fontWeight?: number | string;\n fontStyle?: string;\n textAlign?: string;\n lineHeight?: number;\n letterSpacing?: number;\n borderRadius?: number;\n strokeDasharray?: string;\n imageFrameShape?: string;\n imageFit?: string;\n textDecoration?: string;\n };\n \n // === LAYOUT ORDER (flex/grid) ===\n order?: number;\n\n /** When set, this element is a repeatable section */\n repeatableSection?: { label: string };\n\n /** When true, this element is copied onto new pages */\n staticOnNewPage?: boolean;\n\n // === SPECIAL ELEMENT PROPERTIES ===\n textMode?: 'plain' | 'bullets' | 'link';\n dynamicHeight?: boolean;\n linkConfig?: LinkConfig;\n bulletConfig?: BulletConfig;\n bulletItems?: string[];\n\n // === BACKWARD COMPATIBILITY (old schema) ===\n isDynamic?: boolean;\n fieldName?: string;\n groupId?: string;\n validation?: any;\n repeatable?: any;\n percentageBar?: PercentageBarConfig;\n \n // === SMART ELEMENT ===\n /** When set, this element is a smart element (QR code, rating, progress bar, etc.) */\n smartElementType?: import('@/lib/smartElements').SmartElementType;\n /** Smart element-specific properties (value, colors, etc.) */\n smartProps?: Record<string, any>;\n \n // === TEXT CASE TRANSFORM ===\n textCase?: 'none' | 'uppercase' | 'lowercase' | 'capitalize';\n \n // === TEXT OVERFLOW & LAYOUT BEHAVIOR ===\n overflowPolicy?: 'auto-shrink' | 'grow-and-push' | 'max-lines-ellipsis';\n maxLines?: number;\n minFontSize?: number;\n \n // === FABRIC TEXTBOX SETTINGS ===\n wordWrap?: 'break-word' | 'keep-word' | 'no-wrap';\n splitByGrapheme?: boolean;\n}\n\n// ============================================\n// GROUP NODE - Container with children\n// ============================================\n\n/** Native Fabric-style group: position and visibility only; size comes from Fabric (content). */\nexport interface GroupNode {\n id: string;\n type: 'group';\n name: string;\n children: CanvasNode[];\n collapsed?: boolean;\n locked?: boolean;\n visible?: boolean;\n left?: number;\n top?: number;\n layoutMode?: GroupLayoutMode;\n stackSpacing?: number;\n backgroundColor?: string;\n width?: number;\n height?: number;\n repeatableSection?: { label: string; minEntries?: number; maxEntries?: number; formDefId?: string; formDefSectionPrefix?: string };\n staticOnNewPage?: boolean;\n /** Resize behavior for grid groups */\n resizeMode?: 'freeform' | 'discreteGrid';\n}\n\nexport type GroupLayoutMode = 'absolute' | 'stack' | 'stacked' | 'vertical-stack' | 'horizontal-stack';\n\n/** True if group uses stack layout (vertical or horizontal); reflow and repeatables apply. */\nexport function isStackLayoutMode(mode?: GroupLayoutMode): boolean {\n const m = mode ?? 'absolute';\n return m === 'stack' || m === 'vertical-stack' || m === 'horizontal-stack';\n}\n/** True if vertical stacking (top-to-bottom). */\nexport function isVerticalStackLayoutMode(mode?: GroupLayoutMode): boolean {\n const m = mode ?? 'absolute';\n return m === 'stack' || m === 'vertical-stack';\n}\n/** True if horizontal stacking (left-to-right). */\nexport function isHorizontalStackLayoutMode(mode?: GroupLayoutMode): boolean {\n return mode === 'horizontal-stack';\n}\n\n// ============================================\n// CANVAS NODE - Union type for tree structure\n// ============================================\nexport type CanvasNode = CanvasElement | GroupNode;\n\n// Type guards\nexport function isGroup(node: CanvasNode): node is GroupNode {\n return node.type === 'group';\n}\n\nexport function isElement(node: CanvasNode): node is CanvasElement {\n return node.type !== 'group';\n}\n\n// ============================================\n// HELPER: Generate stable unique ID\n// Uses crypto.randomUUID for truly unique IDs that never collide\n// ============================================\nexport const generateId = (prefix: string = 'el'): string => {\n // Use crypto.randomUUID if available (modern browsers), fallback to custom implementation\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return `${prefix}-${crypto.randomUUID()}`;\n }\n // Fallback: Generate a UUID-like string\n return `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}-${Math.random().toString(36).substr(2, 9)}`;\n};\n\n// ============================================\n// HELPER: Create default element with Fabric defaults\n// ============================================\nexport const createDefaultElement = (\n partial: Partial<CanvasElement> & Pick<CanvasElement, 'id' | 'type' | 'name'>\n): CanvasElement => {\n const base: CanvasElement = {\n left: 48,\n top: 48,\n width: 100,\n height: 100,\n scaleX: 1,\n scaleY: 1,\n angle: 0,\n skewX: 0,\n skewY: 0,\n flipX: false,\n flipY: false,\n originX: 'left',\n originY: 'top',\n opacity: 1,\n fill: '#e2e8f0',\n stroke: '#64748b',\n strokeWidth: 1,\n visible: true,\n selectable: true,\n evented: true,\n ...partial,\n };\n \n // Add text-specific defaults (Keep Words = break at spaces, not mid-word)\n if (base.type === 'text') {\n return {\n ...base,\n wordWrap: base.wordWrap ?? 'keep-word',\n splitByGrapheme: base.splitByGrapheme ?? (base.wordWrap === 'break-word'),\n overflowPolicy: base.overflowPolicy ?? 'grow-and-push',\n };\n }\n \n return base;\n};\n\n// ============================================\n// HELPER: Create default group\n// ============================================\n/** Native Fabric-style group: position and visibility; size from content (Fabric). */\nexport const createDefaultGroup = (\n partial: Partial<GroupNode> & Pick<GroupNode, 'id' | 'name'>\n): GroupNode => ({\n type: 'group',\n children: [],\n collapsed: false,\n visible: true,\n locked: false,\n left: 0,\n top: 0,\n ...partial,\n});\n\n/**\n * Collect all group nodes that have at least one child (for group-bounds Rect on canvas).\n */\nexport function getGroupsWithChildren(children: CanvasNode[], depth = 0): GroupNode[] {\n if (depth > 50) return [];\n const result: GroupNode[] = [];\n for (const node of children) {\n if (isGroup(node) && (node as GroupNode).children?.length > 0) {\n result.push(node as GroupNode);\n result.push(...getGroupsWithChildren((node as GroupNode).children, depth + 1));\n }\n }\n return result;\n}\n\n// ============================================\n// CANVAS SETTINGS & STATE\n// ============================================\n\n// Project-level settings (apply to entire document)\nexport interface ProjectSettings {\n showGrid: boolean;\n snapToGrid: boolean;\n gridSize: number;\n snapToGuides: boolean;\n snapThreshold: number;\n}\n\n/** Special elementId for binding a dynamic field to the first page's background color. */\nexport const PAGE_BACKGROUND_ELEMENT_ID = '__pageBackground__';\n\n// Page-level settings (each page can have its own)\nexport interface PageSettings {\n backgroundColor: string;\n backgroundGradient?: GradientConfig;\n contentTop?: number;\n contentBottom?: number;\n // Backward compatibility (old schema stored these per-page)\n showGrid?: boolean;\n gridSize?: number;\n snapToGrid?: boolean;\n snapToGuides?: boolean;\n snapThreshold?: number;\n}\n\nexport interface CanvasPage {\n id: string;\n name: string;\n children: CanvasNode[]; // Tree structure - elements and groups\n settings: PageSettings;\n}\n\nexport interface CanvasState {\n width: number;\n height: number;\n pages: CanvasPage[];\n currentPageId: string;\n selectedIds: string[];\n zoom: number;\n pan: { x: number; y: number };\n projectSettings: ProjectSettings;\n \n // Dynamic fields system (centralized)\n dynamicFields: DynamicField[];\n fieldGroups: FieldGroup[];\n /** Form rendering preset for Use Template page (default | compact | cards | minimal). */\n formRenderPreset?: string;\n \n /** Form binding mode: 'dynamic' = manual fields, 'preset' = bound to a Form Definition. undefined = not yet chosen. */\n formBindingMode?: 'dynamic' | 'preset';\n /** When formBindingMode is 'preset', the bound form definition ID. */\n boundFormDefId?: string;\n /** When formBindingMode is 'preset', the bound form definition name (for display). */\n boundFormDefName?: string;\n \n // Theme system\n themeConfig?: ThemeConfig;\n}\n\n// ============================================\n// THEME SYSTEM\n// ============================================\n\n/**\n * A property bound to the theme system.\n * When a theme variant is selected, this property's value changes.\n */\nexport interface ThemeProperty {\n id: string; // unique ID\n label: string; // e.g., \"Page Background\", \"Heading Color\"\n elementId: string; // element ID or PAGE_BACKGROUND_ELEMENT_ID\n targetProperty: FieldTargetProperty; // e.g., 'backgroundColor', 'fill', 'stroke'\n svgColorKey?: string; // for SVG color map bindings (original color key)\n}\n\n/**\n * A named theme variant with specific values for all theme properties.\n */\nexport interface ThemeVariant {\n id: string;\n name: string; // e.g., \"Maroon\", \"Ocean Blue\"\n swatchColor: string; // color swatch shown in selector\n values: Record<string, string>; // themePropertyId -> value (color hex, font name, etc.)\n isDefault?: boolean; // the default variant auto-syncs with builder values\n}\n\n/**\n * Theme configuration for a template.\n */\nexport interface ThemeConfig {\n properties: ThemeProperty[];\n variants: ThemeVariant[];\n}\n\n/** Create an empty theme config */\nexport const createEmptyThemeConfig = (): ThemeConfig => ({\n properties: [],\n variants: [{\n id: 'default',\n name: 'Default',\n swatchColor: '#ffffff',\n values: {},\n isDefault: true,\n }],\n});\n\nexport interface AlignmentGuide {\n type: 'vertical' | 'horizontal';\n position: number;\n start: number;\n end: number;\n}\n\nexport type Tool = 'select' | 'text' | 'rectangle' | 'circle' | 'line' | 'image' | 'hand';\n\n// ============================================\n// DYNAMIC FIELDS SYSTEM v2.2\n// Centralized form field configuration\n// ============================================\n\n/**\n * All supported form field types\n */\nexport type DynamicFieldType = \n | 'text' // Single line text input\n | 'textarea' // Multi-line text with optional rich formatting\n | 'number' // Numeric input (integer, decimal, currency, percentage)\n | 'date' // Date picker\n | 'email' // Email with validation\n | 'phone' // Phone number with formatting\n | 'url' // URL/link input\n | 'image' // Image upload with preview\n | 'color' // Color picker\n | 'select' // Single selection dropdown\n | 'multiselect' // Multiple selection (tags)\n | 'rating' // Star/numeric rating\n | 'toggle' // Boolean toggle/switch\n | 'currency'; // Currency input with symbol\n\n/**\n * Number format variants\n */\nexport type NumberFormat = 'integer' | 'decimal' | 'percentage';\n\n/**\n * Target properties that can be updated on elements\n */\nexport type FieldTargetProperty =\n | 'text' // Text content\n | 'content' // Text content (alternate)\n | 'fill' // Fill color\n | 'stroke' // Stroke/border color\n | 'backgroundColor' // Page background color (special: applies to page settings)\n | 'backgroundGradient' // Page background gradient (JSON-serialized GradientConfig)\n | 'src' // Image source\n | 'fontSize' // Font size (number fields)\n | 'fontFamily' // Font family (select fields)\n | 'fontWeight' // Font weight\n | 'opacity' // Opacity (number 0-1)\n | 'visible' // Visibility toggle\n | 'width' // Element width\n | 'height' // Element height\n | 'borderRadius' // Shape/image corner radius\n | 'link' // URL link (applies to any element)\n | `smartProp:${string}`; // Smart element property (e.g. smartProp:value)\n\n/**\n * Option for select/multiselect fields\n */\nexport interface SelectOption {\n value: string;\n label: string;\n icon?: string; // Optional emoji or icon name\n color?: string; // Optional color for visual distinction\n}\n\n/**\n * Validation rules for form fields\n */\nexport interface DynamicFieldValidation {\n required?: boolean;\n minLength?: number;\n maxLength?: number;\n min?: number; // For numbers\n max?: number; // For numbers\n pattern?: string; // Regex pattern\n patternMessage?: string; // Custom error for pattern\n customMessage?: string; // Override default required message\n \n // URL-specific\n validateUrl?: boolean; // Enable URL format validation\n allowedProtocols?: string[]; // e.g., ['https', 'http'] - defaults to ['https', 'http']\n requireHttps?: boolean; // Only allow https URLs\n \n // Image-specific\n maxFileSize?: number; // In bytes\n acceptedFormats?: string[]; // e.g., ['image/png', 'image/jpeg']\n minWidth?: number; // Min image width\n minHeight?: number; // Min image height\n aspectRatio?: number; // Required aspect ratio (width/height)\n \n // Select-specific\n minSelections?: number; // For multiselect\n maxSelections?: number; // For multiselect\n}\n\n/**\n * Mapping from a dynamic field to an element property\n */\nexport interface FieldMapping {\n elementId: string;\n targetProperty: FieldTargetProperty;\n \n // Optional transformations\n transform?: 'uppercase' | 'lowercase' | 'capitalize' | 'none';\n prefix?: string; // Text to prepend\n suffix?: string; // Text to append\n formatPattern?: string; // For dates, numbers (e.g., \"MMM DD, YYYY\")\n}\n\n/**\n * Dynamic field configuration\n */\nexport interface DynamicField {\n id: string; // Unique identifier (e.g., \"full_name\", \"primary_color\")\n label: string; // Display label in form\n type: DynamicFieldType;\n description?: string; // Help text shown under field\n placeholder?: string; // Placeholder text\n defaultValue?: string | number | boolean | string[];\n \n // Element mappings (one field can update multiple elements)\n mappings: FieldMapping[];\n \n // Validation rules\n validation?: DynamicFieldValidation;\n \n // Organization\n group?: string; // Group ID for form sections\n order?: number; // Display order within group\n \n // Type-specific options\n // Number fields\n numberFormat?: NumberFormat;\n currencySymbol?: string; // e.g., \"$\", \"€\", \"£\"\n currencyCode?: string; // e.g., \"USD\", \"EUR\"\n decimalPlaces?: number;\n step?: number; // Increment step for number input\n \n // Select fields\n options?: SelectOption[];\n allowCustomOption?: boolean; // Allow user to add custom option\n \n // Rating fields\n maxRating?: number; // Max stars (default 5)\n allowHalf?: boolean; // Allow half ratings\n \n // Date fields\n dateFormat?: string; // Display format\n minDate?: string; // ISO date string\n maxDate?: string; // ISO date string\n \n // Image fields\n cropAspectRatio?: number; // Enable cropping with aspect ratio\n showPreview?: boolean; // Show image preview\n \n // Textarea fields\n rows?: number; // Initial rows\n maxRows?: number; // Max auto-expand rows\n enableMarkdown?: boolean; // Enable markdown preview\n \n // UI hints\n width?: 'full' | 'half' | 'third'; // Form layout width\n hidden?: boolean; // Hide from form (for computed fields)\n readonly?: boolean; // Show but don't allow editing\n icon?: string; // Icon to show with field\n}\n\n/**\n * Field group for organizing form sections\n */\nexport interface FieldGroup {\n id: string;\n label: string;\n description?: string;\n icon?: string; // Icon for group header\n order: number;\n collapsible?: boolean;\n defaultCollapsed?: boolean;\n}\n\n/**\n * Helper to create a dynamic field with defaults\n */\nexport const createDynamicField = (\n partial: Partial<DynamicField> & Pick<DynamicField, 'id' | 'label' | 'type'>\n): DynamicField => ({\n mappings: [],\n order: 0,\n ...partial,\n});\n\n/**\n * Property-to-FieldType compatibility mapping\n * Defines which field types can bind to which element properties\n */\nexport const PROPERTY_FIELD_COMPATIBILITY: Partial<Record<FieldTargetProperty, DynamicFieldType[]>> & Record<string, DynamicFieldType[]> = {\n text: ['text', 'textarea', 'email', 'phone', 'url', 'date', 'number', 'currency', 'select'],\n content: ['text', 'textarea', 'email', 'phone', 'url', 'date', 'number', 'currency', 'select'],\n src: ['image', 'url'],\n fill: ['color'],\n stroke: ['color'],\n backgroundColor: ['color'],\n backgroundGradient: ['color'],\n fontSize: ['number'],\n fontFamily: ['select'],\n fontWeight: ['number', 'select'],\n opacity: ['number'],\n visible: ['toggle'],\n width: ['number'],\n height: ['number'],\n borderRadius: ['number'],\n link: ['url', 'text'], // URL field type or text for dynamic links\n};\n\n/**\n * Element type to relevant properties mapping\n * Defines which properties are shown for each element type\n */\nexport const ELEMENT_TYPE_PROPERTIES: Record<NodeType, FieldTargetProperty[]> = {\n text: ['text', 'fill', 'fontSize', 'fontFamily', 'fontWeight', 'opacity', 'visible', 'link'],\n image: ['src', 'opacity', 'borderRadius', 'visible', 'link'],\n shape: ['fill', 'stroke', 'opacity', 'borderRadius', 'visible', 'link'],\n line: ['stroke', 'opacity', 'visible', 'link'],\n group: ['visible', 'link'],\n};\n\n/**\n * Get compatible field types for a given target property.\n * For smartProp:* properties, infers compatibility from the smart element definition.\n */\nexport const getCompatibleFieldTypes = (targetProperty: FieldTargetProperty): DynamicFieldType[] => {\n if (PROPERTY_FIELD_COMPATIBILITY[targetProperty]) {\n return PROPERTY_FIELD_COMPATIBILITY[targetProperty];\n }\n // Smart prop: infer from suffix pattern\n if (targetProperty.startsWith('smartProp:')) {\n const key = targetProperty.slice('smartProp:'.length).toLowerCase();\n if (key.includes('color') || key === 'fg' || key === 'bg') return ['color'];\n if (key.includes('url') || key.includes('link')) return ['url', 'text'];\n if (key.includes('rating') || key.includes('progress') || key.includes('percent')) return ['number'];\n return ['text', 'textarea', 'url', 'number'];\n }\n return ['text'];\n};\n\n/**\n * Check if a field type is compatible with a target property\n */\nexport const isFieldCompatible = (fieldType: DynamicFieldType, targetProperty: FieldTargetProperty): boolean => {\n const compatible = getCompatibleFieldTypes(targetProperty);\n return compatible.includes(fieldType);\n};\n\n/**\n * Get relevant properties for an element type\n */\nexport const getElementProperties = (elementType: NodeType): FieldTargetProperty[] => {\n return ELEMENT_TYPE_PROPERTIES[elementType] || ['visible'];\n};\n\n/**\n * Helper to create a field group\n */\nexport const createFieldGroup = (\n partial: Partial<FieldGroup> & Pick<FieldGroup, 'id' | 'label'>\n): FieldGroup => ({\n order: 0,\n ...partial,\n});\n\n/**\n * Field type metadata for UI\n */\nexport const FIELD_TYPE_META: Record<DynamicFieldType, {\n label: string;\n icon: string;\n description: string;\n defaultTargetProperty: FieldTargetProperty;\n}> = {\n text: { label: 'Text', icon: 'type', description: 'Single line text', defaultTargetProperty: 'text' },\n textarea: { label: 'Long Text', icon: 'align-left', description: 'Multi-line text', defaultTargetProperty: 'text' },\n number: { label: 'Number', icon: 'hash', description: 'Numeric value', defaultTargetProperty: 'text' },\n date: { label: 'Date', icon: 'calendar', description: 'Date picker', defaultTargetProperty: 'text' },\n email: { label: 'Email', icon: 'mail', description: 'Email address', defaultTargetProperty: 'text' },\n phone: { label: 'Phone', icon: 'phone', description: 'Phone number', defaultTargetProperty: 'text' },\n url: { label: 'URL', icon: 'link', description: 'Web link', defaultTargetProperty: 'text' },\n image: { label: 'Image', icon: 'image', description: 'Image upload', defaultTargetProperty: 'src' },\n color: { label: 'Color', icon: 'palette', description: 'Color picker', defaultTargetProperty: 'fill' },\n select: { label: 'Select', icon: 'chevron-down', description: 'Single choice', defaultTargetProperty: 'text' },\n multiselect: { label: 'Multi-Select', icon: 'check-square', description: 'Multiple choices', defaultTargetProperty: 'text' },\n rating: { label: 'Rating', icon: 'star', description: 'Star rating', defaultTargetProperty: 'text' },\n toggle: { label: 'Toggle', icon: 'toggle-left', description: 'On/Off switch', defaultTargetProperty: 'visible' },\n currency: { label: 'Currency', icon: 'dollar-sign', description: 'Money amount', defaultTargetProperty: 'text' },\n};\n\n// ============================================\n// TEMPLATE CONFIG (for saving/loading) - v2.2\n// ============================================\nexport interface TemplateConfigPage {\n id: string;\n name: string;\n children: CanvasNode[]; // Nested tree structure\n settings: PageSettings;\n}\n\nexport interface TemplateConfig {\n version: string; // \"2.2\" for dynamic fields system\n name: string;\n description?: string;\n canvas: {\n width: number;\n height: number;\n };\n projectSettings: ProjectSettings;\n pages: TemplateConfigPage[];\n \n // NEW: Centralized dynamic fields configuration\n dynamicFields: DynamicField[];\n fieldGroups?: FieldGroup[];\n \n // Theme system\n themeConfig?: ThemeConfig;\n \n /** Form binding mode: 'dynamic' = manual fields, 'preset' = bound to a Form Definition. */\n formBindingMode?: 'dynamic' | 'preset';\n /** When formBindingMode is 'preset', the bound form definition ID. */\n boundFormDefId?: string;\n /** When formBindingMode is 'preset', the bound form definition name (for display). */\n boundFormDefName?: string;\n \n // UI state preferences\n showSections?: boolean; // Show section highlights on canvas\n showDynamicLabels?: boolean; // Show dynamic field labels\n \n /** When true, group children left/top are stored relative to parent (v2.2+). Omit/false = legacy absolute. */\n relativeGroupPositions?: boolean;\n\n /** When true, template has only structure/properties (display, position, auto, inherit); layout engine computes dimensions/positions on demand (no script layout, no reflow on load). */\n layoutEngineDriven?: boolean;\n\n /** Auto pagination: when content overflows the page, flow to a new page (future). For now, use \"Add page\" for multi-page docs. */\n autoPagination?: boolean;\n\n /**\n * When autoPagination is true: which root-level group is the flow region (allowed to overflow),\n * and which root-level node ids to repeat on continuation pages (e.g. header, footer).\n * Flow region = the one group whose content can be split across pages; all other content is fixed on page 1.\n */\n paginationConfig?: {\n /** Root-level group id that is allowed to grow and overflow; its content is split across pages. */\n flowRegionId?: string;\n /** Root-level node ids to clone on every continuation page (e.g. header, footer). */\n repeatOnContinuationIds?: string[];\n };\n\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface DocumentTemplate {\n id: string;\n name: string;\n category: 'resume' | 'certificate' | 'id-card' | 'biodata' | 'other';\n canvas: CanvasState;\n createdAt: Date;\n updatedAt: Date;\n}\n\n// Available fonts for professional documents\nexport const AVAILABLE_FONTS = [\n // Serif - Professional & Traditional\n { name: 'Playfair Display', category: 'Serif', style: 'Elegant headlines' },\n { name: 'Merriweather', category: 'Serif', style: 'Readable body text' },\n { name: 'Lora', category: 'Serif', style: 'Classic look' },\n \n // Sans-Serif - Modern & Clean\n { name: 'Montserrat', category: 'Sans-Serif', style: 'Modern headers' },\n { name: 'Open Sans', category: 'Sans-Serif', style: 'Clean body text' },\n { name: 'Roboto', category: 'Sans-Serif', style: 'Versatile' },\n { name: 'Lato', category: 'Sans-Serif', style: 'Friendly' },\n { name: 'Raleway', category: 'Sans-Serif', style: 'Elegant thin' },\n { name: 'Poppins', category: 'Sans-Serif', style: 'Geometric' },\n { name: 'Inter', category: 'Sans-Serif', style: 'UI friendly' },\n { name: 'Nunito', category: 'Sans-Serif', style: 'Rounded' },\n { name: 'Source Sans Pro', category: 'Sans-Serif', style: 'Professional' },\n { name: 'Work Sans', category: 'Sans-Serif', style: 'Modern' },\n \n // Display - Creative\n { name: 'Oswald', category: 'Display', style: 'Bold impact' },\n { name: 'Bebas Neue', category: 'Display', style: 'All caps' },\n { name: 'Abril Fatface', category: 'Display', style: 'Poster style' },\n \n // Handwriting - Personal touch\n { name: 'Dancing Script', category: 'Handwriting', style: 'Signatures' },\n { name: 'Pacifico', category: 'Handwriting', style: 'Casual' },\n { name: 'Great Vibes', category: 'Handwriting', style: 'Elegant cursive' },\n] as const;\n\nexport type FontName = typeof AVAILABLE_FONTS[number]['name'];\n\n// Config interfaces for special element features\n\nexport interface LinkConfig {\n url?: string;\n openInNewTab?: boolean;\n}\n\nexport interface BulletConfig {\n style: 'disc' | 'circle' | 'square' | 'number' | 'dash';\n bulletColor?: string;\n itemSpacing?: number;\n}\n\nexport interface SmartFormData {\n [fieldName: string]: string | number | string[] | SmartFormData[];\n}\n\nexport interface ValidationError {\n fieldName: string;\n message: string;\n}\n\n// ============================================\n// TREE UTILITY FUNCTIONS\n// ============================================\n\n/**\n * Flatten children tree to get all elements (for Fabric.js rendering)\n * Returns elements in z-order (first = bottom, last = top)\n * Includes depth guard to prevent infinite recursion from circular references\n */\nexport function flattenChildren(children: CanvasNode[], visited?: Set<string>, depth = 0): CanvasElement[] {\n // Safety guard: prevent infinite recursion\n const MAX_DEPTH = 50;\n if (depth > MAX_DEPTH) {\n console.warn('flattenChildren: Maximum depth exceeded, possible circular reference');\n return [];\n }\n \n const seen = visited || new Set<string>();\n const result: CanvasElement[] = [];\n \n for (const node of children) {\n // Skip if we've already processed this node (circular reference guard)\n if (seen.has(node.id)) {\n console.warn(`flattenChildren: Circular reference detected for node ${node.id}`);\n continue;\n }\n seen.add(node.id);\n \n if (isGroup(node)) {\n result.push(...flattenChildren(node.children, seen, depth + 1));\n } else {\n result.push(node as CanvasElement);\n }\n }\n \n return result;\n}\n\n/**\n * Find a node by ID in the tree\n * Includes depth guard to prevent infinite recursion\n */\nexport function findNodeById(children: CanvasNode[], id: string, depth = 0): CanvasNode | null {\n if (depth > 50) return null; // Safety guard\n \n for (const node of children) {\n if (node.id === id) return node;\n if (isGroup(node)) {\n const found = findNodeById(node.children, id, depth + 1);\n if (found) return found;\n }\n }\n return null;\n}\n\n/**\n * Collect all groups that have no children (empty groups). Used so the canvas can render a draggable placeholder for them.\n */\nexport function getEmptyGroups(children: CanvasNode[], depth = 0): GroupNode[] {\n if (depth > 50) return [];\n const result: GroupNode[] = [];\n for (const node of children) {\n if (isGroup(node)) {\n if (node.children.length === 0) result.push(node);\n result.push(...getEmptyGroups(node.children, depth + 1));\n }\n }\n return result;\n}\n\n/**\n * Find parent group of a node\n * Includes depth guard to prevent infinite recursion\n */\nexport function findParentGroup(children: CanvasNode[], targetId: string, depth = 0): GroupNode | null {\n if (depth > 50) return null; // Safety guard\n \n for (const node of children) {\n if (isGroup(node)) {\n // Check if target is direct child\n if (node.children.some(child => child.id === targetId)) {\n return node;\n }\n // Recurse into group\n const found = findParentGroup(node.children, targetId, depth + 1);\n if (found) return found;\n }\n }\n return null;\n}\n\n/**\n * Update a node in the tree (immutably)\n */\nexport function updateNodeInTree(\n children: CanvasNode[],\n id: string,\n updates: Partial<CanvasNode>\n): CanvasNode[] {\n return children.map(node => {\n if (node.id === id) {\n return { ...node, ...updates } as CanvasNode;\n }\n if (isGroup(node)) {\n return {\n ...node,\n children: updateNodeInTree(node.children, id, updates),\n };\n }\n return node;\n });\n}\n\n/**\n * Remove a node from the tree (immutably)\n */\nexport function removeNodeFromTree(children: CanvasNode[], id: string): CanvasNode[] {\n return children\n .filter(node => node.id !== id)\n .map(node => {\n if (isGroup(node)) {\n return {\n ...node,\n children: removeNodeFromTree(node.children, id),\n };\n }\n return node;\n });\n}\n\n/**\n * Add a node to a specific parent (or root if parentId is null)\n */\nexport function addNodeToTree(\n children: CanvasNode[],\n node: CanvasNode,\n parentId: string | null,\n index?: number\n): CanvasNode[] {\n if (!parentId) {\n // Add to root\n if (index !== undefined) {\n const result = [...children];\n result.splice(index, 0, node);\n return result;\n }\n return [...children, node];\n }\n \n return children.map(child => {\n if (child.id === parentId && isGroup(child)) {\n const newChildren = [...child.children];\n if (index !== undefined) {\n newChildren.splice(index, 0, node);\n } else {\n newChildren.push(node);\n }\n return { ...child, children: newChildren };\n }\n if (isGroup(child)) {\n return {\n ...child,\n children: addNodeToTree(child.children, node, parentId, index),\n };\n }\n return child;\n });\n}\n\n/**\n * Move a node to a new position in the tree\n */\nexport function moveNodeInTree(\n children: CanvasNode[],\n nodeId: string,\n newParentId: string | null,\n newIndex: number\n): CanvasNode[] {\n // First, find and remove the node\n const node = findNodeById(children, nodeId);\n if (!node) return children;\n \n const withoutNode = removeNodeFromTree(children, nodeId);\n \n // Then add it to the new position\n return addNodeToTree(withoutNode, node, newParentId, newIndex);\n}\n\n/**\n * Get all node IDs in a subtree (for selection expansion)\n */\nexport function getAllNodeIds(nodes: CanvasNode[]): string[] {\n const ids: string[] = [];\n for (const node of nodes) {\n ids.push(node.id);\n if (isGroup(node)) {\n ids.push(...getAllNodeIds(node.children));\n }\n }\n return ids;\n}\n\n/**\n * Get all element IDs in a subtree (excluding group IDs)\n */\nexport function getAllElementIds(nodes: CanvasNode[]): string[] {\n const ids: string[] = [];\n for (const node of nodes) {\n if (isGroup(node)) {\n ids.push(...getAllElementIds(node.children));\n } else {\n ids.push(node.id);\n }\n }\n return ids;\n}\n\n/**\n * Find the smallest (deepest) group that contains all of the given node IDs.\n * Used when moving a selection: move the common ancestor group instead of each element.\n */\nexport function findCommonAncestorGroup(selectedIds: string[], pageChildren: CanvasNode[]): GroupNode | null {\n if (selectedIds.length === 0) return null;\n const firstId = selectedIds[0];\n let candidate = findParentGroup(pageChildren, firstId);\n while (candidate) {\n const descendantIds = new Set([candidate.id, ...getAllNodeIds(candidate.children ?? [])]);\n if (selectedIds.every((id) => descendantIds.has(id))) return candidate;\n candidate = findParentGroup(pageChildren, candidate.id);\n }\n return null;\n}\n","/**\n * Text Measurement Service\n * \n * Uses Fabric.js Textbox to accurately measure text dimensions.\n * This provides pixel-accurate measurements instead of estimations.\n */\n\nimport * as fabric from 'fabric';\nimport { CanvasElement } from '@/types/editor';\n\n// Cache for measured heights to avoid redundant calculations\nconst heightCache = new Map<string, { height: number; timestamp: number }>();\nconst CACHE_TTL = 5000; // 5 seconds cache\n\n/** Width bucket (px) so small resize changes hit cache during drag (e.g. 8px). */\nconst WIDTH_BUCKET_PX = 8;\nfunction bucketWidth(w: number): number {\n return Math.round(w / WIDTH_BUCKET_PX) * WIDTH_BUCKET_PX;\n}\n\n/**\n * Generate a cache key for an element based on all properties that affect text bounds.\n * Uses width bucket so live group resize hits cache often (no recompute every pixel).\n */\nfunction getCacheKey(element: CanvasElement): string {\n const widthPx = typeof element.width === 'number' ? element.width : 200;\n return JSON.stringify({\n text: element.text,\n widthBucket: bucketWidth(widthPx),\n fontSize: element.fontSize,\n fontFamily: element.fontFamily,\n fontWeight: element.fontWeight,\n fontStyle: element.fontStyle,\n textAlign: element.textAlign,\n lineHeight: element.lineHeight,\n charSpacing: element.charSpacing,\n underline: element.underline,\n linethrough: element.linethrough,\n scaleX: element.scaleX,\n scaleY: element.scaleY,\n splitByGrapheme: element.splitByGrapheme,\n overflowPolicy: element.overflowPolicy,\n height: element.overflowPolicy === 'auto-shrink' ? element.height : undefined,\n });\n}\n\n/**\n * Measure text height using Fabric.js Textbox\n * This creates an off-screen Textbox, sets the text, and measures the actual rendered height.\n */\nexport function measureTextHeight(element: CanvasElement): number {\n if (element.type !== 'text') {\n return element.height || 20;\n }\n // For empty text, measure a single space to get consistent line height\n const textToMeasure = element.text || ' ';\n\n // Check cache first\n const cacheKey = getCacheKey(element);\n const cached = heightCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < CACHE_TTL) {\n return cached.height;\n }\n\n try {\n const widthPx = typeof element.width === 'number' ? element.width : 200;\n const measureWidth = Math.max(1, bucketWidth(widthPx)); // measure at bucket so cache hits during drag\n let fontSize = element.fontSize || 16;\n\n // Auto-shrink: reduce font size until text fits within stored width & height (mirrors createText logic)\n const overflowPolicy = element.overflowPolicy || 'grow-and-push';\n if (overflowPolicy === 'auto-shrink') {\n const baseHeight = element.height;\n const explicitLineCount = Math.max(1, textToMeasure.split('\\n').length);\n while (fontSize > 1) {\n const testTb = new fabric.Textbox(textToMeasure, {\n width: measureWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n splitByGrapheme: false,\n });\n testTb.initDimensions();\n const textHeight = testTb.height || 0;\n const renderedLineCount = testTb.textLines?.length || 1;\n const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;\n const fitsHeight = !baseHeight || textHeight <= baseHeight;\n const actualTextboxWidth = testTb.width ?? measureWidth;\n const widthDidGrow = actualTextboxWidth > measureWidth + 0.5;\n const lineWidths = (testTb as any).__lineWidths as number[] | undefined;\n const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;\n const fitsWidth = !widthDidGrow && maxLineWidth <= measureWidth + 1;\n if (hasNoImplicitWrap && fitsHeight && fitsWidth) break;\n fontSize--;\n }\n // For auto-shrink, measure at the shrunk font size and cap to stored height\n const finalTb = new fabric.Textbox(textToMeasure, {\n width: measureWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n splitByGrapheme: false,\n });\n finalTb.initDimensions();\n const measuredH = (finalTb.height || element.height || 20) * (element.scaleY || 1);\n const cappedH = typeof baseHeight === 'number' ? Math.min(baseHeight * (element.scaleY || 1), measuredH) : measuredH;\n heightCache.set(cacheKey, { height: cappedH, timestamp: Date.now() });\n return cappedH;\n }\n\n const textbox = new fabric.Textbox(textToMeasure, {\n width: measureWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n textAlign: (element.textAlign as any) || 'left',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n underline: element.underline ?? false,\n linethrough: element.linethrough ?? false,\n splitByGrapheme: element.splitByGrapheme ?? (element.wordWrap === 'break-word'),\n left: 0,\n top: 0,\n });\n\n // Initialize dimensions to trigger text reflow\n textbox.initDimensions();\n\n // Get the actual height\n const height = textbox.height || element.height || 20;\n \n // Apply scale\n const scaledHeight = height * (element.scaleY || 1);\n\n // Cache the result\n heightCache.set(cacheKey, { height: scaledHeight, timestamp: Date.now() });\n\n return scaledHeight;\n } catch (error) {\n console.warn('[measureTextHeight] Failed to measure text, using fallback:', error);\n return fallbackEstimateHeight(element);\n }\n}\n\n/**\n * Minimum width so text doesn't overflow horizontally (longest line width).\n * Uses a temporary Textbox with large width to measure actual line widths.\n */\nexport function getMinTextWidth(element: CanvasElement): number {\n if (element.type !== 'text' || !element.text) {\n return typeof element.width === 'number' ? element.width : 200;\n }\n try {\n const textbox = new fabric.Textbox(element.text, {\n width: 10000,\n fontSize: element.fontSize || 16,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n splitByGrapheme: element.splitByGrapheme ?? (element.wordWrap === 'break-word'),\n left: 0,\n top: 0,\n });\n textbox.initDimensions();\n const lineWidths = (textbox as any).__lineWidths as number[] | undefined;\n if (lineWidths && lineWidths.length > 0) {\n const maxLine = Math.max(...lineWidths);\n return Math.ceil(maxLine) + 2;\n }\n } catch (_) {\n // ignore\n }\n return typeof element.width === 'number' ? element.width : 200;\n}\n\n/**\n * Fallback height estimation when Fabric measurement fails\n * Uses a simple character-based estimation\n */\nfunction fallbackEstimateHeight(element: CanvasElement): number {\n if (element.type !== 'text' || !element.text) {\n return element.height || 20;\n }\n\n const text = element.text;\n const fontSize = element.fontSize || 16;\n const lineHeight = element.lineHeight || 1.2;\n const fontStyle = element.fontStyle || 'normal';\n const width = (element.width || 200) * (element.scaleX || 1);\n\n // Estimate characters per line based on average character width (~0.5 * fontSize)\n const avgCharWidth = fontSize * 0.5;\n const charsPerLine = Math.max(1, Math.floor(width / avgCharWidth));\n\n // Count explicit line breaks and wrapped lines\n const lines = text.split('\\n');\n let totalLines = 0;\n\n for (const line of lines) {\n const wrappedLines = Math.max(1, Math.ceil(line.length / charsPerLine));\n totalLines += wrappedLines;\n }\n\n const estimatedHeight = totalLines * fontSize * lineHeight;\n return Math.max(element.height || 20, estimatedHeight) * (element.scaleY || 1);\n}\n\n/**\n * Measure text and return full bounding box\n */\nexport function measureTextBounds(element: CanvasElement): {\n width: number;\n height: number;\n left: number;\n top: number;\n bottom: number;\n right: number;\n} {\n const width = (element.width || 100) * (element.scaleX || 1);\n const height = element.type === 'text' \n ? measureTextHeight(element)\n : (element.height || 20) * (element.scaleY || 1);\n\n return {\n left: element.left,\n top: element.top,\n width,\n height,\n bottom: element.top + height,\n right: element.left + width,\n };\n}\n\n/**\n * Clear the measurement cache\n * Call this when making bulk updates or when memory is a concern\n */\nexport function clearMeasurementCache(): void {\n heightCache.clear();\n}\n\n/**\n * Apply auto-shrink policy to text element\n * Reduces font size until text fits within the specified bounds\n * Returns the new fontSize to use\n */\nexport function calculateAutoShrinkFontSize(\n element: CanvasElement,\n maxWidth: number,\n maxHeight: number\n): number {\n if (element.type !== 'text' || !element.text) {\n return element.fontSize || 16;\n }\n\n const minFontSize = element.minFontSize || 8;\n let fontSize = element.fontSize || 16;\n\n // Binary search for the right font size\n let low = minFontSize;\n let high = fontSize;\n\n while (low < high - 1) {\n const mid = Math.floor((low + high) / 2);\n \n const testElement = { ...element, fontSize: mid };\n const bounds = measureTextBounds(testElement);\n\n if (bounds.width <= maxWidth && bounds.height <= maxHeight) {\n low = mid;\n } else {\n high = mid;\n }\n }\n\n return low;\n}\n\n/**\n * Apply max-lines-ellipsis policy to text\n * Truncates text to fit within maxLines and adds ellipsis\n * Returns the truncated text\n */\nexport function truncateTextToMaxLines(\n element: CanvasElement,\n maxLines: number\n): string {\n if (element.type !== 'text' || !element.text) {\n return element.text || '';\n }\n\n const text = element.text;\n const fontSize = element.fontSize || 16;\n const width = (element.width || 200) * (element.scaleX || 1);\n\n // Estimate characters per line\n const avgCharWidth = fontSize * 0.5;\n const charsPerLine = Math.max(1, Math.floor(width / avgCharWidth));\n\n const lines = text.split('\\n');\n const outputLines: string[] = [];\n let lineCount = 0;\n\n for (const line of lines) {\n if (lineCount >= maxLines) break;\n\n // Check if this line needs to wrap\n if (line.length <= charsPerLine) {\n outputLines.push(line);\n lineCount++;\n } else {\n // Split into multiple lines\n let remaining = line;\n while (remaining.length > 0 && lineCount < maxLines) {\n const chunk = remaining.substring(0, charsPerLine);\n remaining = remaining.substring(charsPerLine);\n \n if (lineCount === maxLines - 1 && remaining.length > 0) {\n // Last allowed line with more content - add ellipsis\n outputLines.push(chunk.trim() + '…');\n } else {\n outputLines.push(chunk);\n }\n lineCount++;\n }\n }\n }\n\n return outputLines.join('\\n');\n}\n","/**\n * Layout Engine (stub) – start from scratch.\n * Exports minimal no-op/simple implementations so the app runs without the previous layout logic.\n */\n\nimport type { CanvasElement, CanvasNode, CanvasState, CanvasPage, GroupNode } from '@/types/editor';\nimport type { GroupLayoutMode } from '@/types/editor';\nimport { isElement, isGroup, findParentGroup, findNodeById, isVerticalStackLayoutMode, isStackLayoutMode, generateId } from '@/types/editor';\nimport { measureTextHeight } from '@/lib/textMeasurement';\n\n/** Config for which nodes repeat on continuation pages and which group is the flow region. */\nexport interface PaginationConfig {\n flowRegionId?: string;\n repeatOnContinuationIds?: string[];\n}\n\nexport interface BoundsOptions {\n pageContentWidth?: number;\n pageContentHeight?: number;\n resolvedContentWidth?: number;\n resolvedContentHeight?: number;\n}\n\nexport interface LayoutRecalcResult {\n updates: Map<string, { top?: number; left?: number; width?: number; height?: number }>;\n boundsChanged: boolean;\n newBounds: { top: number; bottom: number; height: number; width?: number };\n}\n\nfunction simpleWidth(node: CanvasNode): number {\n if (isElement(node)) {\n const w = (node as CanvasElement).width;\n return typeof w === 'number' ? w : 100;\n }\n return 1; // Group: size from content (see getNodeBounds)\n}\n\nfunction simpleHeight(node: CanvasNode): number {\n if (isElement(node)) {\n const el = node as CanvasElement;\n const h = el.height;\n if (typeof h === 'number') return h;\n // For text elements without explicit height, measure actual text height\n if (el.type === 'text' && el.text) {\n return measureTextHeight(el);\n }\n return 20;\n }\n return 1; // Group: size from content (see getNodeBounds)\n}\n\nfunction getNodeLeft(node: CanvasNode): number {\n return typeof node.left === 'number' ? node.left : 0;\n}\n\nfunction getNodeTop(node: CanvasNode): number {\n return typeof node.top === 'number' ? node.top : 0;\n}\n\n/** Stub: return absolute layout for all sections (native Fabric group, no flex/grid). */\nexport function getDefaultLayoutMode(_sectionType?: string): GroupLayoutMode {\n return 'absolute';\n}\n\n/** Stack group: vertical = first child top; rest top = prevBottom + gap; horizontal = first left; rest left = prevRight + gap. Returns group-relative { top, left } per child id. */\nexport function resolveStackGroupEffectivePositions(\n group: GroupNode,\n pageChildren: CanvasNode[],\n options?: BoundsOptions\n): Map<string, { top: number; left: number }> {\n const mode = group.layoutMode ?? 'absolute';\n if (!isStackLayoutMode(mode)) return new Map();\n const gap = group.stackSpacing ?? 8;\n const kids = group.children ?? [];\n const out = new Map<string, { top: number; left: number }>();\n if (isVerticalStackLayoutMode(mode)) {\n let prevBottom = 0;\n for (let i = 0; i < kids.length; i++) {\n const child = kids[i];\n const storedTop = getNodeTop(child);\n const storedLeft = getNodeLeft(child);\n const effectiveTop = i === 0 ? storedTop : prevBottom + gap + storedTop;\n out.set(child.id, { top: effectiveTop, left: storedLeft });\n const h = getNodeBounds(child, pageChildren, options).height;\n prevBottom = effectiveTop + h;\n }\n } else {\n let prevRight = 0;\n for (let i = 0; i < kids.length; i++) {\n const child = kids[i];\n const storedLeft = getNodeLeft(child);\n const storedTop = getNodeTop(child);\n const effectiveLeft = i === 0 ? storedLeft : prevRight + gap + storedLeft;\n out.set(child.id, { top: storedTop, left: effectiveLeft });\n const w = getNodeBounds(child, pageChildren, options).width;\n prevRight = effectiveLeft + w;\n }\n }\n return out;\n}\n\n/** Group size from children (or explicit width/height when empty). For stack groups uses resolved positions. */\nfunction groupBoundsFromChildren(\n group: GroupNode,\n pageChildren: CanvasNode[],\n options?: BoundsOptions\n): { width: number; height: number } {\n const kids = group.children ?? [];\n if (kids.length === 0) {\n const w = group.width;\n const h = group.height;\n return { width: typeof w === 'number' && w > 0 ? w : 1, height: typeof h === 'number' && h > 0 ? h : 1 };\n }\n const isStack = isStackLayoutMode(group.layoutMode);\n const positions = isStack ? resolveStackGroupEffectivePositions(group, pageChildren, options) : null;\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;\n for (const child of kids) {\n const b = getNodeBounds(child, pageChildren, options);\n const cl = positions ? (positions.get(child.id)?.left ?? getNodeLeft(child)) : getNodeLeft(child);\n const ct = positions ? (positions.get(child.id)?.top ?? getNodeTop(child)) : getNodeTop(child);\n minX = Math.min(minX, cl);\n minY = Math.min(minY, ct);\n maxX = Math.max(maxX, cl + b.width);\n maxY = Math.max(maxY, ct + b.height);\n }\n return {\n width: Math.max(1, maxX - minX),\n height: Math.max(1, maxY - minY),\n };\n}\n\nexport function getNodeBounds(\n node: CanvasNode,\n pageChildren?: CanvasNode[],\n options?: BoundsOptions\n): { left: number; top: number; width: number; height: number; bottom: number; right: number } {\n const left = getNodeLeft(node);\n const top = getNodeTop(node);\n let width: number;\n let height: number;\n if (isGroup(node) && pageChildren?.length !== undefined) {\n const size = groupBoundsFromChildren(node as GroupNode, pageChildren ?? [], options);\n width = size.width;\n height = size.height;\n } else {\n width = simpleWidth(node);\n height = simpleHeight(node);\n }\n return { left, top, width, height, bottom: top + height, right: left + width };\n}\n\nexport function getNodeBoundsFromChildren(node: CanvasNode): {\n left: number;\n top: number;\n width: number;\n height: number;\n bottom: number;\n right: number;\n} {\n return getNodeBounds(node);\n}\n\nfunction absoluteBoundsRecur(node: CanvasNode, pageChildren: CanvasNode[], options?: BoundsOptions): { left: number; top: number; width: number; height: number } {\n const parent = pageChildren ? findParentGroup(pageChildren, node.id) : null;\n const b = getNodeBounds(node, pageChildren, options);\n if (!parent) return b;\n const parentAbs = absoluteBoundsRecur(parent, pageChildren, options);\n // Stack group: use resolved position (first child group-relative, rest chain) not stored left/top\n const isStackParent = isStackLayoutMode(parent.layoutMode);\n const inParentLeft = isStackParent\n ? (resolveStackGroupEffectivePositions(parent, pageChildren, options).get(node.id)?.left ?? b.left)\n : b.left;\n const inParentTop = isStackParent\n ? (resolveStackGroupEffectivePositions(parent, pageChildren, options).get(node.id)?.top ?? b.top)\n : b.top;\n return {\n left: parentAbs.left + inParentLeft,\n top: parentAbs.top + inParentTop,\n width: b.width,\n height: b.height,\n };\n}\n\nexport function getAbsoluteBounds(\n node: CanvasNode,\n pageChildren: CanvasNode[],\n options?: BoundsOptions\n): { left: number; top: number; width: number; height: number; bottom: number; right: number } {\n const { left, top, width, height } = absoluteBoundsRecur(node, pageChildren, options);\n return { left, top, width, height, bottom: top + height, right: left + width };\n}\n\nexport function absoluteToStorePosition(\n absoluteLeft: number,\n absoluteTop: number,\n nodeId: string,\n pageChildren: CanvasNode[]\n): { left: number; top: number } {\n const node = findNodeById(pageChildren, nodeId);\n if (!node) return { left: absoluteLeft, top: absoluteTop };\n const parent = findParentGroup(pageChildren, nodeId);\n if (!parent) return { left: absoluteLeft, top: absoluteTop };\n const parentAbs = getAbsoluteBounds(parent, pageChildren);\n const storeLeft = absoluteLeft - parentAbs.left;\n const inParentTop = absoluteTop - parentAbs.top;\n const isStack = isStackLayoutMode((parent as GroupNode).layoutMode);\n if (!isStack) return { left: storeLeft, top: inParentTop };\n const kids = (parent as GroupNode).children ?? [];\n const idx = kids.findIndex((c) => c.id === nodeId);\n if (idx < 0) return { left: storeLeft, top: inParentTop };\n if (idx === 0) return { left: storeLeft, top: inParentTop };\n const gap = (parent as GroupNode).stackSpacing ?? 8;\n const resolved = resolveStackGroupEffectivePositions(parent as GroupNode, pageChildren);\n const prev = kids[idx - 1];\n const prevResolved = resolved.get(prev.id);\n const prevHeight = getNodeBounds(prev, pageChildren).height;\n const prevBottom = prevResolved ? prevResolved.top + prevHeight : 0;\n const storeTop = inParentTop - prevBottom - gap;\n return { left: storeLeft, top: Math.max(0, storeTop) };\n}\n\nexport function getEffectiveGroupDisplay(_group: GroupNode): 'block' | 'flex' | 'grid' {\n return 'block';\n}\n\nexport function getEffectiveGroupFlexDirection(_group: GroupNode): 'row' | 'column' {\n return 'column';\n}\n\nexport function getGroupContentBox(\n group: GroupNode,\n pageChildren?: CanvasNode[],\n options?: BoundsOptions\n): { left: number; top: number; width: number; height: number } {\n const b = getNodeBounds(group, pageChildren, options);\n return { left: b.left, top: b.top, width: b.width, height: b.height };\n}\n\nexport function reflowPageFromRoot(_pageChildren: CanvasNode[]): Map<string, { left?: number; top?: number; width?: number; height?: number }> {\n return new Map();\n}\n\nexport function reflowFromGroup(_pageChildren: CanvasNode[], _groupId: string): Map<string, { left?: number; top?: number; width?: number; height?: number }> {\n return new Map();\n}\n\n/** Reflow stack group: recompute stored top/left so children form a vertical or horizontal stack. */\nexport function reflowStackGroup(\n group: GroupNode,\n pageChildren: CanvasNode[],\n spacing?: number\n): Map<string, { top?: number; left?: number }> {\n const mode = group.layoutMode ?? 'absolute';\n if (!isStackLayoutMode(mode)) return new Map();\n const gap = spacing ?? group.stackSpacing ?? 8;\n const kids = group.children ?? [];\n if (kids.length <= 1) return new Map();\n const updates = new Map<string, { top?: number; left?: number }>();\n if (isVerticalStackLayoutMode(mode)) {\n const firstLeft = getNodeLeft(kids[0]);\n let prevBottom = 0;\n for (let i = 0; i < kids.length; i++) {\n const child = kids[i];\n const b = getNodeBounds(child, pageChildren);\n const currentTop = getNodeTop(child);\n const effectiveTop = i === 0 ? currentTop : prevBottom + gap;\n prevBottom = effectiveTop + b.height;\n if (i > 0) updates.set(child.id, { top: 0, left: firstLeft });\n }\n } else {\n const firstTop = getNodeTop(kids[0]);\n let prevRight = 0;\n for (let i = 0; i < kids.length; i++) {\n const child = kids[i];\n const b = getNodeBounds(child, pageChildren);\n const currentLeft = getNodeLeft(child);\n const effectiveLeft = i === 0 ? currentLeft : prevRight + gap;\n prevRight = effectiveLeft + b.width;\n if (i > 0) updates.set(child.id, { left: 0, top: firstTop });\n }\n }\n return updates;\n}\n\n/** Fill text heights in tree using measureTextHeight so stack reflow uses correct heights (e.g. on use page when user types). Always re-measures text so current content drives layout. */\nfunction fillTextHeightsInTree(children: CanvasNode[]): CanvasNode[] {\n return children.map((node) => {\n if (isGroup(node)) {\n return { ...node, children: fillTextHeightsInTree((node as GroupNode).children ?? []) };\n }\n if (isElement(node)) {\n const el = node as CanvasElement;\n if (el.type === 'text') {\n const h = measureTextHeight(el);\n return { ...el, height: h };\n }\n return node;\n }\n return node;\n });\n}\n\n/** Fill text heights for use/preview so resolved stack positions use correct heights. Positions are chain-relative and resolved on read; no top overwriting. */\nexport function applyStackReflowToPageTree(children: CanvasNode[]): CanvasNode[] {\n return fillTextHeightsInTree(children);\n}\n\nlet _dragBoundsOverride: Record<string, { left: number; top: number; width: number; height: number }> | null = null;\nexport function setDragBoundsOverride(_overrides: Record<string, { left: number; top: number; width: number; height: number } | null> | null): void {\n _dragBoundsOverride = _overrides as Record<string, { left: number; top: number; width: number; height: number }> | null;\n}\nexport function clearDragBoundsOverride(): void {\n _dragBoundsOverride = null;\n}\n\nexport function computeGroupResizeLayoutKey(\n _group: GroupNode,\n _contentWidth: number,\n _contentHeight: number,\n _bucketPx?: number,\n _pageChildren?: CanvasNode[]\n): string {\n return '';\n}\n\nexport function getGroupLayoutSnapTargets(\n _group: GroupNode,\n _pageChildren: CanvasNode[],\n _box: { left: number; top: number; width: number; height: number }\n): { verticalTargets: number[]; horizontalTargets: number[] } {\n return { verticalTargets: [], horizontalTargets: [] };\n}\n\nexport function computeDiscreteGridSnappedBox(\n _group: GroupNode,\n _pageChildren: CanvasNode[],\n box: { left: number; top: number; width: number; height: number }\n): { snappedBox: { left: number; top: number; width: number; height: number }; cols: number; rows: number } {\n return { snappedBox: box, cols: 1, rows: 1 };\n}\n\nexport function getDiscreteGridSnapGuides(\n _group: GroupNode,\n _pageChildren: CanvasNode[],\n _anchorBox: { left: number; top: number; width: number; height: number },\n _cols: number,\n _rows: number,\n _fixedCellH?: number\n): { vertical: Array<{ position: number; active: boolean }>; horizontal: Array<{ position: number; active: boolean }> } {\n return { vertical: [], horizontal: [] };\n}\n\nexport function getOrderedFlexOrGridChildren(parent: GroupNode): CanvasNode[] {\n return parent.children;\n}\n\nexport function isGridGroupForResize(_group: GroupNode): boolean {\n return false;\n}\n\nexport function handleElementChange(\n _pageChildren: CanvasNode[],\n _changedElementId: string,\n _updateElementFn: (id: string, updates: Partial<CanvasElement>) => void\n): void {}\n\nexport function recalculateSiblingsBelow(\n _parent: GroupNode,\n _changedNodeId: string,\n _direction: 'vertical' | 'horizontal' = 'vertical',\n _pageChildren?: CanvasNode[],\n _pendingUpdates?: Map<string, { width?: number; height?: number; top?: number; left?: number }>,\n _options?: BoundsOptions\n): LayoutRecalcResult {\n return {\n updates: new Map(),\n boundsChanged: false,\n newBounds: { top: 0, bottom: 0, height: 0, width: 0 },\n };\n}\n\nexport function propagateLayoutChange(\n _pageChildren: CanvasNode[],\n _groupId: string,\n _updateElementFn: (id: string, updates: Partial<CanvasElement>) => void,\n _pendingUpdates: Map<string, { width?: number; height?: number; top?: number; left?: number }>\n): void {}\n\nexport function calculateFlatLayout(\n _group: GroupNode,\n _options?: { pageChildren?: CanvasNode[]; options?: BoundsOptions }\n): Map<string, { left?: number; top?: number; width?: number; height?: number }> {\n return new Map();\n}\n\n/** Returns true if the group's content extends below the given page height (group is measured with getAbsoluteBounds). */\nexport function detectOverflow(\n group: GroupNode,\n pageChildren: CanvasNode[],\n pageHeight: number,\n options?: BoundsOptions\n): boolean {\n const b = getAbsoluteBounds(group, pageChildren, options);\n return b.bottom > pageHeight;\n}\n\n/**\n * Returns root-level node ids whose absolute bottom extends beyond pageHeight.\n * Use this to know which nodes overflow the page; when using paginationConfig.flowRegionId,\n * only that group is considered the \"flow\" and the rest are fixed (header/footer).\n */\nexport function detectPageOverflow(\n pageChildren: CanvasNode[],\n pageHeight: number,\n options?: BoundsOptions\n): string[] {\n const overflowIds: string[] = [];\n for (const node of pageChildren) {\n const b = getAbsoluteBounds(node, pageChildren, options);\n if (b.bottom > pageHeight) overflowIds.push(node.id);\n }\n return overflowIds;\n}\n\n/**\n * Result of splitting flow-region content across pages: which child indices fit on the first page\n * and which overflow to the continuation page. Flow region is assumed to be a vertical stack.\n */\nexport interface FlowSplitResult {\n /** Number of flow-region children that fit on the first page. */\n firstPageCount: number;\n /** Index in flow-region children at which overflow starts (first item on continuation page). */\n overflowFromIndex: number;\n /** Absolute top of the flow region (so caller can compute available height = pageHeight - flowTop). */\n flowRegionTop: number;\n /** Absolute bottom of the flow region. */\n flowRegionBottom: number;\n}\n\n/**\n * Given a flow region (vertical stack group), compute how to split its children across page 1 and continuation.\n * Uses absolute bounds; available height on page 1 = pageHeight - flowRegionTop.\n * Returns firstPageCount = how many children fit on page 1, overflowFromIndex = first child index that goes to page 2.\n */\nexport function computeFlowSplit(\n flowGroup: GroupNode,\n pageChildren: CanvasNode[],\n pageHeight: number,\n options?: BoundsOptions\n): FlowSplitResult {\n const flowAbs = getAbsoluteBounds(flowGroup, pageChildren, options);\n const flowTop = flowAbs.top;\n const flowBottom = flowAbs.bottom;\n const availableHeight = Math.max(0, pageHeight - flowTop);\n const kids = flowGroup.children ?? [];\n const gap = flowGroup.stackSpacing ?? 8;\n if (kids.length === 0) {\n return { firstPageCount: 0, overflowFromIndex: 0, flowRegionTop: flowTop, flowRegionBottom: flowBottom };\n }\n let cumulative = 0;\n let firstPageCount = 0;\n for (let i = 0; i < kids.length; i++) {\n if (i > 0) cumulative += gap;\n const b = getNodeBounds(kids[i], pageChildren, options);\n const h = b.height;\n if (cumulative + h > availableHeight && firstPageCount === 0) {\n firstPageCount = 1;\n break;\n }\n cumulative += h;\n firstPageCount = i + 1;\n if (cumulative >= availableHeight) break;\n }\n return {\n firstPageCount,\n overflowFromIndex: Math.min(firstPageCount, kids.length),\n flowRegionTop: flowTop,\n flowRegionBottom: flowBottom,\n };\n}\n\n/** Deep-clone a node tree with new ids (for continuation page structure). */\nfunction cloneNodeWithNewIds(node: CanvasNode): CanvasNode {\n if (isGroup(node)) {\n const g = node as GroupNode;\n return {\n ...g,\n id: generateId('group'),\n children: (g.children ?? []).map(cloneNodeWithNewIds),\n };\n }\n const el = node as CanvasElement;\n return { ...el, id: generateId('el') };\n}\n\n/** Clone a group but replace its children with an empty array (for flow region placeholder on continuation page). */\nfunction cloneFlowRegionPlaceholder(flowGroup: GroupNode): GroupNode {\n return {\n ...flowGroup,\n id: generateId('group'),\n children: [],\n };\n}\n\n/**\n * Describes the root-level structure of a continuation page: cloned repeat nodes + flow region placeholder.\n * Use this when auto-pagination is enabled to build \"page 2\" from page 1.\n */\nexport interface ContinuationPageStructure {\n /** Root-level children for the continuation page: clones of repeat nodes (header/footer) + one flow region placeholder (empty group). */\n rootChildren: CanvasNode[];\n /** Id of the flow region placeholder group (so caller can fill it with overflow content or use for layout). */\n flowRegionPlaceholderId: string;\n}\n\n/**\n * Build the structure of a new (continuation) page from page 1's root children and pagination config.\n * - Repeats: nodes whose id is in repeatOnContinuationIds are cloned (new ids) and appear on the continuation page.\n * - Flow region: the group identified by flowRegionId is cloned once with empty children; overflow content is placed there at render/apply time.\n * Order on continuation page = same as on page 1: repeat nodes first (in page 1 order), then flow region placeholder.\n */\nexport function getContinuationPageStructure(\n page1RootChildren: CanvasNode[],\n config: PaginationConfig\n): ContinuationPageStructure {\n const repeatIds = new Set(config.repeatOnContinuationIds ?? []);\n const flowId = config.flowRegionId;\n const rootChildren: CanvasNode[] = [];\n let flowPlaceholder: GroupNode | null = null;\n for (const node of page1RootChildren) {\n if (repeatIds.has(node.id)) rootChildren.push(cloneNodeWithNewIds(node));\n if (flowId && node.id === flowId && isGroup(node)) {\n flowPlaceholder = cloneFlowRegionPlaceholder(node as GroupNode);\n }\n }\n if (flowPlaceholder) rootChildren.push(flowPlaceholder);\n return {\n rootChildren,\n flowRegionPlaceholderId: flowPlaceholder?.id ?? '',\n };\n}\n\nexport function reflowPageRootChildren(\n _children: CanvasNode[],\n _gap?: number,\n _direction?: 'vertical' | 'horizontal'\n): Map<string, { left?: number; top?: number }> {\n return new Map();\n}\n\nexport function constrainChildrenToGroupBounds(\n _group: GroupNode,\n _pageChildren: CanvasNode[]\n): Map<string, Partial<CanvasElement> & Partial<GroupNode>> {\n return new Map();\n}\n\nexport function normalizeGroupChildrenPositions(_children: CanvasNode[]): CanvasNode[] {\n return _children;\n}\n\nexport function applyLayoutToCanvas(canvas: CanvasState): CanvasState {\n return canvas;\n}\n","import { create } from 'zustand';\nimport { \n CanvasElement, \n CanvasState, \n CanvasNode, \n GroupNode,\n Position, \n Tool, \n CanvasPage,\n ProjectSettings,\n PageSettings,\n DynamicField,\n FieldGroup,\n FieldMapping,\n GroupLayoutMode,\n ThemeConfig,\n ThemeProperty,\n ThemeVariant,\n createDefaultElement,\n createDefaultGroup,\n createDynamicField,\n createEmptyThemeConfig,\n generateId,\n isGroup,\n isElement,\n flattenChildren,\n findNodeById,\n findParentGroup,\n updateNodeInTree,\n removeNodeFromTree,\n addNodeToTree,\n moveNodeInTree,\n getAllElementIds,\n isStackLayoutMode,\n} from '@/types/editor';\nimport { getNodeBounds, getNodeBoundsFromChildren, getAbsoluteBounds, constrainChildrenToGroupBounds, reflowPageFromRoot, reflowStackGroup } from '@/lib/layoutEngine';\nimport { clearMeasurementCache } from '@/lib/textMeasurement';\n\nconst defaultProjectSettings: ProjectSettings = {\n showGrid: false,\n snapToGrid: false,\n gridSize: 20,\n snapToGuides: true,\n snapThreshold: 5,\n};\n\nconst defaultPageSettings: PageSettings = {\n backgroundColor: '#ffffff',\n};\n\nconst createDefaultPage = (id: string, name: string): CanvasPage => ({\n id,\n name,\n children: [],\n settings: { ...defaultPageSettings },\n});\n\n// Reference to the Fabric canvas for PDF export\nlet fabricCanvasRef: import('fabric').Canvas | null = null;\n\nexport const setFabricCanvasRef = (canvas: import('fabric').Canvas | null) => {\n fabricCanvasRef = canvas;\n};\n\nexport const getFabricCanvasRef = () => fabricCanvasRef;\n\ninterface EditorState {\n canvas: CanvasState;\n /** Bumped on updateNode/updateElement so canvas sync effect runs when panel changes something */\n canvasUpdateVersion: number;\n activeTool: Tool;\n clipboard: CanvasNode[];\n collapsedGroups: Set<string>; // UI state for collapsed groups in layers panel\n showDynamicLabels: boolean;\n showSections: boolean; // Show section highlights on canvas\n hoveredGroupId: string | null; // Group ID being hovered in layers panel\n\n // Undo/redo\n history: CanvasState[];\n historyIndex: number;\n lastCommittedSignature: string;\n\n // Selectors (computed from pages)\n getCurrentPage: () => CanvasPage;\n getCurrentChildren: () => CanvasNode[];\n getCurrentElements: () => CanvasElement[]; // Flattened elements for Fabric.js\n\n // Actions\n setActiveTool: (tool: Tool) => void;\n setShowDynamicLabels: (show: boolean) => void;\n setShowSections: (show: boolean) => void;\n setHoveredGroupId: (groupId: string | null) => void;\n \n // Node operations (works for both elements and groups)\n addNode: (node: CanvasNode, parentId?: string | null, index?: number) => void;\n addElement: (element: CanvasElement, parentId?: string | null) => void;\n updateNode: (id: string, updates: Partial<CanvasNode>, options?: { recordHistory?: boolean; skipLayoutRecalc?: boolean }) => void;\n updateElement: (id: string, updates: Partial<CanvasElement>, options?: { recordHistory?: boolean; skipLayoutRecalc?: boolean; skipStackReflow?: boolean }) => void;\n deleteNodes: (ids: string[]) => void;\n deleteElements: (ids: string[]) => void;\n \n // Selection\n selectElements: (ids: string[], addToSelection?: boolean, expandGroups?: boolean) => void;\n clearSelection: () => void;\n \n // Canvas\n setZoom: (zoom: number) => void;\n setPan: (pan: Position) => void;\n setCanvasSize: (width: number, height: number) => void;\n // Z-order (now just reordering in children array)\n bringForward: (id: string) => void;\n sendBackward: (id: string) => void;\n moveNodeToIndex: (nodeId: string, parentId: string | null, newIndex: number) => void;\n \n // Clipboard\n copyElements: (ids: string[]) => void;\n cutElements: (ids: string[]) => void;\n pasteElements: (targetParentId?: string | null) => void;\n duplicateElements: (ids: string[]) => void;\n addEntryToStackGroup: (groupId: string) => void; // Add new entry (subgroup) to vertical-stack/horizontal-stack group\n duplicateEntryInStackGroup: (groupId: string, entryId: string) => void; // Duplicate entry as sibling in stack group\n \n // Element properties\n toggleElementVisibility: (id: string) => void;\n toggleElementLock: (id: string) => void;\n setGroupVisibility: (groupId: string, visible: boolean) => void; // Set visibility for group and all children\n \n // Form rendering (Use Template page style)\n setFormRenderPreset: (preset: string | undefined) => void;\n \n // Form binding mode\n setFormBindingMode: (mode: 'dynamic' | 'preset' | undefined, formDefId?: string, formDefName?: string) => void;\n\n // Dynamic Fields (centralized field management)\n addDynamicField: (field: DynamicField) => void;\n updateDynamicField: (id: string, updates: Partial<DynamicField>) => void;\n removeDynamicField: (id: string) => void;\n addFieldMapping: (fieldId: string, mapping: FieldMapping) => void;\n removeFieldMapping: (fieldId: string, elementId: string) => void;\n reorderFields: (fieldIds: string[]) => void;\n getDynamicFieldById: (id: string) => DynamicField | undefined;\n getFieldsForElement: (elementId: string) => DynamicField[];\n \n // Field Groups\n addFieldGroup: (group: FieldGroup) => void;\n updateFieldGroup: (id: string, updates: Partial<FieldGroup>) => void;\n removeFieldGroup: (id: string) => void;\n reorderFieldGroups: (groupIds: string[]) => void;\n \n // Theme system\n addThemeProperty: (property: ThemeProperty, currentValue: string) => void;\n updateThemeProperty: (propertyId: string, updates: Partial<ThemeProperty>) => void;\n removeThemeProperty: (propertyId: string) => void;\n addThemeVariant: (variant: ThemeVariant) => void;\n updateThemeVariant: (variantId: string, updates: Partial<ThemeVariant>) => void;\n removeThemeVariant: (variantId: string) => void;\n setThemeConfig: (config: ThemeConfig) => void;\n syncDefaultThemeProperty: (propertyId: string, value: string) => void;\n applyThemeAsOriginal: (variantId: string) => void;\n \n // Grouping\n groupNodes: (ids: string[], name?: string) => void;\n addEmptyGroup: (name?: string) => void;\n ungroupNodes: (groupId: string) => void;\n renameNode: (id: string, name: string) => void;\n moveNodeToGroup: (nodeId: string, groupId: string | null, index?: number) => void;\n toggleGroupCollapse: (groupId: string) => void;\n \n // Convenience aliases (element/group specific naming)\n groupElements: (ids: string[], name?: string) => void;\n ungroupElements: (groupId: string) => void;\n renameGroup: (groupId: string, name: string) => void;\n renameElement: (elementId: string, name: string) => void;\n \n // Settings\n updateProjectSettings: (settings: Partial<ProjectSettings>) => void;\n\n // Page management\n addPage: () => void;\n /** Add a new page and copy all elements/groups marked \"Static on new page\" from the current page. */\n addPageWithStaticElements: () => void;\n deletePage: (pageId: string) => void;\n duplicatePage: (pageId: string) => void;\n renamePage: (pageId: string, name: string) => void;\n setCurrentPage: (pageId: string) => void;\n reorderPages: (fromIndex: number, toIndex: number) => void;\n updatePageSettings: (pageId: string, settings: Partial<PageSettings>) => void;\n moveElementsToPage: (elementIds: string[], targetPageId: string) => void;\n moveElementsToPageWithPositions: (\n elementIds: string[],\n targetPageId: string,\n positionsById: Record<string, { left: number; top: number }>\n ) => void;\n \n // Reorder (used by AI operations)\n reorderElements: (pageId: string, orderedIds: string[]) => void;\n\n // Import/Load config\n loadConfig: (config: import('@/types/editor').TemplateConfig) => void;\n\n // Run full reflow on current page and apply to store (e.g. after fonts ready so group auto dimensions are correct)\n applyReflowToCurrentPage: () => void;\n /** Run vertical stack reflow for a group on a given page (e.g. after persisting section children so siblings shift). */\n reflowStackGroupInPage: (pageId: string, groupId: string) => void;\n\n // Reset canvas to initial state\n resetCanvas: () => void;\n\n commitHistory: () => void;\n undo: () => void;\n redo: () => void;\n}\n\nconst defaultPageId = 'page-1';\nconst defaultPage = createDefaultPage(defaultPageId, 'Page 1');\n\nconst initialCanvas: CanvasState = {\n width: 595,\n height: 842,\n pages: [defaultPage],\n currentPageId: defaultPageId,\n selectedIds: [],\n zoom: 1,\n pan: { x: 0, y: 0 },\n projectSettings: { ...defaultProjectSettings },\n dynamicFields: [],\n fieldGroups: [], // Groups are created on-demand by user\n formRenderPreset: undefined,\n formBindingMode: undefined,\n boundFormDefId: undefined,\n boundFormDefName: undefined,\n};\n\n// Helper function to deep clone a node with new unique IDs for all nested children\nconst cloneNodeWithNewIds = (node: CanvasNode): CanvasNode => {\n if (isGroup(node)) {\n return {\n ...node,\n id: generateId('group'),\n children: node.children.map(cloneNodeWithNewIds),\n };\n }\n // For elements, generate new ID based on type\n const prefix = node.type === 'text' ? 'text' : node.type === 'image' ? 'img' : node.type === 'shape' ? 'shape' : 'line';\n return {\n ...node,\n id: generateId(prefix),\n };\n};\n\n// Old cloneNode kept for backward compatibility (doesn't regenerate IDs)\nconst cloneNode = (node: CanvasNode): CanvasNode => {\n if (isGroup(node)) {\n return {\n ...node,\n children: node.children.map(cloneNode),\n };\n }\n return { ...node };\n};\n\nconst cloneCanvas = (canvas: CanvasState): CanvasState => ({\n width: canvas.width,\n height: canvas.height,\n zoom: canvas.zoom,\n pan: { ...canvas.pan },\n selectedIds: [...canvas.selectedIds],\n pages: canvas.pages.map((page) => ({\n ...page,\n children: page.children.map(cloneNode),\n settings: { ...page.settings },\n })),\n currentPageId: canvas.currentPageId,\n projectSettings: { ...canvas.projectSettings },\n dynamicFields: canvas.dynamicFields.map(f => {\n // Handle both new format (mappings) and old format (elementIds)\n if (f.mappings && Array.isArray(f.mappings)) {\n // New format: clone mappings array\n return { ...f, mappings: [...f.mappings] };\n } else if ((f as any).elementIds && Array.isArray((f as any).elementIds)) {\n // Old format: preserve elementIds\n return { ...f, elementIds: [...(f as any).elementIds] };\n } else {\n // Fallback: ensure mappings exists as empty array\n return { ...f, mappings: [] };\n }\n }),\n fieldGroups: canvas.fieldGroups.map(g => ({ ...g })),\n formRenderPreset: canvas.formRenderPreset,\n formBindingMode: canvas.formBindingMode,\n boundFormDefId: canvas.boundFormDefId,\n boundFormDefName: canvas.boundFormDefName,\n themeConfig: canvas.themeConfig ? {\n properties: canvas.themeConfig.properties.map(p => ({ ...p })),\n variants: canvas.themeConfig.variants.map(v => ({ ...v, values: { ...v.values } })),\n } : undefined,\n});\n\nconst sameIdSet = (a: string[], b: string[]): boolean => {\n if (a.length !== b.length) return false;\n const setA = new Set(a);\n for (const id of b) {\n if (!setA.has(id)) return false;\n }\n return true;\n};\n\nconst canvasSignature = (canvas: CanvasState) =>\n JSON.stringify({\n width: canvas.width,\n height: canvas.height,\n zoom: canvas.zoom,\n pan: canvas.pan,\n pages: canvas.pages,\n currentPageId: canvas.currentPageId,\n });\n\nconst MAX_HISTORY = 100;\n\n/** Timeout for deferred reflow after text-only panel edits. 0 = run on next tick for document-like responsiveness (no overlap). */\nlet deferredTextReflowTimeoutId: ReturnType<typeof setTimeout> | null = null;\nconst DEFERRED_TEXT_REFLOW_MS = 0;\n\n/** When true, bypass debounce for text reflow so layout runs immediately while user is typing (Word-like). Set by PageCanvas on text:editing:entered/exited. */\nlet isEditingTextRef: { current: boolean } = { current: false };\nexport function setEditingText(value: boolean) {\n isEditingTextRef.current = value;\n}\n\nconst commitFromState = (state: Pick<EditorState, 'history' | 'historyIndex' | 'lastCommittedSignature'>, canvas: CanvasState) => {\n const signature = canvasSignature(canvas);\n if (signature === state.lastCommittedSignature) {\n return {\n history: state.history,\n historyIndex: state.historyIndex,\n lastCommittedSignature: state.lastCommittedSignature,\n };\n }\n\n const base = state.history.slice(0, state.historyIndex + 1);\n base.push(cloneCanvas(canvas));\n const trimmed = base.length > MAX_HISTORY ? base.slice(base.length - MAX_HISTORY) : base;\n\n return {\n history: trimmed,\n historyIndex: trimmed.length - 1,\n lastCommittedSignature: signature,\n };\n};\n\n// Helper to get current page from canvas state\nconst getCurrentPageFromCanvas = (canvas: CanvasState): CanvasPage => {\n return canvas.pages.find(p => p.id === canvas.currentPageId) || canvas.pages[0];\n};\n\n// Helper to update current page's children\nconst updateCurrentPageChildren = (\n canvas: CanvasState,\n updater: (children: CanvasNode[]) => CanvasNode[]\n): CanvasState => {\n const updatedPages = canvas.pages.map(p =>\n p.id === canvas.currentPageId\n ? { ...p, children: updater(p.children) }\n : p\n );\n return { ...canvas, pages: updatedPages };\n};\n\nconst reflowFlexOrGridAfterReorder = (canvas: CanvasState): CanvasState => canvas;\n\nconst applyReflowToCanvas = (canvas: CanvasState): CanvasState => canvas;\n\n/** Apply vertical stack reflow to a group (and recursively to nested stack subgroups). Returns new page children. */\nfunction applyStackReflowToGroup(pageChildren: CanvasNode[], groupId: string): CanvasNode[] {\n const group = findNodeById(pageChildren, groupId) as GroupNode | null;\n if (!group || !isGroup(group) || !isStackLayoutMode(group.layoutMode)) return pageChildren;\n let next = pageChildren;\n // Reflow nested stack subgroups first so their bounds and positions are correct before we reflow this group\n const kids = group.children ?? [];\n for (const child of kids) {\n if (isGroup(child) && isStackLayoutMode(child.layoutMode)) {\n next = applyStackReflowToGroup(next, child.id);\n }\n }\n const groupAfter = findNodeById(next, groupId) as GroupNode | null;\n if (!groupAfter || !isGroup(groupAfter)) return next;\n const updates = reflowStackGroup(groupAfter, next, groupAfter.stackSpacing);\n if (updates.size === 0) return next;\n updates.forEach((u, id) => {\n next = updateNodeInTree(next, id, u);\n });\n return next;\n}\n\n// Page-space bounds for a root-level node (no parent). Used for paste/duplicate placement.\nconst getRootLevelBounds = (node: CanvasNode): { left: number; top: number; right: number; bottom: number } => {\n const b = getNodeBoundsFromChildren(node);\n if (isGroup(node)) {\n const l = node.left ?? 0;\n const t = node.top ?? 0;\n return { left: l + b.left, top: t + b.top, right: l + b.right, bottom: t + b.bottom };\n }\n return { left: b.left, top: b.top, right: b.right, bottom: b.bottom };\n};\n\nconst getBoundingBoxOfRoots = (nodes: CanvasNode[]): { left: number; top: number; right: number; bottom: number } => {\n if (nodes.length === 0) return { left: 0, top: 0, right: 0, bottom: 0 };\n const first = getRootLevelBounds(nodes[0]);\n let left = first.left, top = first.top, right = first.right, bottom = first.bottom;\n for (let i = 1; i < nodes.length; i++) {\n const b = getRootLevelBounds(nodes[i]);\n left = Math.min(left, b.left);\n top = Math.min(top, b.top);\n right = Math.max(right, b.right);\n bottom = Math.max(bottom, b.bottom);\n }\n return { left, top, right, bottom };\n};\n\nconst shiftNodeBy = (node: CanvasNode, dx: number, dy: number): CanvasNode => {\n if (isElement(node)) {\n return { ...node, left: (node.left ?? 0) + dx, top: (node.top ?? 0) + dy };\n }\n const g = node as GroupNode;\n return {\n ...g,\n left: (g.left ?? 0) + dx,\n top: (g.top ?? 0) + dy,\n children: g.children.map((c) => shiftNodeBy(c, dx, dy)),\n };\n};\n\nexport const useEditorStore = create<EditorState>((set, get) => ({\n canvas: initialCanvas,\n canvasUpdateVersion: 0,\n activeTool: 'select',\n clipboard: [],\n collapsedGroups: new Set(),\n showDynamicLabels: false,\n showSections: false,\n hoveredGroupId: null,\n\n history: [cloneCanvas(initialCanvas)],\n historyIndex: 0,\n lastCommittedSignature: canvasSignature(initialCanvas),\n\n // Selectors\n getCurrentPage: () => getCurrentPageFromCanvas(get().canvas),\n getCurrentChildren: () => getCurrentPageFromCanvas(get().canvas).children,\n getCurrentElements: () => flattenChildren(getCurrentPageFromCanvas(get().canvas).children),\n\n setActiveTool: (tool) => set({ activeTool: tool }),\n setShowDynamicLabels: (show) => set({ showDynamicLabels: show }),\n setShowSections: (show) => set({ showSections: show }),\n setHoveredGroupId: (groupId) => set({ hoveredGroupId: groupId }),\n\n setFormRenderPreset: (preset) =>\n set((state) => {\n const nextCanvas: CanvasState = { ...state.canvas, formRenderPreset: preset || undefined };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n setFormBindingMode: (mode, formDefId, formDefName) =>\n set((state) => {\n const nextCanvas: CanvasState = {\n ...state.canvas,\n formBindingMode: mode,\n boundFormDefId: formDefId,\n boundFormDefName: formDefName,\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n commitHistory: () =>\n set((state) => {\n const committed = commitFromState(state, state.canvas);\n return committed;\n }),\n\n undo: () =>\n set((state) => {\n if (state.historyIndex <= 0) return {};\n const nextIndex = state.historyIndex - 1;\n // Restore canvas exactly from history; do not re-run reflow so we keep the committed\n // dimensions (Fabric-based text heights, auto group heights) and avoid \"heighted\" sections.\n const nextCanvas = cloneCanvas(state.history[nextIndex]);\n return {\n canvas: nextCanvas,\n historyIndex: nextIndex,\n lastCommittedSignature: canvasSignature(nextCanvas),\n canvasUpdateVersion: state.canvasUpdateVersion + 1,\n };\n }),\n\n redo: () =>\n set((state) => {\n if (state.historyIndex >= state.history.length - 1) return {};\n const nextIndex = state.historyIndex + 1;\n const nextCanvas = cloneCanvas(state.history[nextIndex]);\n return {\n canvas: nextCanvas,\n historyIndex: nextIndex,\n lastCommittedSignature: canvasSignature(nextCanvas),\n canvasUpdateVersion: state.canvasUpdateVersion + 1,\n };\n }),\n\n // === NODE OPERATIONS ===\n \n addNode: (node, parentId = null, index) =>\n set((state) => {\n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n addNodeToTree(children, node, parentId, index)\n );\n nextCanvas.selectedIds = [node.id];\n const committed = commitFromState(state, nextCanvas);\n const out = { canvas: nextCanvas, ...committed };\n return out;\n }),\n\n addElement: (element, parentId = null) =>\n set((state) => {\n let elementToAdd = element;\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const pageChildren = currentPage?.children ?? [];\n if (parentId == null) {\n const PAGE_MARGIN = 48;\n const PAGE_GAP = 16;\n let maxBottom = PAGE_MARGIN;\n for (const node of pageChildren) {\n const b = getNodeBounds(node, pageChildren);\n if (b.bottom > maxBottom) maxBottom = b.bottom;\n }\n let newTop = maxBottom + PAGE_GAP;\n // Clamp top so the element stays visible within the canvas\n const pageHeight = state.canvas.height ?? 842;\n const elHeight = element.height ?? 50;\n const maxAllowedTop = Math.max(PAGE_MARGIN, pageHeight - elHeight - PAGE_MARGIN);\n if (newTop > maxAllowedTop) {\n newTop = Math.min(PAGE_MARGIN, maxAllowedTop);\n }\n elementToAdd = { ...element, left: PAGE_MARGIN, top: newTop };\n } else {\n const parent = findNodeById(pageChildren, parentId) as GroupNode | null;\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n elementToAdd = { ...elementToAdd, left: 0, top: 0 };\n }\n }\n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n addNodeToTree(children, elementToAdd, parentId)\n );\n nextCanvas.selectedIds = [elementToAdd.id];\n const committed = commitFromState(state, nextCanvas);\n const out = { canvas: nextCanvas, ...committed };\n return out;\n }),\n\n updateNode: (id, updates, options) =>\n set((state) => {\n const mergedUpdates = { ...updates };\n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n updateNodeInTree(children, id, mergedUpdates)\n );\n\n // When group width/height changes, constrain children to content box (div-like: wrap inside) and reflow\n const groupSizeChanged = (updates.width !== undefined || updates.height !== undefined) && !options?.skipLayoutRecalc;\n if (groupSizeChanged) {\n const currentPage = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n if (currentPage) {\n let updatedGroup = findNodeById(currentPage.children, id);\n if (updatedGroup && isGroup(updatedGroup)) {\n // Ensure group has explicit left/top (like a div) so contentBox is correct and children stay in place\n const g = updatedGroup as GroupNode;\n const pageChildrenForBounds = currentPage.children;\n if (g.left === undefined || g.top === undefined) {\n const b = getNodeBounds(g, pageChildrenForBounds);\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) =>\n updateNodeInTree(children, id, { left: b.left, top: b.top })\n );\n const pageAfter = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n updatedGroup = pageAfter ? findNodeById(pageAfter.children, id) : updatedGroup;\n }\n const childUpdates = constrainChildrenToGroupBounds(updatedGroup as GroupNode, pageChildrenForBounds);\n if (childUpdates.size > 0) {\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) => {\n let c = children;\n childUpdates.forEach((u, nodeId) => {\n c = updateNodeInTree(c, nodeId, u);\n });\n return c;\n });\n // Recursively constrain nested groups (child groups that got a new width)\n let nestedUpdates = childUpdates;\n while (nestedUpdates.size > 0) {\n const nextPage = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId)!;\n const nextPageChildren = nextPage.children;\n const nextNested = new Map<string, Partial<CanvasElement> & Partial<GroupNode>>();\n nestedUpdates.forEach((_u, nodeId) => {\n const node = findNodeById(nextPageChildren, nodeId);\n if (node && isGroup(node)) {\n const sub = constrainChildrenToGroupBounds(node as GroupNode, nextPageChildren);\n sub.forEach((up, id) => nextNested.set(id, up));\n }\n });\n if (nextNested.size === 0) break;\n nestedUpdates = nextNested;\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) => {\n let c = children;\n nextNested.forEach((u, nodeId) => {\n c = updateNodeInTree(c, nodeId, u);\n });\n return c;\n });\n }\n }\n }\n }\n }\n\n // When group is set to stack layout, normalize children to chain-relative (top 0, left = first's left) so subgroups align.\n const nextLayoutMode = (updates as Partial<GroupNode>).layoutMode;\n if (nextLayoutMode && isStackLayoutMode(nextLayoutMode)) {\n const currentPage = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n const group = currentPage ? (findNodeById(currentPage.children, id) as GroupNode | null) : null;\n if (currentPage && group && isGroup(group) && isStackLayoutMode(group.layoutMode)) {\n const kids = group.children ?? [];\n const gap = group.stackSpacing ?? 8;\n const pageChildren = currentPage.children;\n if (kids.length > 0) {\n // Sort by effective top (use absolute for consistent order when stored tops differ)\n const sortedKids = [...kids].sort(\n (a, b) => getAbsoluteBounds(a, pageChildren).top - getAbsoluteBounds(b, pageChildren).top\n );\n let next = updateNodeInTree(currentPage.children, id, { children: sortedKids });\n const groupSorted = findNodeById(next, id) as GroupNode;\n const orderedKids = groupSorted?.children ?? sortedKids;\n const firstLeft = typeof orderedKids[0]?.left === 'number' ? orderedKids[0].left : 0;\n let prevBottom = 0;\n for (let i = 0; i < orderedKids.length; i++) {\n const child = orderedKids[i];\n const currentTop = typeof child.top === 'number' ? child.top : 0;\n const height = getNodeBounds(child, next).height;\n if (i === 0) {\n prevBottom = currentTop + height;\n continue;\n }\n const storedTop = Math.max(0, currentTop - prevBottom - gap);\n next = updateNodeInTree(next, child.id, { top: storedTop, left: firstLeft });\n prevBottom = currentTop + height;\n }\n // Recursively reflow nested stack subgroups (e.g. Content with Title inside)\n next = applyStackReflowToGroup(next, id);\n nextCanvas = updateCurrentPageChildren(nextCanvas, () => next);\n }\n }\n }\n\n if (options?.recordHistory === false) {\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1 };\n }\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n updateElement: (id, updates, options) =>\n set((state) => {\n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n updateNodeInTree(children, id, updates)\n );\n\n // When any text/font/word-wrap property changes on a text element, persist measured height so\n // flex layout reflows (siblings reposition) and store stays in sync with Fabric textbox bounds\n const textRelatedProps = [\n 'text', 'fontSize', 'lineHeight', 'width', 'fontFamily', 'fontWeight',\n 'fontStyle', 'charSpacing', 'textAlign', 'splitByGrapheme', 'wordWrap',\n 'underline', 'linethrough',\n ];\n const textRelatedChange = textRelatedProps.some(prop => prop in updates);\n // When text-related props change, only persist height/width when caller provided them (e.g. from Fabric).\n // Do not overwrite with manual measurement - use Fabric bounds only.\n if (textRelatedChange && typeof updates.height === 'number') {\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) =>\n updateNodeInTree(children, id, { height: updates.height as number })\n );\n }\n\n // Check if this update affects layout (size/height/position or any text property that changes bounds)\n const layoutAffectingProps = [\n 'height', 'width', 'scaleX', 'scaleY', 'top', 'left',\n 'text', 'fontSize', 'lineHeight', 'fontFamily', 'fontWeight',\n 'fontStyle', 'charSpacing', 'textAlign', 'splitByGrapheme', 'wordWrap',\n 'underline', 'linethrough',\n ];\n const affectsLayout = layoutAffectingProps.some(prop => prop in updates);\n // Props that actually change size/position and should trigger stack reflow (reflow overwrites stored top for non-first children)\n const dimensionOrPositionProps = ['height', 'width', 'scaleX', 'scaleY', 'top', 'left'];\n const updatesDimensionOrPosition = dimensionOrPositionProps.some(prop => prop in updates);\n // Stack groups: reflow only when dimension/position changed; skip when caller says so (e.g. Fabric sync after font/size/weight change so top is not reset)\n const shouldReflowStack = affectsLayout && updatesDimensionOrPosition && !options?.skipStackReflow && !Object.keys(updates).every(k => k === 'left' || k === 'top');\n if (shouldReflowStack) {\n const currentPage = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n if (currentPage) {\n const parent = findParentGroup(currentPage.children, id);\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n const reflowed = applyStackReflowToGroup(currentPage.children, parent.id);\n nextCanvas = updateCurrentPageChildren(nextCanvas, () => reflowed);\n }\n }\n }\n // When user edits only left/top from the panel, don't run layout recalc or we overwrite their manual position\n const positionOnlyEdit = Object.keys(updates).every(k => k === 'left' || k === 'top');\n // Text-only from panel: skip heavy reflow so canvas updates instantly; run reflow after a short delay\n const onlyTextChange = affectsLayout && Object.keys(updates).every(k => k === 'text');\n if (onlyTextChange && options?.recordHistory !== false && !options?.skipLayoutRecalc) {\n if (isEditingTextRef.current) {\n get().applyReflowToCurrentPage();\n } else {\n clearTimeout(deferredTextReflowTimeoutId ?? undefined);\n deferredTextReflowTimeoutId = setTimeout(() => {\n get().applyReflowToCurrentPage();\n deferredTextReflowTimeoutId = null;\n }, DEFERRED_TEXT_REFLOW_MS);\n }\n }\n if (options?.recordHistory === false) {\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1 };\n }\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n deleteNodes: (ids) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n let nextChildren = currentPage.children;\n const rootLevelIds = new Set(currentPage.children.map((c) => c.id));\n\n // Collect ALL element IDs being deleted (including children of deleted groups)\n const allDeletedIds = new Set<string>();\n for (const id of ids) {\n allDeletedIds.add(id);\n const node = findNodeById(currentPage.children, id);\n if (node && isGroup(node)) {\n getAllElementIds(node.children).forEach(eid => allDeletedIds.add(eid));\n }\n }\n\n let deletedAnyRootLevel = false;\n for (const id of ids) {\n if (rootLevelIds.has(id)) deletedAnyRootLevel = true;\n nextChildren = removeNodeFromTree(nextChildren, id);\n }\n\n let nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = state.canvas.selectedIds.filter((id) => !ids.includes(id));\n\n // Clean up theme properties for deleted elements\n if (nextCanvas.themeConfig) {\n const remainingProps = nextCanvas.themeConfig.properties.filter(p => !allDeletedIds.has(p.elementId));\n if (remainingProps.length !== nextCanvas.themeConfig.properties.length) {\n const removedPropIds = new Set(\n nextCanvas.themeConfig.properties.filter(p => allDeletedIds.has(p.elementId)).map(p => p.id)\n );\n nextCanvas = {\n ...nextCanvas,\n themeConfig: {\n ...nextCanvas.themeConfig,\n properties: remainingProps,\n variants: nextCanvas.themeConfig.variants.map(v => {\n const values = { ...v.values };\n for (const pid of removedPropIds) delete values[pid];\n return { ...v, values };\n }),\n },\n };\n }\n }\n\n // Clean up dynamic fields for deleted elements\n if (nextCanvas.dynamicFields?.length) {\n nextCanvas = {\n ...nextCanvas,\n dynamicFields: nextCanvas.dynamicFields\n .map(f => ({\n ...f,\n mappings: (f.mappings ?? []).filter(m => !allDeletedIds.has(m.elementId)),\n }))\n .filter(f => (f.mappings ?? []).length > 0),\n };\n }\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n deleteElements: (ids) => get().deleteNodes(ids),\n\n selectElements: (ids, addToSelection = false, expandGroups = false) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const currentIds = state.canvas.selectedIds;\n\n // Expand selection to include all elements in the same group (only when expandGroups is true)\n const expandWithGroupMembers = (elementIds: string[]): string[] => {\n if (!expandGroups) return elementIds;\n \n const expandedIds = new Set<string>(elementIds);\n \n for (const id of elementIds) {\n const parent = findParentGroup(currentPage.children, id);\n if (parent) {\n // Add all element IDs in this group\n getAllElementIds(parent.children).forEach(elId => expandedIds.add(elId));\n }\n }\n \n return Array.from(expandedIds);\n };\n\n const expandedIds = expandWithGroupMembers(ids);\n\n if (addToSelection) {\n const newIds = expandedIds.filter((id) => !currentIds.includes(id));\n const removedIds = expandedIds.filter((id) => currentIds.includes(id));\n const finalIds = [...currentIds.filter((id) => !removedIds.includes(id)), ...newIds];\n const fullyExpandedIds = expandWithGroupMembers(finalIds);\n\n if (sameIdSet(fullyExpandedIds, currentIds)) return {};\n return { canvas: { ...state.canvas, selectedIds: fullyExpandedIds } };\n }\n\n if (sameIdSet(expandedIds, currentIds)) return {};\n return { canvas: { ...state.canvas, selectedIds: expandedIds } };\n }),\n\n clearSelection: () =>\n set((state) => (state.canvas.selectedIds.length ? { canvas: { ...state.canvas, selectedIds: [] } } : {})),\n\n updateProjectSettings: (settings) =>\n set((state) => {\n const nextCanvas: CanvasState = {\n ...state.canvas,\n projectSettings: { ...state.canvas.projectSettings, ...settings },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n setZoom: (zoom) =>\n set((state) => ({\n canvas: { ...state.canvas, zoom: Math.max(0.1, Math.min(10, zoom)) },\n })),\n\n setPan: (pan) => set((state) => ({ canvas: { ...state.canvas, pan } })),\n\n setCanvasSize: (width, height) =>\n set((state) => {\n const nextCanvas: CanvasState = {\n ...state.canvas,\n width,\n height,\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n\n // === Z-ORDER (reordering in tree) ===\n \n bringForward: (id) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const parent = findParentGroup(currentPage.children, id);\n const siblings = parent ? parent.children : currentPage.children;\n const index = siblings.findIndex(n => n.id === id);\n \n if (index < siblings.length - 1) {\n const newSiblings = [...siblings];\n [newSiblings[index], newSiblings[index + 1]] = [newSiblings[index + 1], newSiblings[index]];\n \n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) => {\n if (!parent) return newSiblings;\n return updateNodeInTree(children, parent.id, { children: newSiblings });\n });\n nextCanvas = reflowFlexOrGridAfterReorder(nextCanvas);\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n const page = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n if (page) {\n const reflowed = applyStackReflowToGroup(page.children, parent.id);\n nextCanvas = updateCurrentPageChildren(nextCanvas, () => reflowed);\n }\n }\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }\n return {};\n }),\n\n sendBackward: (id) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const parent = findParentGroup(currentPage.children, id);\n const siblings = parent ? parent.children : currentPage.children;\n const index = siblings.findIndex(n => n.id === id);\n \n if (index > 0) {\n const newSiblings = [...siblings];\n [newSiblings[index], newSiblings[index - 1]] = [newSiblings[index - 1], newSiblings[index]];\n \n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) => {\n if (!parent) return newSiblings;\n return updateNodeInTree(children, parent.id, { children: newSiblings });\n });\n nextCanvas = reflowFlexOrGridAfterReorder(nextCanvas);\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n const page = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n if (page) {\n const reflowed = applyStackReflowToGroup(page.children, parent.id);\n nextCanvas = updateCurrentPageChildren(nextCanvas, () => reflowed);\n }\n }\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }\n return {};\n }),\n\n moveNodeToIndex: (nodeId, parentId, newIndex) =>\n set((state) => {\n let nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n moveNodeInTree(children, nodeId, parentId, newIndex)\n );\n \n nextCanvas = reflowFlexOrGridAfterReorder(nextCanvas);\n\n // Stack groups: store chain-relative positions; set left/top to 0 when moving into a stack group\n if (parentId) {\n const currentPage = nextCanvas.pages.find((p) => p.id === nextCanvas.currentPageId);\n if (currentPage) {\n const parent = findNodeById(currentPage.children, parentId) as GroupNode | null;\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) =>\n updateNodeInTree(children, nodeId, { left: 0, top: 0 })\n );\n }\n }\n }\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n // === CLIPBOARD ===\n \n copyElements: (ids) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const nodesToCopy: CanvasNode[] = [];\n \n for (const id of ids) {\n const node = findNodeById(currentPage.children, id);\n if (node) nodesToCopy.push(cloneNode(node));\n }\n \n return { clipboard: nodesToCopy };\n }),\n\n cutElements: (ids) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const nodesToCut: CanvasNode[] = [];\n \n for (const id of ids) {\n const node = findNodeById(currentPage.children, id);\n if (node) nodesToCut.push(cloneNode(node));\n }\n \n let nextChildren = currentPage.children;\n for (const id of ids) {\n nextChildren = removeNodeFromTree(nextChildren, id);\n }\n \n const nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = [];\n \n const committed = commitFromState(state, nextCanvas);\n return { \n canvas: nextCanvas, \n clipboard: nodesToCut,\n ...committed \n };\n }),\n\n pasteElements: (targetParentId = null) =>\n set((state) => {\n if (state.clipboard.length === 0) return {};\n \n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const targetParent = targetParentId ? findNodeById(currentPage.children, targetParentId) : null;\n\n const timestamp = Date.now();\n const cloneWithNewIds = (node: CanvasNode, index: number, posOffset: number): CanvasNode => {\n const newId = `${node.id}-paste-${timestamp}-${index}`;\n if (isGroup(node)) {\n return {\n ...node,\n id: newId,\n left: (node.left ?? 0) + posOffset,\n top: (node.top ?? 0) + posOffset,\n children: node.children.map((child, i) => cloneWithNewIds(child, i, 0)),\n };\n }\n return {\n ...node,\n id: newId,\n left: (node.left ?? 0) + posOffset,\n top: (node.top ?? 0) + posOffset,\n };\n };\n \n let newNodes = state.clipboard.map((node, i) => cloneWithNewIds(node, i, 0));\n const isPastingIntoStack = targetParent && isGroup(targetParent) && isStackLayoutMode(targetParent.layoutMode);\n if (isPastingIntoStack) {\n newNodes = newNodes.map((n) => ({ ...n, left: 0, top: 0 } as CanvasNode));\n } else {\n const PAGE_MARGIN = 48;\n const PAGE_GAP = 16;\n let maxBottom = PAGE_MARGIN;\n for (const node of currentPage.children) {\n const b = getNodeBounds(node, currentPage.children);\n if (b.bottom > maxBottom) maxBottom = b.bottom;\n }\n const bbox = getBoundingBoxOfRoots(newNodes);\n const shiftX = PAGE_MARGIN - bbox.left;\n const shiftY = maxBottom + PAGE_GAP - bbox.top;\n newNodes = newNodes.map((n) => shiftNodeBy(n, shiftX, shiftY));\n }\n\n let nextChildren = currentPage.children;\n for (const node of newNodes) {\n nextChildren = addNodeToTree(nextChildren, node, targetParentId);\n }\n \n let nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = newNodes.map(n => n.id);\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n duplicateElements: (ids) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n let nextChildren = currentPage.children;\n const newNodes: CanvasNode[] = [];\n const duplicateAtPageLevel: string[] = []; // ids duplicated at page level (no flex parent)\n\n for (const id of ids) {\n const node = findNodeById(nextChildren, id);\n if (!node) continue;\n const parent = findParentGroup(nextChildren, id);\n let cloned = cloneNodeWithNewIds(node);\n if (parent && isGroup(parent) && isStackLayoutMode(parent.layoutMode)) {\n cloned = { ...cloned, left: 0, top: 0 } as CanvasNode;\n }\n duplicateAtPageLevel.push(cloned.id);\n newNodes.push(cloned);\n\n const parentId = parent ? parent.id : null;\n const index = parent\n ? parent.children.findIndex((c) => c.id === id)\n : nextChildren.findIndex((c) => c.id === id);\n const insertIndex = index >= 0 ? index + 1 : undefined;\n nextChildren = addNodeToTree(nextChildren, cloned, parentId, insertIndex);\n }\n\n // Place page-level duplicates below existing content so they never overlap (document-like)\n const pageLevelIds = new Set(duplicateAtPageLevel);\n if (pageLevelIds.size > 0) {\n const PAGE_MARGIN = 48;\n const PAGE_GAP = 16;\n let maxBottom = PAGE_MARGIN;\n for (const node of currentPage.children) {\n const b = getNodeBounds(node, currentPage.children);\n if (b.bottom > maxBottom) maxBottom = b.bottom;\n }\n const pageLevelClones = newNodes.filter((n) => pageLevelIds.has(n.id));\n const bbox = getBoundingBoxOfRoots(pageLevelClones);\n const shiftX = PAGE_MARGIN - bbox.left;\n const shiftY = maxBottom + PAGE_GAP - bbox.top;\n nextChildren = nextChildren.map((node) =>\n pageLevelIds.has(node.id) ? shiftNodeBy(node, shiftX, shiftY) : node\n );\n }\n\n let nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = newNodes.map((n) => n.id);\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n addEntryToStackGroup: (_groupId) =>\n set(() => {\n // Stack/flex/grid layout removed; groups are plain Fabric groups\n return {};\n }),\n\n duplicateEntryInStackGroup: (_groupId, _entryId) =>\n set(() => {\n // Stack/flex/grid layout removed; groups are plain Fabric groups\n return {};\n }),\n\n // === ELEMENT PROPERTIES ===\n \n toggleElementVisibility: (id) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const node = findNodeById(currentPage.children, id);\n if (!node) return {};\n \n const newVisible = node.visible === false ? true : false;\n const nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n updateNodeInTree(children, id, { visible: newVisible })\n );\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n toggleElementLock: (id) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const node = findNodeById(currentPage.children, id);\n if (!node || isGroup(node)) return {};\n \n const nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n updateNodeInTree(children, id, { \n selectable: !node.selectable, \n evented: !node.evented \n })\n );\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n setGroupVisibility: (groupId, visible) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const group = findNodeById(currentPage.children, groupId) as GroupNode | null;\n if (!group || !isGroup(group)) return {};\n \n // Collect all node IDs to update (group + all children recursively, including nested groups)\n const nodeIds: string[] = [groupId];\n \n const collectChildren = (node: CanvasNode) => {\n // Add this node's ID\n nodeIds.push(node.id);\n \n // If it's a group, recursively collect all its children (elements and subgroups)\n if (isGroup(node)) {\n node.children.forEach(child => {\n collectChildren(child); // Recursive call for nested groups\n });\n }\n };\n \n // Start collecting from the group's direct children\n group.children.forEach(child => {\n collectChildren(child);\n });\n \n // Update all nodes in a single batch operation\n // Visibility changes shouldn't affect positions, so we update directly without triggering layout recalculation\n let nextCanvas = state.canvas;\n nodeIds.forEach(id => {\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) =>\n updateNodeInTree(children, id, { visible })\n );\n });\n \n // Don't commit history for visibility-only changes to avoid triggering unnecessary syncs\n // Visibility is a rendering property, not a layout property\n return { canvas: nextCanvas };\n }),\n\n // === DYNAMIC FIELDS (Centralized) ===\n \n addDynamicField: (field) =>\n set((state) => {\n // Check if field with same ID already exists - don't add duplicates\n const existingField = state.canvas.dynamicFields.find(f => f.id === field.id);\n if (existingField) {\n // Field already exists, don't add duplicate\n console.warn(`Dynamic field with ID \"${field.id}\" already exists`);\n return {};\n }\n \n const nextCanvas = {\n ...state.canvas,\n dynamicFields: [...state.canvas.dynamicFields, field],\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n updateDynamicField: (id, updates) =>\n set((state) => {\n const nextCanvas = {\n ...state.canvas,\n dynamicFields: state.canvas.dynamicFields.map(f =>\n f.id === id ? { ...f, ...updates } : f\n ),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n removeDynamicField: (id) =>\n set((state) => {\n const nextCanvas = {\n ...state.canvas,\n dynamicFields: state.canvas.dynamicFields.filter(f => f.id !== id),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n addFieldMapping: (fieldId, mapping) =>\n set((state) => {\n const nextCanvas = {\n ...state.canvas,\n dynamicFields: state.canvas.dynamicFields.map(f => {\n if (f.id !== fieldId) return f;\n const mappings = (f.mappings && Array.isArray(f.mappings)) ? f.mappings : [];\n // Avoid duplicate: same elementId + targetProperty\n if (mappings.some(m => m.elementId === mapping.elementId && m.targetProperty === mapping.targetProperty)) {\n return f;\n }\n return { ...f, mappings: [...mappings, mapping] };\n }),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n removeFieldMapping: (fieldId, elementId) =>\n set((state) => {\n const nextCanvas = {\n ...state.canvas,\n dynamicFields: state.canvas.dynamicFields.map(f => {\n if (f.id !== fieldId) return f;\n // Handle both formats\n if (f.mappings && Array.isArray(f.mappings)) {\n return { ...f, mappings: f.mappings.filter(m => m.elementId !== elementId) };\n } else if ((f as any).elementIds && Array.isArray((f as any).elementIds)) {\n // Old format: remove from elementIds\n return { ...f, elementIds: (f as any).elementIds.filter((id: string) => id !== elementId) };\n } else {\n return f;\n }\n }),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n reorderFields: (fieldIds) =>\n set((state) => {\n const fieldMap = new Map(state.canvas.dynamicFields.map(f => [f.id, f]));\n const reordered = fieldIds\n .map(id => fieldMap.get(id))\n .filter((f): f is DynamicField => f !== undefined)\n .map((f, i) => ({ ...f, order: i }));\n \n // Add any fields not in the list at the end\n const missingFields = state.canvas.dynamicFields\n .filter(f => !fieldIds.includes(f.id))\n .map((f, i) => ({ ...f, order: reordered.length + i }));\n \n const nextDynamicFields = [...reordered, ...missingFields];\n const nextCanvas = {\n ...state.canvas,\n dynamicFields: nextDynamicFields,\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n getDynamicFieldById: (id) => {\n return useEditorStore.getState().canvas.dynamicFields.find(f => f.id === id);\n },\n\n getFieldsForElement: (elementId) => {\n return useEditorStore.getState().canvas.dynamicFields.filter(f => {\n // Handle both formats\n if (f.mappings && Array.isArray(f.mappings)) {\n return f.mappings.some(m => m.elementId === elementId);\n } else if ((f as any).elementIds && Array.isArray((f as any).elementIds)) {\n // Old format: check elementIds\n return (f as any).elementIds.includes(elementId);\n }\n return false;\n });\n },\n\n // === FIELD GROUPS ===\n \n addFieldGroup: (group) =>\n set((state) => {\n // Check if group with same ID already exists - don't add duplicates\n const existingGroup = state.canvas.fieldGroups.find(g => g.id === group.id);\n if (existingGroup) {\n // Group already exists, don't add duplicate\n return {};\n }\n \n const nextCanvas = {\n ...state.canvas,\n fieldGroups: [...state.canvas.fieldGroups, group],\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n updateFieldGroup: (id, updates) =>\n set((state) => {\n const nextCanvas = {\n ...state.canvas,\n fieldGroups: state.canvas.fieldGroups.map(g =>\n g.id === id ? { ...g, ...updates } : g\n ),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n removeFieldGroup: (id) =>\n set((state) => {\n // Also clear group reference from fields\n const nextCanvas = {\n ...state.canvas,\n fieldGroups: state.canvas.fieldGroups.filter(g => g.id !== id),\n dynamicFields: state.canvas.dynamicFields.map(f =>\n f.group === id ? { ...f, group: undefined } : f\n ),\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n reorderFieldGroups: (groupIds) =>\n set((state) => {\n const groupMap = new Map(state.canvas.fieldGroups.map(g => [g.id, g]));\n const reordered = groupIds\n .map(id => groupMap.get(id))\n .filter((g): g is FieldGroup => g !== undefined)\n .map((g, i) => ({ ...g, order: i }));\n \n const missingGroups = state.canvas.fieldGroups\n .filter(g => !groupIds.includes(g.id))\n .map((g, i) => ({ ...g, order: reordered.length + i }));\n \n const nextCanvas = {\n ...state.canvas,\n fieldGroups: [...reordered, ...missingGroups],\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n // === THEME SYSTEM ===\n\n addThemeProperty: (property, currentValue) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig ?? createEmptyThemeConfig();\n // Don't add duplicate\n if (themeConfig.properties.some(p => p.id === property.id)) return {};\n const updatedProperties = [...themeConfig.properties, property];\n // Set current value in default variant\n const updatedVariants = themeConfig.variants.map(v => \n v.isDefault ? { ...v, values: { ...v.values, [property.id]: currentValue } } : v\n );\n const nextCanvas = {\n ...state.canvas,\n themeConfig: { properties: updatedProperties, variants: updatedVariants },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n updateThemeProperty: (propertyId, updates) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n const updatedProperties = themeConfig.properties.map(p =>\n p.id === propertyId ? { ...p, ...updates } : p\n );\n const nextCanvas = {\n ...state.canvas,\n themeConfig: { ...themeConfig, properties: updatedProperties },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n removeThemeProperty: (propertyId) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n const updatedProperties = themeConfig.properties.filter(p => p.id !== propertyId);\n const updatedVariants = themeConfig.variants.map(v => {\n const { [propertyId]: _, ...rest } = v.values;\n return { ...v, values: rest };\n });\n const nextCanvas = {\n ...state.canvas,\n themeConfig: { ...themeConfig, properties: updatedProperties, variants: updatedVariants },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n addThemeVariant: (variant) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig ?? createEmptyThemeConfig();\n const nextCanvas = {\n ...state.canvas,\n themeConfig: { ...themeConfig, variants: [...themeConfig.variants, variant] },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n updateThemeVariant: (variantId, updates) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n const nextCanvas = {\n ...state.canvas,\n themeConfig: {\n ...themeConfig,\n variants: themeConfig.variants.map(v => v.id === variantId ? { ...v, ...updates } : v),\n },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n removeThemeVariant: (variantId) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n // Don't allow removing default variant\n const variant = themeConfig.variants.find(v => v.id === variantId);\n if (variant?.isDefault) return {};\n const nextCanvas = {\n ...state.canvas,\n themeConfig: {\n ...themeConfig,\n variants: themeConfig.variants.filter(v => v.id !== variantId),\n },\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n setThemeConfig: (config) =>\n set((state) => {\n const nextCanvas = { ...state.canvas, themeConfig: config };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n syncDefaultThemeProperty: (propertyId, value) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n const nextCanvas = {\n ...state.canvas,\n themeConfig: {\n ...themeConfig,\n variants: themeConfig.variants.map(v =>\n v.isDefault ? { ...v, values: { ...v.values, [propertyId]: value } } : v\n ),\n },\n };\n // Don't commit history for default theme sync (it's automatic)\n return { canvas: nextCanvas };\n }),\n\n applyThemeAsOriginal: (variantId) =>\n set((state) => {\n const themeConfig = state.canvas.themeConfig;\n if (!themeConfig) return {};\n const variant = themeConfig.variants.find(v => v.id === variantId);\n if (!variant || variant.isDefault) return {};\n\n const PAGE_BG_ID = '__pageBackground__';\n let updatedPages = [...state.canvas.pages];\n\n for (const prop of themeConfig.properties) {\n const value = variant.values[prop.id];\n if (value === undefined) continue;\n\n if (prop.elementId === PAGE_BG_ID) {\n if (prop.targetProperty === 'backgroundColor') {\n updatedPages = updatedPages.map(p => ({\n ...p,\n settings: { ...p.settings, backgroundColor: value },\n }));\n } else if (prop.targetProperty === 'backgroundGradient' && prop.svgColorKey) {\n const stopMatch = prop.svgColorKey.match(/^stop:(\\d+)$/);\n if (stopMatch) {\n const stopIndex = parseInt(stopMatch[1], 10);\n updatedPages = updatedPages.map(p => {\n if (!p.settings.backgroundGradient?.stops[stopIndex]) return p;\n return {\n ...p,\n settings: {\n ...p.settings,\n backgroundGradient: {\n ...p.settings.backgroundGradient!,\n stops: p.settings.backgroundGradient!.stops.map((s, i) =>\n i === stopIndex ? { ...s, color: value } : s\n ),\n },\n },\n };\n });\n }\n }\n continue;\n }\n\n const updateNode = (node: CanvasNode): CanvasNode => {\n if (isGroup(node)) {\n return { ...node, children: node.children.map(updateNode) };\n }\n const el = node as CanvasElement;\n if (el.id !== prop.elementId) return node;\n if (prop.svgColorKey && el.svgColorMap) {\n return { ...el, svgColorMap: { ...el.svgColorMap, [prop.svgColorKey]: value } };\n }\n return { ...el, [prop.targetProperty]: value };\n };\n\n updatedPages = updatedPages.map(p => ({\n ...p,\n children: p.children.map(updateNode),\n }));\n }\n\n // Swap: default gets variant's values, variant gets old default's values + swaps name/swatch\n const defaultVariant = themeConfig.variants.find(v => v.isDefault);\n const oldDefaultValues = defaultVariant ? { ...defaultVariant.values } : {};\n const oldDefaultSwatch = defaultVariant?.swatchColor ?? '#ffffff';\n const oldDefaultName = defaultVariant?.name ?? 'Default';\n\n const updatedThemeConfig = {\n ...themeConfig,\n variants: themeConfig.variants.map(v => {\n if (v.isDefault) return { ...v, values: { ...variant.values }, swatchColor: variant.swatchColor, name: variant.name };\n if (v.id === variantId) return { ...v, values: oldDefaultValues, swatchColor: oldDefaultSwatch, name: oldDefaultName };\n return v;\n }),\n };\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: updatedPages,\n themeConfig: updatedThemeConfig,\n };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n // === GROUPING ===\n \n groupNodes: (ids, name) =>\n set((state) => {\n if (ids.length < 2) return {};\n \n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const idSet = new Set(ids);\n\n // When user selects a group, selection includes the group + all descendants.\n // Only group the top-level selected nodes (exclude any id whose ancestor is also selected).\n const topLevelIds = ids.filter((id) => {\n let parent = findParentGroup(currentPage.children, id);\n while (parent) {\n if (idSet.has(parent.id)) return false;\n parent = findParentGroup(currentPage.children, parent.id);\n }\n return true;\n });\n\n if (topLevelIds.length < 2) return {};\n\n const nodesToGroup: CanvasNode[] = [];\n for (const id of topLevelIds) {\n const node = findNodeById(currentPage.children, id);\n if (node) nodesToGroup.push(cloneNode(node));\n }\n \n if (nodesToGroup.length < 2) return {};\n \n // Find common parent of all nodes to group\n const parentGroups = topLevelIds.map(id => findParentGroup(currentPage.children, id));\n const commonParent = parentGroups.length > 0 && parentGroups.every(p => p?.id === parentGroups[0]?.id)\n ? parentGroups[0]\n : null;\n \n // Find the index where to insert the new group (use first node's index in its parent)\n let insertIndex: number | undefined = undefined;\n if (commonParent && topLevelIds.length > 0) {\n const firstNodeId = topLevelIds[0];\n const firstNodeIndex = commonParent.children.findIndex(child => child.id === firstNodeId);\n if (firstNodeIndex >= 0) {\n insertIndex = firstNodeIndex;\n }\n }\n \n // Sort by visual order (top ascending) so first in array = topmost on canvas. Use absolute bounds so stack children (stored 0,0) sort correctly.\n const pageChildrenForBounds = currentPage.children;\n nodesToGroup.sort((a, b) => {\n const aTop = getAbsoluteBounds(a, pageChildrenForBounds).top ?? 0;\n const bTop = getAbsoluteBounds(b, pageChildrenForBounds).top ?? 0;\n return aTop - bTop;\n });\n\n // Bounding box and relative positions from absolute bounds so subgroups with stored 0,0 (e.g. in a stack) get correct placement\n const pageChildren = currentPage.children;\n let groupLeft = Infinity, groupTop = Infinity;\n let groupRight = -Infinity, groupBottom = -Infinity;\n for (const node of nodesToGroup) {\n const b = getAbsoluteBounds(node, pageChildren);\n groupLeft = Math.min(groupLeft, b.left ?? 0);\n groupTop = Math.min(groupTop, b.top ?? 0);\n groupRight = Math.max(groupRight, b.right ?? 0);\n groupBottom = Math.max(groupBottom, b.bottom ?? 0);\n }\n if (groupLeft === Infinity) groupLeft = 0;\n if (groupTop === Infinity) groupTop = 0;\n const groupWidth = groupRight > -Infinity ? Math.max(0, groupRight - groupLeft) : 100;\n const groupHeight = groupBottom > -Infinity ? Math.max(0, groupBottom - groupTop) : 100;\n\n // Convert child positions to relative to the new group (use absolute bounds so stack children with stored 0,0 get correct rel top/left)\n const childrenWithRelativePos = nodesToGroup.map((node) => {\n const b = getAbsoluteBounds(node, pageChildren);\n const relLeft = (b.left ?? 0) - groupLeft;\n const relTop = (b.top ?? 0) - groupTop;\n return isElement(node)\n ? { ...node, left: relLeft, top: relTop }\n : { ...node, left: relLeft, top: relTop } as GroupNode;\n });\n\n const groupId = generateId('group');\n const newGroup = createDefaultGroup({\n id: groupId,\n name: name || `Group ${state.collapsedGroups.size + 1}`,\n children: childrenWithRelativePos,\n left: groupLeft,\n top: groupTop,\n width: groupWidth,\n height: groupHeight,\n });\n\n let nextChildren = currentPage.children;\n for (const id of topLevelIds) {\n nextChildren = removeNodeFromTree(nextChildren, id);\n }\n const parentId = commonParent?.id || null;\n nextChildren = addNodeToTree(nextChildren, newGroup, parentId, insertIndex);\n\n const nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = [groupId]; // Select the new group\n \n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n addEmptyGroup: (name?: string) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const pageChildren = currentPage?.children ?? [];\n const PAGE_MARGIN = 48;\n const PAGE_GAP = 16;\n const MIN_EMPTY_GROUP_SIZE = 48;\n let maxBottom = PAGE_MARGIN;\n for (const node of pageChildren) {\n const b = getNodeBounds(node, pageChildren);\n if (b.bottom > maxBottom) maxBottom = b.bottom;\n }\n const groupId = generateId('group');\n const newGroup = createDefaultGroup({\n id: groupId,\n name: name ?? `Group ${state.collapsedGroups.size + 1}`,\n left: PAGE_MARGIN,\n top: maxBottom + PAGE_GAP,\n width: Math.max(MIN_EMPTY_GROUP_SIZE, 120),\n height: Math.max(MIN_EMPTY_GROUP_SIZE, 80),\n children: [],\n ...(name === 'Section' ? { backgroundColor: '#e2e8f0' } : {}),\n });\n const nextChildren = addNodeToTree(pageChildren, newGroup, null);\n const nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = [groupId];\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, canvasUpdateVersion: state.canvasUpdateVersion + 1, ...committed };\n }),\n\n ungroupNodes: (groupId) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const group = findNodeById(currentPage.children, groupId);\n \n if (!group || !isGroup(group)) return {};\n \n // Get parent of the group\n const parentGroup = findParentGroup(currentPage.children, groupId);\n const parentId = parentGroup?.id || null;\n \n // Find index of group in parent\n const siblings = parentGroup ? parentGroup.children : currentPage.children;\n const groupIndex = siblings.findIndex(n => n.id === groupId);\n \n let nextChildren = removeNodeFromTree(currentPage.children, groupId);\n const parentAbs = parentId ? getAbsoluteBounds(findNodeById(nextChildren, parentId)!, nextChildren) : null;\n\n // Iterate forward (0 to length-1) so order is preserved: first in stack = first in parent (top to bottom)\n for (let i = 0; i < group.children.length; i++) {\n const child = group.children[i];\n const abs = getAbsoluteBounds(child, currentPage.children);\n const storeLeft = parentAbs ? abs.left - parentAbs.left : abs.left;\n const storeTop = parentAbs ? abs.top - parentAbs.top : abs.top;\n const childWithPos = isElement(child)\n ? { ...child, left: storeLeft, top: storeTop }\n : { ...child, left: storeLeft, top: storeTop } as GroupNode;\n nextChildren = addNodeToTree(nextChildren, childWithPos, parentId, groupIndex + i);\n }\n \n const nextCanvas = updateCurrentPageChildren(state.canvas, () => nextChildren);\n nextCanvas.selectedIds = getAllElementIds(group.children);\n \n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n renameNode: (id, name) =>\n set((state) => {\n const nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n updateNodeInTree(children, id, { name })\n );\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n moveNodeToGroup: (nodeId, groupId, index) =>\n set((state) => {\n const nextCanvas = updateCurrentPageChildren(state.canvas, (children) =>\n moveNodeInTree(children, nodeId, groupId, index ?? 0)\n );\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n toggleGroupCollapse: (groupId) =>\n set((state) => {\n // Update both UI state Set and the group's collapsed property in canvas\n const newCollapsedSet = new Set(state.collapsedGroups);\n const isCollapsed = newCollapsedSet.has(groupId);\n \n if (isCollapsed) {\n newCollapsedSet.delete(groupId);\n } else {\n newCollapsedSet.add(groupId);\n }\n \n // Also update the group's collapsed property in canvas for persistence\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n let nextCanvas = state.canvas;\n const group = findNodeById(currentPage.children, groupId);\n if (group && isGroup(group)) {\n nextCanvas = updateCurrentPageChildren(nextCanvas, (children) =>\n updateNodeInTree(children, groupId, { collapsed: !isCollapsed })\n );\n }\n \n return { \n collapsedGroups: newCollapsedSet,\n canvas: nextCanvas,\n };\n }),\n\n // Convenience aliases\n groupElements: (ids, name) => get().groupNodes(ids, name),\n ungroupElements: (groupId) => get().ungroupNodes(groupId),\n renameGroup: (groupId, name) => get().renameNode(groupId, name),\n renameElement: (elementId, name) => get().renameNode(elementId, name),\n\n // === PAGE MANAGEMENT ===\n \n addPage: () =>\n set((state) => {\n const pageNumber = state.canvas.pages.length + 1;\n const newPageId = `page-${Date.now()}`;\n const newPage = createDefaultPage(newPageId, `Page ${pageNumber}`);\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: [...state.canvas.pages, newPage],\n currentPageId: newPageId,\n selectedIds: [],\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n addPageWithStaticElements: () =>\n set((state) => {\n const currentPage = state.canvas.pages.find(p => p.id === state.canvas.currentPageId);\n if (!currentPage) return {};\n const staticNodes = currentPage.children.filter(\n (n): n is CanvasNode => (n as CanvasNode & { staticOnNewPage?: boolean }).staticOnNewPage === true\n );\n const clonedChildren = staticNodes.map(cloneNodeWithNewIds);\n const pageNumber = state.canvas.pages.length + 1;\n const newPageId = `page-${Date.now()}`;\n const newPage: CanvasPage = {\n id: newPageId,\n name: `Page ${pageNumber}`,\n children: clonedChildren,\n settings: { ...currentPage.settings },\n };\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: [...state.canvas.pages, newPage],\n currentPageId: newPageId,\n selectedIds: [],\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n deletePage: (pageId) =>\n set((state) => {\n if (state.canvas.pages.length <= 1) return {};\n\n const newPages = state.canvas.pages.filter(p => p.id !== pageId);\n const currentPageDeleted = state.canvas.currentPageId === pageId;\n const newCurrentPageId = currentPageDeleted ? newPages[0].id : state.canvas.currentPageId;\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: newPages,\n currentPageId: newCurrentPageId,\n selectedIds: currentPageDeleted ? [] : state.canvas.selectedIds,\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n duplicatePage: (pageId) =>\n set((state) => {\n const page = state.canvas.pages.find(p => p.id === pageId);\n if (!page) return {};\n\n const newPageId = generateId('page');\n const newPage: CanvasPage = {\n ...page,\n id: newPageId,\n name: `${page.name} (copy)`,\n children: page.children.map(cloneNodeWithNewIds), // Use cloneNodeWithNewIds to regenerate all IDs\n };\n\n const pageIndex = state.canvas.pages.findIndex(p => p.id === pageId);\n const newPages = [...state.canvas.pages];\n newPages.splice(pageIndex + 1, 0, newPage);\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: newPages,\n currentPageId: newPageId,\n selectedIds: [],\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n renamePage: (pageId, name) =>\n set((state) => {\n const trimmedName = name.trim();\n if (!trimmedName) return {};\n \n const updatedPages = state.canvas.pages.map(p =>\n p.id === pageId ? { ...p, name: trimmedName } : p\n );\n \n const nextCanvas: CanvasState = { ...state.canvas, pages: updatedPages };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n setCurrentPage: (pageId) =>\n set((state) => {\n if (state.canvas.currentPageId === pageId) return {};\n return { canvas: { ...state.canvas, currentPageId: pageId, selectedIds: [] } };\n }),\n\n reorderPages: (fromIndex, toIndex) =>\n set((state) => {\n const pages = [...state.canvas.pages];\n const [removed] = pages.splice(fromIndex, 1);\n pages.splice(toIndex, 0, removed);\n\n const nextCanvas: CanvasState = { ...state.canvas, pages };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n updatePageSettings: (pageId, settings) =>\n set((state) => {\n const updatedPages = state.canvas.pages.map(p =>\n p.id === pageId ? { ...p, settings: { ...p.settings, ...settings } } : p\n );\n let themeConfig = state.canvas.themeConfig;\n // Auto-sync default theme variant when page background changes\n if (themeConfig) {\n const PAGE_BG_ID = '__pageBackground__';\n let needsSync = false;\n const syncUpdates: Record<string, string> = {};\n if ('backgroundColor' in settings) {\n const bgProp = themeConfig.properties.find(p => p.elementId === PAGE_BG_ID && p.targetProperty === 'backgroundColor');\n if (bgProp) { syncUpdates[bgProp.id] = settings.backgroundColor || '#ffffff'; needsSync = true; }\n }\n if ('backgroundGradient' in settings) {\n const grad = settings.backgroundGradient;\n // Sync per-stop gradient theme properties\n const gradStopProps = themeConfig.properties.filter(p => p.elementId === PAGE_BG_ID && p.targetProperty === 'backgroundGradient' && p.svgColorKey?.startsWith('stop:'));\n for (const prop of gradStopProps) {\n const stopMatch = prop.svgColorKey?.match(/^stop:(\\d+)$/);\n if (stopMatch && grad) {\n const stopIndex = parseInt(stopMatch[1], 10);\n if (grad.stops[stopIndex]) {\n syncUpdates[prop.id] = grad.stops[stopIndex].color;\n needsSync = true;\n }\n }\n }\n }\n if (needsSync) {\n themeConfig = {\n ...themeConfig,\n variants: themeConfig.variants.map(v =>\n v.isDefault ? { ...v, values: { ...v.values, ...syncUpdates } } : v\n ),\n };\n }\n }\n const nextCanvas: CanvasState = { ...state.canvas, pages: updatedPages, themeConfig };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n moveElementsToPage: (elementIds, targetPageId) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const nodesToMove: CanvasNode[] = [];\n \n for (const id of elementIds) {\n const node = findNodeById(currentPage.children, id);\n if (node) nodesToMove.push(cloneNode(node));\n }\n \n if (nodesToMove.length === 0) return {};\n\n // Remove from current page\n let nextCurrentChildren = currentPage.children;\n for (const id of elementIds) {\n nextCurrentChildren = removeNodeFromTree(nextCurrentChildren, id);\n }\n\n const updatedPages = state.canvas.pages.map(p => {\n if (p.id === state.canvas.currentPageId) {\n return { ...p, children: nextCurrentChildren };\n }\n if (p.id === targetPageId) {\n let newChildren = p.children;\n for (const node of nodesToMove) {\n newChildren = addNodeToTree(newChildren, node, null);\n }\n return { ...p, children: newChildren };\n }\n return p;\n });\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: updatedPages,\n selectedIds: [],\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n moveElementsToPageWithPositions: (elementIds, targetPageId, positionsById) =>\n set((state) => {\n const currentPage = getCurrentPageFromCanvas(state.canvas);\n const nodesToMove: CanvasNode[] = [];\n \n for (const id of elementIds) {\n const node = findNodeById(currentPage.children, id);\n if (node && isElement(node)) {\n const pos = positionsById[id];\n if (pos) {\n nodesToMove.push({ ...node, left: pos.left, top: pos.top });\n } else {\n nodesToMove.push(cloneNode(node));\n }\n }\n }\n \n if (nodesToMove.length === 0) return {};\n\n let nextCurrentChildren = currentPage.children;\n for (const id of elementIds) {\n nextCurrentChildren = removeNodeFromTree(nextCurrentChildren, id);\n }\n\n const updatedPages = state.canvas.pages.map(p => {\n if (p.id === state.canvas.currentPageId) {\n return { ...p, children: nextCurrentChildren };\n }\n if (p.id === targetPageId) {\n let newChildren = p.children;\n for (const node of nodesToMove) {\n newChildren = addNodeToTree(newChildren, node, null);\n }\n return { ...p, children: newChildren };\n }\n return p;\n });\n\n const nextCanvas: CanvasState = {\n ...state.canvas,\n pages: updatedPages,\n currentPageId: targetPageId,\n selectedIds: elementIds,\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n // Legacy reorder methods - simplified with tree structure\n reorderElements: (pageId, orderedIds) =>\n set((state) => {\n const targetPage = state.canvas.pages.find(p => p.id === pageId);\n if (!targetPage) return {};\n \n // Reorder root-level nodes based on orderedIds\n const nodeMap = new Map(targetPage.children.map(n => [n.id, n]));\n const reorderedChildren: CanvasNode[] = [];\n const usedIds = new Set<string>();\n \n for (const id of orderedIds) {\n const node = nodeMap.get(id);\n if (node) {\n reorderedChildren.push(node);\n usedIds.add(id);\n }\n }\n \n // Add any nodes not in orderedIds\n for (const node of targetPage.children) {\n if (!usedIds.has(node.id)) {\n reorderedChildren.push(node);\n }\n }\n \n const updatedPages = state.canvas.pages.map(p =>\n p.id === pageId ? { ...p, children: reorderedChildren } : p\n );\n \n const nextCanvas: CanvasState = { ...state.canvas, pages: updatedPages };\n const committed = commitFromState(state, nextCanvas);\n return { canvas: nextCanvas, ...committed };\n }),\n\n // === LOAD/RESET ===\n \n loadConfig: (config) =>\n set((state) => {\n const collapsedGroups = new Set<string>();\n \n // Collect collapsed group IDs from config\n const collectCollapsedGroups = (nodes: CanvasNode[]) => {\n for (const node of nodes) {\n if (isGroup(node) && node.collapsed) {\n collapsedGroups.add(node.id);\n }\n if (isGroup(node)) {\n collectCollapsedGroups(node.children);\n }\n }\n };\n \n config.pages.forEach(p => collectCollapsedGroups(p.children));\n\n clearMeasurementCache();\n\n // Normalize legacy stored widths for specific groups to keep config load resilient.\n const EXPERIENCE_TITLE_ROW_IDS = new Set(['resume-exp-1-title-row', 'resume-exp-2-title-row']);\n function normalizeExperienceTitleRowWidth(nodes: CanvasNode[]): CanvasNode[] {\n return nodes.map((node) => {\n if (isGroup(node)) {\n const g = node as GroupNode;\n const width = EXPERIENCE_TITLE_ROW_IDS.has(g.id) ? (typeof g.width === 'number' ? g.width : undefined) : g.width;\n const height = g.height;\n return { ...g, width, height, children: normalizeExperienceTitleRowWidth(g.children) };\n }\n return node;\n });\n }\n\n // Legacy: normalize group children positions (no-op when tree is already correct).\n function normalizeGroupChildrenPositions(\n nodes: CanvasNode[],\n _pageChildren: CanvasNode[],\n _opts?: { legacyAbsolutePositions?: boolean }\n ): CanvasNode[] {\n return nodes.map((node) => {\n if (isGroup(node)) {\n const g = node as GroupNode;\n return { ...g, children: normalizeGroupChildrenPositions(g.children ?? [], _pageChildren, _opts) };\n }\n return node;\n });\n }\n\n // Legacy: normalize absolute positions. Layout-engine-driven: only normalize experience title row width.\n const legacyAbsolute = config.relativeGroupPositions !== true;\n let pagesWithPositions = config.pages.map(p => {\n const children = legacyAbsolute\n ? normalizeGroupChildrenPositions(p.children, p.children, { legacyAbsolutePositions: true })\n : normalizeExperienceTitleRowWidth(p.children);\n return {\n id: p.id,\n name: p.name,\n children,\n settings: { backgroundColor: '#ffffff', ...p.settings },\n };\n });\n\n // Always run reflow on load so layout (including auto dimensions) is correct. Legacy needs it for positions;\n // layout-engine-driven needs it so builder shows correct layout with real measureTextHeight (seed bake used rough estimate).\n pagesWithPositions = pagesWithPositions.map(p => {\n const reflowUpdates = reflowPageFromRoot(p.children);\n if (reflowUpdates.size === 0) return p;\n let children = p.children;\n reflowUpdates.forEach((u, nodeId) => {\n const dims: Partial<CanvasElement> & Partial<GroupNode> = {};\n if (u.width !== undefined) dims.width = u.width;\n if (u.height !== undefined) dims.height = u.height;\n if (Object.keys(dims).length > 0) children = updateNodeInTree(children, nodeId, dims);\n });\n reflowUpdates.forEach((u, nodeId) => {\n const pos: Partial<CanvasElement> & Partial<GroupNode> = {};\n if (u.left !== undefined) pos.left = u.left;\n if (u.top !== undefined) pos.top = u.top;\n if (Object.keys(pos).length > 0) children = updateNodeInTree(children, nodeId, pos);\n });\n return { ...p, children };\n });\n const nextCanvas: CanvasState = {\n width: config.canvas.width,\n height: config.canvas.height,\n pages: pagesWithPositions,\n currentPageId: config.pages[0]?.id || 'page-1',\n selectedIds: [],\n zoom: 1,\n pan: { x: 0, y: 0 },\n projectSettings: config.projectSettings ?? { ...defaultProjectSettings },\n dynamicFields: (config.dynamicFields ?? []).map(f => {\n // Normalize fields: ensure mappings exists or convert from old format\n if (f.mappings && Array.isArray(f.mappings)) {\n // New format: already correct\n return f;\n } else if ((f as any).elementIds && Array.isArray((f as any).elementIds)) {\n // Old format: create mappings from elementIds (keep elementIds for backward compat)\n const elementIds = (f as any).elementIds;\n const mappings = elementIds.map((elementId: string) => ({\n elementId,\n targetProperty: (f.type === 'image' ? 'src' : f.type === 'color' ? 'fill' : 'text') as any,\n }));\n return { ...f, mappings } as DynamicField;\n } else {\n // Fallback: ensure mappings exists as empty array\n return { ...f, mappings: [] } as DynamicField;\n }\n }),\n fieldGroups: config.fieldGroups ?? [],\n formRenderPreset: (config as unknown as { formRenderPreset?: string }).formRenderPreset,\n formBindingMode: (config as any).formBindingMode,\n boundFormDefId: (config as any).boundFormDefId,\n boundFormDefName: (config as any).boundFormDefName,\n themeConfig: config.themeConfig ?? undefined,\n };\n\n const committed = commitFromState(state, nextCanvas);\n const out: Record<string, unknown> = {\n canvas: nextCanvas,\n canvasUpdateVersion: state.canvasUpdateVersion + 1,\n ...committed,\n collapsedGroups,\n showSections: config.showSections ?? false,\n showDynamicLabels: config.showDynamicLabels ?? false,\n };\n return out;\n }),\n\n applyReflowToCurrentPage: () =>\n set((state) => ({\n canvas: applyReflowToCanvas(state.canvas),\n canvasUpdateVersion: state.canvasUpdateVersion + 1,\n })),\n\n reflowStackGroupInPage: (pageId, groupId) =>\n set((state) => {\n const page = state.canvas.pages.find((p) => p.id === pageId);\n if (!page) return {};\n const reflowed = applyStackReflowToGroup(page.children, groupId);\n const updatedPages = state.canvas.pages.map((p) =>\n p.id === pageId ? { ...p, children: reflowed } : p\n );\n return {\n canvas: { ...state.canvas, pages: updatedPages },\n canvasUpdateVersion: state.canvasUpdateVersion + 1,\n };\n }),\n\n resetCanvas: () =>\n set((state) => {\n const newPageId = `page-${Date.now()}`;\n const newPage = createDefaultPage(newPageId, 'Page 1');\n \n const nextCanvas: CanvasState = {\n width: 595,\n height: 842,\n pages: [newPage],\n currentPageId: newPageId,\n selectedIds: [],\n zoom: 1,\n pan: { x: 0, y: 0 },\n projectSettings: { ...defaultProjectSettings },\n dynamicFields: [],\n fieldGroups: [], // Groups are created on-demand by user\n };\n\n const committed = commitFromState(state, nextCanvas);\n return { \n canvas: nextCanvas, \n ...committed,\n collapsedGroups: new Set(),\n activeTool: 'select' as Tool,\n };\n }),\n}));\n","import * as fabric from 'fabric';\n\n/**\n * Global registry for Fabric.js canvas instances\n * Each page has its own canvas - we need to access them for:\n * - PDF export (iterate all pages)\n * - RightPanel property editing (current page)\n */\n\ntype CanvasMap = Map<string, fabric.Canvas>;\n\nlet canvasRegistry: CanvasMap = new Map();\nlet activePageId: string | null = null;\n\nexport function registerFabricCanvas(pageId: string, canvas: fabric.Canvas) {\n canvasRegistry.set(pageId, canvas);\n}\n\nexport function unregisterFabricCanvas(pageId: string) {\n canvasRegistry.delete(pageId);\n}\n\nexport function setActivePageCanvas(pageId: string) {\n activePageId = pageId;\n}\n\nexport function getActiveCanvas(): fabric.Canvas | null {\n if (!activePageId) return null;\n return canvasRegistry.get(activePageId) || null;\n}\n\nexport function getCanvasForPage(pageId: string): fabric.Canvas | null {\n return canvasRegistry.get(pageId) || null;\n}\n\nexport function getAllCanvases(): Map<string, fabric.Canvas> {\n return new Map(canvasRegistry);\n}\n\nexport function clearCanvasRegistry() {\n canvasRegistry.clear();\n activePageId = null;\n}\n\n/**\n * Capture all pages as a combined base64 PNG screenshot for AI vision analysis.\n * Returns a data:image/png;base64,... string or null if no canvases registered.\n */\nexport function captureCanvasScreenshot(maxWidth = 800): string | null {\n const entries = Array.from(canvasRegistry.entries());\n if (entries.length === 0) return null;\n\n try {\n // For single page, just export directly\n if (entries.length === 1) {\n const [, fc] = entries[0];\n const scale = Math.min(1, maxWidth / (fc.getWidth() || 595));\n return fc.toDataURL({ format: 'png', multiplier: scale });\n }\n\n // Multi-page: stack vertically on an offscreen canvas\n const gap = 10;\n const pageImages: { dataUrl: string; w: number; h: number }[] = [];\n let totalHeight = 0;\n let maxW = 0;\n\n for (const [, fc] of entries) {\n const w = fc.getWidth() || 595;\n const h = fc.getHeight() || 842;\n const scale = Math.min(1, maxWidth / w);\n const dataUrl = fc.toDataURL({ format: 'png', multiplier: scale });\n const scaledW = Math.round(w * scale);\n const scaledH = Math.round(h * scale);\n pageImages.push({ dataUrl, w: scaledW, h: scaledH });\n totalHeight += scaledH + gap;\n if (scaledW > maxW) maxW = scaledW;\n }\n totalHeight -= gap; // Remove last gap\n\n // Draw onto offscreen canvas\n const offscreen = document.createElement('canvas');\n offscreen.width = maxW;\n offscreen.height = totalHeight;\n const ctx = offscreen.getContext('2d');\n if (!ctx) return pageImages[0]?.dataUrl || null;\n\n let yOffset = 0;\n const loadPromises = pageImages.map((pi, idx) => {\n return new Promise<void>((resolve) => {\n const img = new Image();\n img.onload = () => {\n ctx.drawImage(img, 0, yOffset);\n yOffset += pi.h + gap;\n resolve();\n };\n img.onerror = () => resolve();\n img.src = pi.dataUrl;\n });\n });\n\n // Since toDataURL is synchronous on fabric, we can return directly for single page\n // For multi-page, we need async - but let's just return first page for simplicity\n return pageImages[0]?.dataUrl || null;\n } catch (e) {\n console.error('Failed to capture canvas screenshot:', e);\n return null;\n }\n}\n\n/**\n * Capture first page canvas as base64 synchronously. Most reliable for AI vision.\n */\nexport function captureFirstPageScreenshot(maxWidth = 600): string | null {\n const entries = Array.from(canvasRegistry.entries());\n if (entries.length === 0) return null;\n try {\n const [, fc] = entries[0];\n const w = fc.getWidth() || 595;\n const scale = Math.min(1, maxWidth / w);\n return fc.toDataURL({ format: 'png', multiplier: scale });\n } catch (e) {\n console.error('Failed to capture page screenshot:', e);\n return null;\n }\n}\n\n// For debugging\nexport function getRegistryDebugInfo() {\n return {\n activePageId,\n registeredPages: Array.from(canvasRegistry.keys()),\n };\n}\n\n// Expose to window for console debugging (dev only)\nif (typeof window !== 'undefined' && import.meta.env.DEV) {\n (window as any).__fabricCanvasRegistry = {\n getActiveCanvas,\n getCanvasForPage,\n getAllCanvases,\n getRegistryDebugInfo,\n };\n}\n","/**\n * Google Fonts Dynamic Loader\n * Loads fonts on-demand from Google Fonts API.\n * Local fonts (public/fonts/) take priority; Google Fonts is the fallback.\n */\n\n// Fonts we have locally as TTF files — these don't need Google Fonts\nexport const LOCAL_FONTS = new Set([\n // Original local fonts (static TTFs)\n 'Playfair Display', 'Merriweather', 'Lora',\n 'Montserrat', 'Open Sans', 'Roboto', 'Lato', 'Raleway', 'Poppins',\n 'Inter', 'Nunito', 'Source Sans Pro', 'Work Sans',\n 'Oswald', 'Bebas Neue', 'Abril Fatface',\n 'Dancing Script', 'Pacifico', 'Great Vibes',\n // Newly added local fonts (variable + static TTFs)\n 'DM Sans', 'Outfit', 'Figtree', 'Manrope', 'Space Grotesk',\n 'League Spartan', 'EB Garamond', 'Libre Baskerville', 'Crimson Text',\n 'DM Serif Display', 'Libre Franklin', 'Mulish', 'Quicksand', 'Rubik',\n 'Karla', 'Plus Jakarta Sans', 'Sora', 'Urbanist', 'Lexend',\n 'Albert Sans', 'Noto Sans', 'Cabin', 'Barlow', 'Josefin Sans',\n 'Archivo', 'Overpass', 'Exo 2',\n 'Roboto Mono', 'Fira Code', 'JetBrains Mono', 'Source Code Pro',\n 'IBM Plex Mono', 'Space Mono',\n 'Sacramento', 'Alex Brush', 'Allura', 'Caveat', 'Lobster', 'Comfortaa',\n 'Anton', 'Teko',\n // Indic script fallback fonts\n 'Noto Sans Devanagari',\n 'Hind',\n]);\n\n// Track which Google Fonts we've already loaded\nconst loadedGoogleFonts = new Set<string>();\nconst failedGoogleFonts = new Set<string>(); // Fonts that failed to load — don't retry\n\n// Track loading promises to avoid duplicate requests\nconst loadingPromises = new Map<string, Promise<boolean>>();\n\n/**\n * Load a font from Google Fonts if it's not available locally.\n * Returns true if the font was loaded (or already available).\n */\nexport async function loadGoogleFont(fontFamily: string, weights?: number[]): Promise<boolean> {\n if (LOCAL_FONTS.has(fontFamily)) return true;\n if (loadedGoogleFonts.has(fontFamily)) return true;\n if (failedGoogleFonts.has(fontFamily)) return false;\n\n\n // Check if already loading\n const existing = loadingPromises.get(fontFamily);\n if (existing) return existing;\n\n const promise = _doLoadGoogleFont(fontFamily, weights);\n loadingPromises.set(fontFamily, promise);\n\n try {\n const result = await promise;\n if (result) {\n loadedGoogleFonts.add(fontFamily);\n } else {\n failedGoogleFonts.add(fontFamily);\n }\n return result;\n } finally {\n loadingPromises.delete(fontFamily);\n }\n}\n\nasync function _doLoadGoogleFont(fontFamily: string, weights?: number[]): Promise<boolean> {\n try {\n // Build the Google Fonts URL\n const weightStr = (weights || [300, 400, 500, 600, 700]).join(';');\n const encodedFamily = encodeURIComponent(fontFamily);\n // Use display=swap so text is visible immediately with fallback font\n const url = `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@0,${weightStr};1,${weightStr}&display=swap`;\n\n // Check if this stylesheet is already in the DOM\n const existingLink = document.querySelector(`link[href=\"${url}\"]`);\n if (existingLink) return true;\n\n // Inject link element\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = url;\n\n return new Promise((resolve) => {\n link.onload = async () => {\n // Wait for the font to actually be usable\n try {\n await document.fonts.load(`16px \"${fontFamily}\"`);\n await document.fonts.load(`bold 16px \"${fontFamily}\"`);\n } catch { /* ignore */ }\n resolve(true);\n };\n link.onerror = () => {\n console.warn(`[GoogleFonts] Failed to load: ${fontFamily}`);\n resolve(false);\n };\n document.head.appendChild(link);\n });\n } catch (e) {\n console.warn(`[GoogleFonts] Error loading ${fontFamily}:`, e);\n return false;\n }\n}\n\n/**\n * Check if a font is available locally (no Google Fonts needed)\n */\nexport function isLocalFont(fontFamily: string): boolean {\n return LOCAL_FONTS.has(fontFamily);\n}\n\n/**\n * Get the list of all available fonts (local + previously loaded Google Fonts)\n */\nexport function getLoadedFonts(): string[] {\n return [...LOCAL_FONTS, ...loadedGoogleFonts];\n}\n\n// ── Shared font catalogue ──\n\n/**\n * Extended font list: all popular Canva fonts organized by category.\n * Local fonts are marked; others will be loaded from Google Fonts on-demand.\n */\nexport interface FontEntry {\n name: string;\n category: string;\n local: boolean; // true = we have the TTF file locally\n}\n\nexport const EXTENDED_FONT_LIST: FontEntry[] = [\n // ── Serif ──\n { name: 'Playfair Display', category: 'Serif', local: true },\n { name: 'Merriweather', category: 'Serif', local: true },\n { name: 'Lora', category: 'Serif', local: true },\n { name: 'EB Garamond', category: 'Serif', local: true },\n { name: 'Libre Baskerville', category: 'Serif', local: true },\n { name: 'Crimson Text', category: 'Serif', local: true },\n { name: 'DM Serif Display', category: 'Serif', local: true },\n { name: 'Cormorant Garamond', category: 'Serif', local: false },\n { name: 'Noto Serif', category: 'Serif', local: false },\n { name: 'PT Serif', category: 'Serif', local: false },\n { name: 'Bitter', category: 'Serif', local: false },\n { name: 'Spectral', category: 'Serif', local: false },\n { name: 'Cardo', category: 'Serif', local: false },\n { name: 'Old Standard TT', category: 'Serif', local: false },\n { name: 'Vollkorn', category: 'Serif', local: false },\n\n // ── Sans-Serif ──\n { name: 'Montserrat', category: 'Sans-Serif', local: true },\n { name: 'Open Sans', category: 'Sans-Serif', local: true },\n { name: 'Roboto', category: 'Sans-Serif', local: true },\n { name: 'Lato', category: 'Sans-Serif', local: true },\n { name: 'Raleway', category: 'Sans-Serif', local: true },\n { name: 'Poppins', category: 'Sans-Serif', local: true },\n { name: 'Inter', category: 'Sans-Serif', local: true },\n { name: 'Nunito', category: 'Sans-Serif', local: true },\n { name: 'Source Sans Pro', category: 'Sans-Serif', local: true },\n { name: 'Work Sans', category: 'Sans-Serif', local: true },\n { name: 'DM Sans', category: 'Sans-Serif', local: true },\n { name: 'Outfit', category: 'Sans-Serif', local: true },\n { name: 'Figtree', category: 'Sans-Serif', local: true },\n { name: 'Manrope', category: 'Sans-Serif', local: true },\n { name: 'Space Grotesk', category: 'Sans-Serif', local: true },\n { name: 'Mulish', category: 'Sans-Serif', local: true },\n { name: 'Quicksand', category: 'Sans-Serif', local: true },\n { name: 'Rubik', category: 'Sans-Serif', local: true },\n { name: 'Karla', category: 'Sans-Serif', local: true },\n { name: 'Plus Jakarta Sans', category: 'Sans-Serif', local: true },\n { name: 'Libre Franklin', category: 'Sans-Serif', local: true },\n { name: 'Sora', category: 'Sans-Serif', local: true },\n { name: 'Urbanist', category: 'Sans-Serif', local: true },\n { name: 'Lexend', category: 'Sans-Serif', local: true },\n { name: 'Albert Sans', category: 'Sans-Serif', local: true },\n { name: 'Noto Sans', category: 'Sans-Serif', local: true },\n { name: 'Cabin', category: 'Sans-Serif', local: true },\n { name: 'Barlow', category: 'Sans-Serif', local: true },\n { name: 'Josefin Sans', category: 'Sans-Serif', local: true },\n { name: 'Archivo', category: 'Sans-Serif', local: true },\n { name: 'Overpass', category: 'Sans-Serif', local: true },\n { name: 'Exo 2', category: 'Sans-Serif', local: true },\n { name: 'Nunito Sans', category: 'Sans-Serif', local: false },\n { name: 'PT Sans', category: 'Sans-Serif', local: false },\n\n // ── Display ──\n { name: 'Oswald', category: 'Display', local: true },\n { name: 'Bebas Neue', category: 'Display', local: true },\n { name: 'Abril Fatface', category: 'Display', local: true },\n { name: 'League Spartan', category: 'Display', local: true },\n { name: 'Anton', category: 'Display', local: true },\n { name: 'Teko', category: 'Display', local: true },\n { name: 'Righteous', category: 'Display', local: false },\n { name: 'Alfa Slab One', category: 'Display', local: false },\n { name: 'Archivo Black', category: 'Display', local: false },\n { name: 'Fredoka One', category: 'Display', local: false },\n { name: 'Passion One', category: 'Display', local: false },\n { name: 'Bungee', category: 'Display', local: false },\n { name: 'Secular One', category: 'Display', local: false },\n { name: 'Lilita One', category: 'Display', local: false },\n { name: 'Titan One', category: 'Display', local: false },\n { name: 'Russo One', category: 'Display', local: false },\n { name: 'Staatliches', category: 'Display', local: false },\n { name: 'Dela Gothic One', category: 'Display', local: false },\n { name: 'Black Ops One', category: 'Display', local: false },\n { name: 'Permanent Marker', category: 'Display', local: false },\n\n // ── Handwriting / Script ──\n { name: 'Dancing Script', category: 'Handwriting', local: true },\n { name: 'Pacifico', category: 'Handwriting', local: true },\n { name: 'Great Vibes', category: 'Handwriting', local: true },\n { name: 'Sacramento', category: 'Handwriting', local: true },\n { name: 'Alex Brush', category: 'Handwriting', local: true },\n { name: 'Allura', category: 'Handwriting', local: true },\n { name: 'Caveat', category: 'Handwriting', local: true },\n { name: 'Lobster', category: 'Handwriting', local: true },\n { name: 'Comfortaa', category: 'Handwriting', local: true },\n { name: 'Satisfy', category: 'Handwriting', local: false },\n { name: 'Kalam', category: 'Handwriting', local: false },\n { name: 'Indie Flower', category: 'Handwriting', local: false },\n { name: 'Courgette', category: 'Handwriting', local: false },\n { name: 'Cookie', category: 'Handwriting', local: false },\n { name: 'Shadows Into Light', category: 'Handwriting', local: false },\n { name: 'Patrick Hand', category: 'Handwriting', local: false },\n { name: 'Amatic SC', category: 'Handwriting', local: false },\n\n // ── Monospace ──\n { name: 'Roboto Mono', category: 'Monospace', local: true },\n { name: 'Fira Code', category: 'Monospace', local: true },\n { name: 'JetBrains Mono', category: 'Monospace', local: true },\n { name: 'Source Code Pro', category: 'Monospace', local: true },\n { name: 'IBM Plex Mono', category: 'Monospace', local: true },\n { name: 'Space Mono', category: 'Monospace', local: true },\n\n // ── Indic Script ──\n { name: 'Hind', category: 'Sans-Serif', local: true },\n];\n\n/** Quick lookup: normalized name → font entry */\nconst _fontLookupCache = new Map<string, FontEntry>();\nexport function findFontEntry(name: string): FontEntry | undefined {\n const key = name.toLowerCase().replace(/[\\s\\-_]/g, '');\n if (_fontLookupCache.has(key)) return _fontLookupCache.get(key);\n const entry = EXTENDED_FONT_LIST.find(f =>\n f.name.toLowerCase().replace(/[\\s\\-_]/g, '') === key\n );\n if (entry) _fontLookupCache.set(key, entry);\n return entry;\n}\n","/**\n * Font Loading Utilities\n *\n * Long-standing issue (since early in the project): text overflowing its box on load\n * because bounds were measured before fonts were ready. Fabric and our text measurement\n * use font metrics; if fonts load after first paint, we get wrong width/height.\n *\n * Safeguards in place:\n * - preloadAllFonts() + document.fonts.ready before first canvas sync (PageCanvas)\n * - Preload fonts used by text elements on the current page (not just FONTS_TO_PRELOAD)\n * - clearFabricCharCache() before setReady so Fabric doesn't use stale char width cache\n * - clearMeasurementCache() so getMinTextWidth / measureTextHeight don't use old cache\n * - setupFontLoadingListener: on late font load, reflow textboxes and persist height to store\n * - ensureFontLoaded: automatically loads Google Fonts on-demand for non-local fonts\n */\n\nimport * as fabric from 'fabric';\nimport { LOCAL_FONTS, loadGoogleFont } from '@/lib/googleFonts';\n\n// List of all fonts used in the application\nexport const FONTS_TO_PRELOAD = [\n 'Open Sans',\n 'Playfair Display',\n 'Merriweather',\n 'Lora',\n 'Montserrat',\n 'Roboto',\n 'Lato',\n 'Raleway',\n 'Poppins',\n 'Inter',\n 'Nunito',\n 'Source Sans Pro',\n 'Work Sans',\n 'Oswald',\n 'Bebas Neue',\n 'Abril Fatface',\n 'Dancing Script',\n 'Pacifico',\n 'Great Vibes',\n // Newly added local fonts\n 'DM Sans',\n 'Outfit',\n 'Figtree',\n 'Manrope',\n 'Space Grotesk',\n 'League Spartan',\n 'EB Garamond',\n 'Libre Baskerville',\n 'Crimson Text',\n 'DM Serif Display',\n 'Libre Franklin',\n 'Mulish',\n 'Quicksand',\n 'Rubik',\n 'Karla',\n 'Plus Jakarta Sans',\n 'Sora',\n 'Urbanist',\n 'Lexend',\n 'Albert Sans',\n 'Noto Sans',\n 'Cabin',\n 'Barlow',\n 'Josefin Sans',\n 'Archivo',\n 'Overpass',\n 'Exo 2',\n 'Roboto Mono',\n 'Fira Code',\n 'JetBrains Mono',\n 'Source Code Pro',\n 'IBM Plex Mono',\n 'Space Mono',\n 'Sacramento',\n 'Alex Brush',\n 'Allura',\n 'Caveat',\n 'Lobster',\n 'Comfortaa',\n 'Anton',\n 'Teko',\n 'Hind',\n];\n\n// Track font loading state globally\nlet fontsLoaded = false;\nlet fontsLoadingPromise: Promise<void> | null = null;\n\n/**\n * Preload a specific font by using the CSS Font Loading API\n */\nexport const preloadFont = async (fontFamily: string): Promise<boolean> => {\n try {\n if (document.fonts) {\n await document.fonts.load(`16px \"${fontFamily}\"`);\n await document.fonts.load(`bold 16px \"${fontFamily}\"`);\n return true;\n }\n return false;\n } catch (e) {\n console.warn(`Failed to preload font: ${fontFamily}`, e);\n return false;\n }\n};\n\n/**\n * Preload all fonts used in the application\n * Returns a promise that resolves when all fonts are loaded\n */\nexport const preloadAllFonts = async (): Promise<void> => {\n if (fontsLoaded) return;\n \n if (fontsLoadingPromise) {\n return fontsLoadingPromise;\n }\n \n fontsLoadingPromise = (async () => {\n if (document.fonts) {\n await document.fonts.ready;\n }\n await Promise.all(FONTS_TO_PRELOAD.map(font => preloadFont(font)));\n // Wait again so the browser has applied the new fonts before we resolve (reduces intermittent wrong metrics)\n if (document.fonts) {\n await document.fonts.ready;\n }\n fontsLoaded = true;\n })();\n\n return fontsLoadingPromise;\n};\n\n/** Call after loading page-specific fonts; ensures browser has applied them before first sync. */\nexport const waitForFontsReady = async (): Promise<void> => {\n if (!document.fonts) return;\n await document.fonts.ready;\n};\n\nconst DEFAULT_FONT_CHECK_TIMEOUT_MS = 3500;\nconst FONT_CHECK_POLL_MS = 60;\n\n/**\n * Resolves when the browser reports all given fonts as loaded (document.fonts.check),\n * or after timeout. Polls so we don't setReady until fonts are actually available.\n */\nexport const waitUntilFontsAvailable = async (\n fontFamilies: string[],\n options?: { timeoutMs?: number; pollIntervalMs?: number }\n): Promise<void> => {\n if (!document.fonts || fontFamilies.length === 0) return;\n const timeoutMs = options?.timeoutMs ?? DEFAULT_FONT_CHECK_TIMEOUT_MS;\n const pollMs = options?.pollIntervalMs ?? FONT_CHECK_POLL_MS;\n const deadline = Date.now() + timeoutMs;\n const check = (): boolean =>\n fontFamilies.every(\n (f) => document.fonts!.check(`16px \"${f}\"`) && document.fonts!.check(`bold 16px \"${f}\"`)\n );\n while (!check()) {\n if (Date.now() >= deadline) break;\n await new Promise((r) => setTimeout(r, pollMs));\n }\n};\n\n/**\n * Check if fonts have been loaded\n */\nexport const areFontsLoaded = (): boolean => fontsLoaded;\n\n/**\n * Clear only Fabric's global character width cache (no canvas needed).\n * Call this after fonts load and before creating/measuring text so Fabric\n * doesn't use stale metrics from before fonts were ready.\n */\nexport const clearFabricCharCache = (): void => {\n const fabricAny = fabric as any;\n if (typeof fabricAny.charWidthsCache === 'object' && fabricAny.charWidthsCache !== null) {\n Object.keys(fabricAny.charWidthsCache).forEach(key => {\n delete fabricAny.charWidthsCache[key];\n });\n }\n if (typeof fabric.util !== 'undefined' && typeof (fabric.util as any).clearFabricFontCache === 'function') {\n (fabric.util as any).clearFabricFontCache();\n }\n};\n\n/**\n * Clear Fabric.js font cache and re-render all textboxes on a canvas\n * This should be called after fonts are loaded to fix text measurement issues\n */\nexport const clearFontCacheAndRerender = (canvas: fabric.Canvas): void => {\n clearFabricCharCache();\n\n // Re-initialize dimensions for all textboxes to use correct font metrics\n canvas.getObjects().forEach((obj) => {\n if (obj instanceof fabric.Textbox) {\n // Force dirty state to ensure re-render\n obj.dirty = true;\n obj.initDimensions();\n obj.setCoords();\n }\n });\n \n canvas.requestRenderAll();\n};\n\n/**\n * Ensure a specific font is loaded before using it.\n * For local fonts (TTF in public/fonts/), uses CSS Font Loading API.\n * For non-local fonts, dynamically loads from Google Fonts.\n */\nexport const ensureFontLoaded = async (fontFamily: string): Promise<void> => {\n if (!fontFamily) return;\n\n // If it's a local font, just check/load via browser API\n if (LOCAL_FONTS.has(fontFamily)) {\n try {\n const isLoaded = document.fonts?.check(`16px \"${fontFamily}\"`);\n if (isLoaded) return;\n await document.fonts?.load(`16px \"${fontFamily}\"`);\n await document.fonts?.load(`bold 16px \"${fontFamily}\"`);\n } catch (e) {\n console.warn(`Failed to ensure local font loaded: ${fontFamily}`, e);\n }\n return;\n }\n\n // For non-local fonts, load from Google Fonts (injects CSS + waits for font)\n try {\n await loadGoogleFont(fontFamily);\n } catch (e) {\n console.warn(`Failed to load Google Font: ${fontFamily}`, e);\n }\n};\n\n/**\n * Setup font loading listener for a canvas\n * Automatically re-renders textboxes when fonts finish loading\n * @param afterRerender - Optional callback run after reflow (e.g. persist text dimensions to store)\n * Returns a cleanup function to remove the listener\n */\nexport const setupFontLoadingListener = (\n canvas: fabric.Canvas,\n afterRerender?: (canvas: fabric.Canvas) => void\n): (() => void) => {\n const handleFontLoad = () => {\n clearFontCacheAndRerender(canvas);\n afterRerender?.(canvas);\n };\n\n if (document.fonts) {\n document.fonts.addEventListener('loadingdone', handleFontLoad);\n }\n\n return () => {\n if (document.fonts) {\n document.fonts.removeEventListener('loadingdone', handleFontLoad);\n }\n };\n};\n\n// Start preloading fonts immediately when this module is imported\n// This ensures fonts start loading as early as possible\npreloadAllFonts();\n","import * as fabric from 'fabric';\n\n// ============================================\n// FABRIC OBJECT METADATA\n// ============================================\nexport type FabricObjectWithMeta = fabric.FabricObject & {\n __docuforgeId?: string; // Element ID for individual objects\n __docuforgeGroupId?: string; // Group ID for fabric.Group objects\n};\n\nexport const getObjectId = (obj: fabric.FabricObject): string | undefined =>\n (obj as FabricObjectWithMeta).__docuforgeId;\n\nexport const getFabricGroupId = (obj: fabric.FabricObject): string | undefined =>\n (obj as FabricObjectWithMeta).__docuforgeGroupId;\n\nexport const setObjectData = (obj: fabric.FabricObject, id: string): void => {\n (obj as FabricObjectWithMeta).__docuforgeId = id;\n};\n\nexport const setFabricGroupId = (obj: fabric.FabricObject, groupId: string): void => {\n (obj as FabricObjectWithMeta).__docuforgeGroupId = groupId;\n};\n","import * as fabric from 'fabric';\nimport { CanvasElement } from '@/types/editor';\nimport { setObjectData, getObjectId } from './fabricUtils';\nimport { updateCoverLayout, createMaskedImageElement } from './canvaMaskedImage';\nimport type { MutableRefObject } from 'react';\n\nimport { API_URL } from '@/lib/api';\n\n// ── In-memory caches to avoid re-fetching on theme switches ──\n\n/** Cache fetched SVG text by URL so repeated loads skip the network. */\nconst svgTextCache = new Map<string, string | null>();\n\n/** Cache resolved HTMLImageElements by URL so Fabric.fromURL hits memory. */\nconst htmlImageCache = new Map<string, HTMLImageElement>();\n\nconst MAX_CACHE_ENTRIES = 200;\n\nfunction trimCache<K, V>(map: Map<K, V>) {\n if (map.size > MAX_CACHE_ENTRIES) {\n const keysToDelete = Array.from(map.keys()).slice(0, map.size - MAX_CACHE_ENTRIES);\n keysToDelete.forEach(k => map.delete(k));\n }\n}\n\n/**\n * Preload an image URL into the browser's in-memory cache.\n * Subsequent fabric.FabricImage.fromURL calls for the same URL\n * will resolve from memory instead of hitting the network.\n */\nexport function preloadImage(url: string): Promise<HTMLImageElement> {\n const cached = htmlImageCache.get(url);\n if (cached) return Promise.resolve(cached);\n\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n htmlImageCache.set(url, img);\n trimCache(htmlImageCache);\n resolve(img);\n };\n img.onerror = reject;\n img.src = url;\n });\n}\n\n/**\n * Batch-preload multiple image URLs in parallel.\n * Best called before mounting the canvas so assets are warm.\n */\nexport function preloadImages(urls: string[]): Promise<void> {\n const unique = [...new Set(urls)].filter(u => u && !htmlImageCache.has(u));\n if (unique.length === 0) return Promise.resolve();\n return Promise.allSettled(unique.map(preloadImage)).then(() => {});\n}\n\n/** Check if a URL points to a private/localhost host that can't be proxied. */\nexport function isPrivateUrl(url: string): boolean {\n try {\n const h = new URL(url).hostname.toLowerCase();\n return h === 'localhost' || h === '127.0.0.1' || h === '0.0.0.0' || h.endsWith('.local') || /^(10\\.|192\\.168\\.|169\\.254\\.)/.test(h);\n } catch { return false; }\n}\n\n/** Convert a Supabase signed storage URL to a public URL (bucket is public). */\nfunction toPublicStorageUrl(url: string): string | null {\n const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;\n if (!supabaseUrl || !url.includes(supabaseUrl)) return null;\n // Match signed URL pattern: /storage/v1/object/sign/<bucket>/<path>?token=...\n const signedMatch = url.match(/\\/storage\\/v1\\/object\\/sign\\/([^?]+)/);\n if (signedMatch) {\n return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;\n }\n // Already a public URL — no proxy needed\n if (url.includes('/storage/v1/object/public/')) return url;\n return null;\n}\n\n/**\n * CloudFront asset URLs currently need the image proxy because the CDN response\n * does not include browser-safe CORS headers for Fabric/image canvas loading.\n */\n\nexport function getProxiedImageUrl(imageUrl: string): string {\n if (!imageUrl) return '';\n if (imageUrl.startsWith('data:') || imageUrl.startsWith('blob:')) return imageUrl;\n if (isPrivateUrl(imageUrl)) {\n console.warn('[image-proxy] Skipping private URL:', imageUrl.substring(0, 80));\n return '';\n }\n // Supabase storage URLs don't need proxying — use public URL directly\n const publicUrl = toPublicStorageUrl(imageUrl);\n if (publicUrl) return publicUrl;\n return `${API_URL}/image-proxy?url=${encodeURIComponent(imageUrl)}`;\n}\n\n/** Whether the image is SVG (by URL or explicit format). */\nexport function isSvgImage(imageUrl: string, sourceFormat?: 'svg' | 'png'): boolean {\n if (sourceFormat === 'svg') return true;\n const lower = (imageUrl || '').toLowerCase();\n return lower.includes('.svg') || lower.startsWith('data:image/svg+xml');\n}\n\n/** Parse width/height from SVG string (viewBox \"0 0 W H\" or width/height attributes). */\nexport function parseSvgDimensionsFromText(svgText: string): { width: number; height: number } | null {\n if (!svgText || typeof svgText !== 'string') return null;\n const viewBoxMatch = svgText.match(/viewBox\\s*=\\s*[\"']?\\s*(-?[\\d.]+)\\s+(-?[\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)/i);\n if (viewBoxMatch) {\n const w = parseFloat(viewBoxMatch[3]);\n const h = parseFloat(viewBoxMatch[4]);\n if (Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) return { width: w, height: h };\n }\n const widthMatch = svgText.match(/\\bwidth\\s*=\\s*[\"']?\\s*([\\d.]+)/i);\n const heightMatch = svgText.match(/\\bheight\\s*=\\s*[\"']?\\s*([\\d.]+)/i);\n if (widthMatch && heightMatch) {\n const w = parseFloat(widthMatch[1]);\n const h = parseFloat(heightMatch[1]);\n if (Number.isFinite(w) && Number.isFinite(h) && w > 0 && h > 0) return { width: w, height: h };\n }\n return null;\n}\n\n/** Fetch SVG from URL and return its text. Public wrapper that also accepts sourceFormat. */\nexport async function fetchSvgTextPublic(imageUrl: string, sourceFormat?: 'svg' | 'png'): Promise<string | null> {\n return fetchSvgText(imageUrl, sourceFormat);\n}\n\nasync function fetchSvgText(imageUrl: string, sourceFormat?: 'svg' | 'png'): Promise<string | null> {\n try {\n // Handle inline SVG data URLs directly (no caching needed)\n if (imageUrl.startsWith('data:image/svg+xml;base64,')) {\n return atob(imageUrl.slice('data:image/svg+xml;base64,'.length));\n }\n if (imageUrl.startsWith('data:image/svg+xml,')) {\n return decodeURIComponent(imageUrl.slice('data:image/svg+xml,'.length).replace(/\\+/g, ' '));\n }\n\n // Check in-memory cache first\n if (svgTextCache.has(imageUrl)) {\n return svgTextCache.get(imageUrl) ?? null;\n }\n\n // For non-SVG-looking sources, still attempt fetch if sourceFormat says svg\n const canBeSvg = sourceFormat === 'svg' || isSvgImage(imageUrl, sourceFormat) || imageUrl.startsWith('blob:') || imageUrl.startsWith('http');\n if (!canBeSvg) return null;\n\n const fetchWithTimeout = async (url: string) => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 8000);\n try {\n return await fetch(url, { signal: controller.signal });\n } finally {\n clearTimeout(timeoutId);\n }\n };\n\n const url = imageUrl.startsWith('blob:') || imageUrl.startsWith('data:')\n ? imageUrl\n : getProxiedImageUrl(imageUrl);\n\n const res = await fetchWithTimeout(url);\n if (!res.ok) {\n svgTextCache.set(imageUrl, null);\n return null;\n }\n\n const text = await res.text();\n // Guard: only treat as SVG when markup actually contains <svg ...>\n if (!/<svg[\\s>]/i.test(text)) {\n svgTextCache.set(imageUrl, null);\n return null;\n }\n svgTextCache.set(imageUrl, text);\n trimCache(svgTextCache);\n return text;\n } catch {\n return null;\n }\n}\n\n/** Fetch SVG from URL and return its intrinsic dimensions (viewBox or width/height). */\nexport async function loadSvgDimensions(imageUrl: string): Promise<{ width: number; height: number } | null> {\n const text = await fetchSvgText(imageUrl);\n return text ? parseSvgDimensionsFromText(text) : null;\n}\n\n/**\n * Ensure root <svg> has explicit width and height (from viewBox if missing) so the browser\n * renders at the correct size. Returns modified SVG string.\n */\nfunction injectSvgExplicitDimensions(svgText: string, width: number, height: number): string {\n if (!svgText || width <= 0 || height <= 0) return svgText;\n const w = String(Math.round(width));\n const h = String(Math.round(height));\n // Replace or add width/height on the opening <svg ...> tag\n const svgOpenMatch = svgText.match(/<svg([^>]*)>/i);\n if (!svgOpenMatch) return svgText;\n const attrs = svgOpenMatch[1];\n let newAttrs = attrs\n .replace(/\\bwidth\\s*=\\s*[\"'][^\"']*[\"']/gi, `width=\"${w}\"`)\n .replace(/\\bheight\\s*=\\s*[\"'][^\"']*[\"']/gi, `height=\"${h}\"`);\n if (!/\\bwidth\\s*=/i.test(newAttrs)) newAttrs = ` width=\"${w}\"${newAttrs}`;\n if (!/\\bheight\\s*=/i.test(newAttrs)) newAttrs = ` height=\"${h}\"${newAttrs}`;\n return svgText.replace(/<svg[^>]*>/i, `<svg${newAttrs}>`);\n}\n\n/**\n * For SVG URLs: fetch SVG, inject explicit width/height from viewBox so the browser\n * renders at correct intrinsic size. Returns a data URL so Fabric's image loads with\n * correct naturalWidth/naturalHeight and the full image is not clipped.\n * Returns null for non-SVG or on failure (caller should use original URL).\n */\nexport async function getNormalizedSvgUrl(imageUrl: string, colorMap?: Record<string, string>, sourceFormat?: 'svg' | 'png'): Promise<string | null> {\n if (!isSvgImage(imageUrl, sourceFormat)) return null;\n let text = await fetchSvgText(imageUrl, sourceFormat);\n if (!text) return null;\n // Apply color replacements if provided\n if (colorMap && Object.keys(colorMap).length > 0) {\n const { applySvgColorMap } = await import('./svgColorUtils');\n text = applySvgColorMap(text, colorMap);\n }\n const dims = parseSvgDimensionsFromText(text);\n if (!dims || dims.width <= 0 || dims.height <= 0) return null;\n const normalized = injectSvgExplicitDimensions(text, dims.width, dims.height);\n const encoded = encodeURIComponent(normalized);\n return `data:image/svg+xml,${encoded}`;\n}\n\n/**\n * Normalize Fabric image dimensions for SVG. Browsers often report 0 or wrong size for SVG\n * loaded into HTMLImageElement; this sets width/height from natural dimensions or by\n * parsing the SVG so object-fit (cover/contain/fill) and crop zoom behave correctly.\n */\nexport async function normalizeSvgImageDimensions(\n fabricImage: fabric.FabricImage,\n imageUrl: string,\n sourceFormat?: 'svg' | 'png'\n): Promise<void> {\n if (!isSvgImage(imageUrl, sourceFormat)) return;\n const el = (fabricImage as any).getElement?.() ?? (fabricImage as any)._element;\n let w = 0;\n let h = 0;\n if (el && typeof el.naturalWidth === 'number' && typeof el.naturalHeight === 'number' && el.naturalWidth > 0 && el.naturalHeight > 0) {\n w = el.naturalWidth;\n h = el.naturalHeight;\n }\n if (w <= 0 || h <= 0) {\n const dims = await loadSvgDimensions(imageUrl);\n if (dims && dims.width > 0 && dims.height > 0) {\n w = dims.width;\n h = dims.height;\n }\n }\n if (w > 0 && h > 0) {\n fabricImage.set({ width: w, height: h });\n fabricImage.setCoords();\n }\n}\n\nexport function createImagePlaceholder(element: CanvasElement): fabric.FabricObject {\n const visualWidth = Number(element.width) * (element.scaleX ?? 1);\n const visualHeight = Number(element.height) * (element.scaleY ?? 1);\n \n const bgRect = new fabric.Rect({\n originX: 'left',\n originY: 'top',\n left: 0,\n top: 0,\n width: visualWidth,\n height: visualHeight,\n fill: 'transparent',\n stroke: 'transparent',\n strokeWidth: 0,\n rx: element.clipShape === 'rounded' ? (element.rx || 8) : 0,\n ry: element.clipShape === 'rounded' ? (element.ry || 8) : 0,\n });\n \n \n const group = new fabric.Group([bgRect], {\n left: element.left,\n top: element.top,\n originX: 'left',\n originY: 'top',\n selectable: element.selectable,\n evented: element.evented,\n });\n \n return group;\n}\n\nexport function createImagePlaceholderForGroup(element: CanvasElement): fabric.FabricObject {\n // CRITICAL: Match crop group structure exactly\n // Crop groups store visual (scaled) dimensions as frameW/frameH and use scaleX/scaleY = 1\n // So we use visual dimensions as intrinsic width/height, not scaled again\n const frameWidth = Number(element.width) * (element.scaleX ?? 1);\n const frameHeight = Number(element.height) * (element.scaleY ?? 1);\n \n const bgRect = new fabric.Rect({\n originX: 'left',\n originY: 'top',\n left: 0,\n top: 0,\n width: frameWidth,\n height: frameHeight,\n fill: 'transparent',\n stroke: 'transparent',\n strokeWidth: 0,\n rx: element.clipShape === 'rounded' ? (element.rx || 8) : 0,\n ry: element.clipShape === 'rounded' ? (element.ry || 8) : 0,\n });\n \n \n // CRITICAL: Use center origin like crop groups to ensure proper positioning\n const group = new fabric.Group([bgRect], {\n left: element.left,\n top: element.top,\n originX: 'center',\n originY: 'center',\n width: frameWidth,\n height: frameHeight,\n scaleX: 1,\n scaleY: 1,\n angle: element.angle ?? 0,\n opacity: element.opacity ?? 1,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n \n // CRITICAL: Apply clip shape if element had one\n if (element.clipShape && element.clipShape !== 'none') {\n const clipPath = createImageClipPath(element, frameWidth, frameHeight);\n if (clipPath) {\n (clipPath as any).absolutePositioned = false;\n (clipPath as any).excludeFromExport = true;\n group.clipPath = clipPath;\n }\n }\n \n return group;\n}\n\nexport function createImageClipPath(\n element: CanvasElement,\n imgWidth: number,\n imgHeight: number\n): fabric.FabricObject | undefined {\n const clipShape = element.clipShape || 'none';\n if (clipShape === 'none') return undefined;\n\n switch (clipShape) {\n case 'rectangle':\n return new fabric.Rect({\n width: imgWidth,\n height: imgHeight,\n originX: 'center',\n originY: 'center',\n });\n case 'rounded': {\n // Calculate radius from ratio\n const rxRatio = element.rx || 0.1;\n const minDim = Math.min(imgWidth, imgHeight);\n const radius = rxRatio > 0.5 \n ? Math.min(rxRatio, imgWidth / 2, imgHeight / 2) // Old pixel value\n : Math.min(rxRatio * minDim, imgWidth / 2, imgHeight / 2); // New ratio value\n return new fabric.Rect({\n width: imgWidth,\n height: imgHeight,\n rx: radius,\n ry: radius,\n originX: 'center',\n originY: 'center',\n });\n }\n case 'circle': {\n const radius = Math.min(imgWidth, imgHeight) / 2;\n return new fabric.Ellipse({\n rx: radius,\n ry: radius,\n originX: 'center',\n originY: 'center',\n });\n }\n default:\n return undefined;\n }\n}\n\nexport async function loadImageAsync(\n element: CanvasElement,\n placeholder: fabric.FabricObject,\n fc: fabric.Canvas,\n fabricRef: MutableRefObject<fabric.Canvas | null>,\n syncLockedRef: MutableRefObject<boolean>,\n isTransforming: (canvas: fabric.Canvas) => boolean\n): Promise<fabric.FabricObject | void> {\n const imageUrl = element.src || element.imageUrl;\n if (!imageUrl) return;\n const nextSvgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : '';\n\n // CRITICAL: Early return for crop groups - don't remove/recreate if only crop/resize changed\n if (placeholder instanceof fabric.Group && (placeholder as any).__cropGroup) {\n const ct = (placeholder as any).__cropData;\n const existingImg = ct?._img;\n const existingSrc = (placeholder as any).__imageSrc;\n const existingSvgColorMap = (placeholder as any).__svgColorMap || '';\n\n if (existingImg && existingSrc === imageUrl && existingSvgColorMap === nextSvgColorMap) {\n return placeholder;\n }\n }\n\n try {\n let url = getProxiedImageUrl(imageUrl);\n const hasColorOverrides = Boolean(element.svgColorMap && Object.keys(element.svgColorMap).length > 0);\n const isInlineSvgDataUrl = imageUrl.startsWith('data:image/svg+xml');\n\n // Preserve imported SVG fidelity: do not re-normalize inline SVG data URLs unless\n // we explicitly need color remapping from the editor.\n if (isSvgImage(imageUrl, element.sourceFormat) && (!isInlineSvgDataUrl || hasColorOverrides)) {\n const normalized = await getNormalizedSvgUrl(imageUrl, element.svgColorMap, element.sourceFormat);\n if (normalized) url = normalized;\n }\n const img = await fabric.FabricImage.fromURL(url, { crossOrigin: 'anonymous' });\n\n if (!fabricRef.current) return;\n\n await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);\n\n const isHidden = !element.visible;\n img.set({\n originX: 'left',\n originY: 'top',\n visible: !isHidden,\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: !isHidden && element.selectable,\n evented: !isHidden && element.evented,\n hasControls: !isHidden && element.selectable,\n hasBorders: !isHidden && element.selectable,\n lockMovementX: !element.selectable,\n lockMovementY: !element.selectable,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n objectCaching: false,\n noScaleCache: true,\n });\n\n // Handle transform matrix or calculate scale based on imageFit\n if (element.transformMatrix && element.transformMatrix.length === 6) {\n img.set({\n left: element.left,\n top: element.top,\n scaleX: element.scaleX ?? 1,\n scaleY: element.scaleY ?? 1,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n });\n img.setCoords();\n } else {\n const imageFit = element.imageFit || 'cover';\n const imgNaturalWidth = img.width || 1;\n const imgNaturalHeight = img.height || 1;\n const elementWidth = Number(element.width);\n const elementHeight = Number(element.height);\n \n let baseScaleX: number;\n let baseScaleY: number;\n \n if (imageFit === 'fill') {\n baseScaleX = elementWidth / imgNaturalWidth;\n baseScaleY = elementHeight / imgNaturalHeight;\n } else if (imageFit === 'cover') {\n const scaleX = elementWidth / imgNaturalWidth;\n const scaleY = elementHeight / imgNaturalHeight;\n const scale = Math.max(scaleX, scaleY);\n baseScaleX = scale;\n baseScaleY = scale;\n } else if (imageFit === 'contain') {\n const scaleX = elementWidth / imgNaturalWidth;\n const scaleY = elementHeight / imgNaturalHeight;\n const scale = Math.min(scaleX, scaleY);\n baseScaleX = scale;\n baseScaleY = scale;\n } else {\n baseScaleX = 1;\n baseScaleY = 1;\n }\n \n if (imageFit === 'fill') {\n const finalScaleX = baseScaleX * (element.scaleX ?? 1);\n const finalScaleY = baseScaleY * (element.scaleY ?? 1);\n const visualW = Number(elementWidth) * (element.scaleX ?? 1);\n const visualH = Number(elementHeight) * (element.scaleY ?? 1);\n // Use center origin so scaling with corner handles keeps the image centered\n const centerX = element.left + visualW / 2;\n const centerY = element.top + visualH / 2;\n\n img.set({\n originX: 'center',\n originY: 'center',\n left: centerX,\n top: centerY,\n scaleX: finalScaleX,\n scaleY: finalScaleY,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n });\n \n const clipPath = createImageClipPath(element, img.width || 1, img.height || 1);\n if (clipPath) {\n img.clipPath = clipPath;\n }\n } else {\n const elementWidth = Number(element.width) * (element.scaleX ?? 1);\n const elementHeight = Number(element.height) * (element.scaleY ?? 1);\n const scaledImageWidth = imgNaturalWidth * baseScaleX;\n const scaledImageHeight = imgNaturalHeight * baseScaleY;\n const imageLeft = (elementWidth - scaledImageWidth) / 2;\n const imageTop = (elementHeight - scaledImageHeight) / 2;\n \n img.set({\n originX: 'left',\n originY: 'top',\n left: imageLeft,\n top: imageTop,\n scaleX: baseScaleX,\n scaleY: baseScaleY,\n angle: 0,\n skewX: 0,\n skewY: 0,\n });\n \n (img as any).__cropBaseScaleX = baseScaleX;\n (img as any).__cropBaseScaleY = baseScaleY;\n }\n }\n\n (img as any).__imageSrc = imageUrl;\n (img as any).__svgColorMap = nextSvgColorMap;\n\n const imageFit = element.imageFit || 'cover';\n let finalObject: fabric.FabricObject = img;\n \n if (imageFit !== 'fill') {\n const elementWidth = Number(element.width) * (element.scaleX ?? 1);\n const elementHeight = Number(element.height) * (element.scaleY ?? 1);\n const clipShape = element.clipShape || 'none';\n // rx is stored as ratio (0-0.5), convert old pixel values if needed\n let rxRatio = 0;\n if (clipShape === 'rounded') {\n const elementRx = element.rx || 0.1; // Default to 10% if not set\n // If rx > 0.5, it's likely an old pixel value, convert to ratio\n if (elementRx > 0.5) {\n const minDim = Math.min(elementWidth, elementHeight);\n rxRatio = Math.min(elementRx / minDim, 0.5); // Cap at 50%\n } else {\n rxRatio = elementRx;\n }\n }\n \n let shape: 'circle' | 'roundRect' | 'rect' = 'rect';\n if (clipShape === 'circle') {\n shape = 'circle';\n } else if (clipShape === 'rounded' || clipShape === 'rectangle') {\n shape = 'roundRect';\n }\n \n const existingCropGroup = fc.getObjects().find(\n (obj) => obj instanceof fabric.Group && \n (obj as any).__cropGroup && \n getObjectId(obj) === element.id\n ) as fabric.Group | undefined;\n \n let panX = 0.5;\n let panY = 0.5;\n let zoom = 1;\n if (existingCropGroup) {\n const existingImg = (existingCropGroup as any).__cropData?._img;\n if (existingImg) {\n panX = (existingImg as any)._ct?.panX ?? (existingImg as any).__panX ?? 0.5;\n panY = (existingImg as any)._ct?.panY ?? (existingImg as any).__panY ?? 0.5;\n zoom = (existingImg as any)._ct?.zoom ?? 1;\n }\n }\n \n const cropGroup = await createMaskedImageElement({\n image: img,\n frameW: elementWidth,\n frameH: elementHeight,\n left: element.left,\n top: element.top,\n angle: element.angle ?? 0,\n opacity: element.opacity ?? 1,\n visible: element.visible,\n shape,\n rx: rxRatio, // Pass ratio, not pixel value\n stroke: element.stroke,\n strokeWidth: element.strokeWidth,\n panX,\n panY,\n zoom,\n });\n\n // Store maintainResolution flag on the crop group (default to true)\n (cropGroup as any).__maintainResolution = element.maintainResolution !== false;\n \n // CRITICAL: Update layout again after setting maintainResolution\n // This ensures zoom is adjusted correctly for the current maintainResolution setting\n updateCoverLayout(cropGroup);\n\n setObjectData(cropGroup, element.id);\n (cropGroup as any).__imageSrc = imageUrl;\n (cropGroup as any).__svgColorMap = nextSvgColorMap;\n finalObject = cropGroup;\n } else {\n setObjectData(img, element.id);\n finalObject = img;\n }\n\n const idx = fc.getObjects().indexOf(placeholder);\n fc.remove(placeholder);\n if (idx >= 0) {\n fc.insertAt(idx, finalObject);\n } else {\n fc.add(finalObject);\n }\n\n fc.requestRenderAll();\n return finalObject;\n } catch (error) {\n // Failed to load image - keep placeholder\n return;\n }\n}\n","import * as fabric from 'fabric';\nimport { getProxiedImageUrl } from './canvasImageLoader';\n\n/**\n * Helper function to clamp a value between min and max\n */\nfunction clamp(v: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, v));\n}\n\n/**\n * Scale control/border size with zoom so handles look the same at any zoom level.\n * The canvas element is (width*zoom)×(height*zoom) px; controls are drawn in that pixel space.\n * So we set cornerSize = 10 * zoom so handles stay the same visual size relative to the canvas.\n */\nexport function applyControlSizeForZoom(canvas: fabric.Canvas, obj: fabric.Object) {\n if (!canvas || !obj) return;\n const z = canvas.getZoom() || 1;\n const size = Math.max(6, Math.round(10 * z));\n obj.set({\n cornerSize: size,\n borderScaleFactor: z,\n });\n obj.setCoords();\n}\n\n/**\n * Finalize crop group coords after layout updates\n * CRITICAL: Must be called after every layout mutation to ensure corner hit-testing works\n */\nfunction finalizeCropGroupCoords(g: fabric.Group) {\n const ct = (g as any).__cropData;\n if (!ct) return;\n \n // CRITICAL: Ensure group width/height exactly equals frameW/frameH BEFORE setCoords\n // If width/height don't match, controls land in wrong spot\n g.set({ width: ct.frameW, height: ct.frameH });\n \n // Mark dirty for repaint\n (g as any).dirty = true;\n \n // DO NOT call _calcBounds() — it recalculates group dimensions from children,\n // overriding our explicit frameW/frameH. For crop groups the frame size is the\n // source of truth, not the child image's natural/scaled bounds.\n\n if (typeof (g as any)._updateObjectsCoords === 'function') {\n (g as any)._updateObjectsCoords();\n }\n \n // CRITICAL: Always call setCoords after layout changes\n // This ensures corner controls are hittable\n g.setCoords();\n}\n\n/**\n * Recompute image scale & position so it covers the frame,\n * and apply pan (crop) without showing empty background.\n */\nexport function updateCoverLayout(g: fabric.Group) {\n const ct = (g as any).__cropData;\n if (!ct) return;\n\n const { frameW, frameH, shape, rx: rxRatio, _img: img, _border: border } = ct;\n if (!img) return;\n \n // Calculate actual rx from ratio (rxRatio is 0-0.5, representing 0-50% of smaller dimension)\n // If rxRatio is > 0.5, it's likely an old pixel value, so convert it\n const minDim = Math.min(frameW, frameH);\n let rx = rxRatio > 0.5 ? rxRatio : rxRatio * minDim;\n \n // CRITICAL: Cap rx at half of each dimension to ensure valid rounded corners\n // This ensures corners are uniform and don't exceed the maximum possible radius\n // Also ensure rx is at least 0 (can't be negative)\n rx = Math.max(0, Math.min(rx, frameW / 2, frameH / 2));\n\n // Update clipPath geometry to match frame\n // CRITICAL: If shape changed, recreate clipPath (Rect vs Ellipse)\n const needsNewClipPath = \n !g.clipPath ||\n (shape === 'circle' && !(g.clipPath instanceof fabric.Ellipse)) ||\n (shape !== 'circle' && !(g.clipPath instanceof fabric.Rect));\n \n if (needsNewClipPath) {\n // Recreate clipPath with correct type\n const clip = shape === 'circle'\n ? new fabric.Ellipse({\n rx: frameW / 2,\n ry: frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n })\n : new fabric.Rect({\n width: frameW,\n height: frameH,\n rx: rx, // rx is already calculated from ratio above\n ry: rx, // Ensure both are the same for uniform corners\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n (clip as any).absolutePositioned = false;\n (clip as any).excludeFromExport = true;\n // CRITICAL: Set coordinates and mark as dirty to ensure proper rendering\n clip.setCoords();\n (clip as any).dirty = true;\n g.clipPath = clip;\n } else if (g.clipPath && typeof (g.clipPath as any).set === 'function') {\n // Update existing clipPath properties\n if (shape === 'circle') {\n (g.clipPath as fabric.FabricObject).set({ \n rx: frameW / 2, \n ry: frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n // CRITICAL: Keep clipPath non-interactive\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n } else {\n // CRITICAL: For rounded rectangles, always recreate the clip path when rx changes\n // Fabric.js sometimes doesn't properly re-render rounded corners when rx/ry are updated\n // Recreating ensures all four corners are properly rounded\n const clipPathObj = g.clipPath as fabric.FabricObject;\n const currentRx = (clipPathObj as any).rx || 0;\n const currentWidth = (clipPathObj as any).width || 0;\n const currentHeight = (clipPathObj as any).height || 0;\n \n // Recreate if rx changed or dimensions changed significantly\n const rxChanged = Math.abs(currentRx - rx) > 0.01;\n const dimsChanged = Math.abs(currentWidth - frameW) > 0.1 || Math.abs(currentHeight - frameH) > 0.1;\n \n if (rxChanged || dimsChanged) {\n // Recreate clip path to ensure proper rounded corners rendering on all four corners\n const newClip = new fabric.Rect({\n width: frameW,\n height: frameH,\n rx: rx,\n ry: rx, // Ensure both are identical for uniform corners\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n (newClip as any).absolutePositioned = false;\n (newClip as any).excludeFromExport = true;\n newClip.setCoords();\n (newClip as any).dirty = true;\n g.clipPath = newClip;\n } else {\n // Just update if nothing significant changed\n clipPathObj.set({ \n width: frameW, \n height: frameH, \n rx: rx,\n ry: rx,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n clipPathObj.setCoords();\n (clipPathObj as any).dirty = true;\n }\n }\n // Ensure clipPath is group-local (not absolutePositioned) for live updates\n (g.clipPath as any).absolutePositioned = false;\n (g.clipPath as any).excludeFromExport = true;\n }\n\n // Update optional border geometry\n if (border) {\n if (shape === 'circle') {\n border.set({ rx: frameW / 2, ry: frameH / 2 });\n } else {\n // Calculate actual rx from ratio for border (use same calculation as clip path)\n const minDim = Math.min(frameW, frameH);\n let actualRx = rxRatio > 0.5 ? rxRatio : rxRatio * minDim;\n // Cap at half of each dimension to ensure valid rounded corners\n actualRx = Math.max(0, Math.min(actualRx, frameW / 2, frameH / 2));\n border.set({ width: frameW, height: frameH, rx: actualRx, ry: actualRx });\n }\n }\n\n // Initialize crop state if needed\n (img as any)._ct = (img as any)._ct || { panX: 0.5, panY: 0.5, zoom: 1 };\n \n // Migrate from old format if needed\n if ((img as any).__panX !== undefined || (img as any).__panY !== undefined) {\n (img as any)._ct.panX = (img as any).__panX ?? 0.5;\n (img as any)._ct.panY = (img as any).__panY ?? 0.5;\n }\n\n const iw = img.width || 1;\n const ih = img.height || 1;\n\n // fit === 'contain': scale image to fit inside frame (no clipping, full image visible)\n // otherwise cover: fill frame (may crop image)\n const fitContain = (ct as any).fit === 'contain';\n const baseScale = fitContain\n ? Math.min(frameW / iw, frameH / ih)\n : Math.max(frameW / iw, frameH / ih);\n\n // Zoom factor (>=1) - crop mode only; contain mode ignores zoom\n const zoom = fitContain ? 1 : Math.max(1, (img as any)._ct.zoom ?? 1);\n const finalScale = baseScale * zoom;\n\n img.set({\n scaleX: finalScale,\n scaleY: finalScale,\n originX: 'center',\n originY: 'center',\n });\n\n const dispW = iw * finalScale;\n const dispH = ih * finalScale;\n\n const overflowX = Math.max(0, dispW - frameW);\n const overflowY = Math.max(0, dispH - frameH);\n\n // Contain: always centered (no pan). Cover: use pan\n const panX = clamp((img as any)._ct.panX ?? 0.5, 0, 1);\n const panY = clamp((img as any)._ct.panY ?? 0.5, 0, 1);\n const offsetX = fitContain ? 0 : (overflowX > 0 ? -overflowX * (panX - 0.5) : 0);\n const offsetY = fitContain ? 0 : (overflowY > 0 ? -overflowY * (panY - 0.5) : 0);\n\n img.set({ left: offsetX, top: offsetY });\n\n // Mark everything dirty for live repaint during drag\n (g as any).dirty = true;\n (img as any).dirty = true;\n \n if (g.clipPath) {\n (g.clipPath as any).dirty = true;\n }\n\n // CRITICAL: Finalize coords after every layout update\n // This ensures corner controls are hittable\n finalizeCropGroupCoords(g);\n \n // CRITICAL: Request immediate render to show clip shape changes\n if (g.canvas) {\n g.setCoords();\n g.canvas.requestRenderAll();\n }\n}\n\n/**\n * Safely extract DOM event from Fabric eventData\n * Fabric v6 control handlers can have inconsistent event structure\n */\nfunction getDomEvent(eventData: any): any {\n return eventData?.e ?? eventData ?? null;\n}\n\n/**\n * Convert pointer delta to group-local delta (handles zoom/rotation)\n * CRITICAL: Store last pointer on target object, not transform (transform gets recreated)\n * \n * @param target - The fabric.Group object\n * @param eventData - The full eventData from Fabric control handler (NOT eventData.e)\n */\nexport function getLocalDeltaStable(\n target: fabric.Group,\n eventData: any\n): { localDx: number; localDy: number } {\n const canvas = target.canvas;\n if (!canvas) return { localDx: 0, localDy: 0 };\n\n // Safely extract DOM event - handle inconsistent Fabric v6 event structure\n const e = getDomEvent(eventData);\n if (!e) {\n return { localDx: 0, localDy: 0 };\n }\n\n // Get current pointer position\n const p = canvas.getPointer(e);\n \n // Store last pointer on target object (not transform - transform gets recreated)\n const last = (target as any).__lastPointerForCrop || p;\n (target as any).__lastPointerForCrop = p;\n\n // Calculate delta in canvas coordinates\n const dx = p.x - last.x;\n const dy = p.y - last.y;\n\n // Convert to group-local coordinates accounting for rotation\n const angle = fabric.util.degreesToRadians(target.angle || 0);\n const cos = Math.cos(-angle);\n const sin = Math.sin(-angle);\n\n return {\n localDx: dx * cos - dy * sin,\n localDy: dx * sin + dy * cos,\n };\n}\n\n/**\n * Pan image inside frame (crop)\n * This is called from canvas mouse:move during crop drag\n * CRITICAL: Updates panX/panY (source of truth), then calls updateCoverLayout to apply it\n */\nexport function panImageInside(g: fabric.Group, axis: 'x' | 'y', localDelta: number) {\n const img = (g as any).__cropData?._img;\n if (!img) return;\n\n const { frameW, frameH } = (g as any).__cropData;\n\n const iw = img.width || 1;\n const ih = img.height || 1;\n const s = img.scaleX || 1; // cover scale already applied\n\n const dispW = iw * s;\n const dispH = ih * s;\n const overflowX = Math.max(0, dispW - frameW);\n const overflowY = Math.max(0, dispH - frameH);\n\n // Initialize pan state if needed\n (img as any)._ct = (img as any)._ct || { panX: 0.5, panY: 0.5 };\n\n if (axis === 'x' && overflowX === 0) return;\n if (axis === 'y' && overflowY === 0) return;\n\n if (axis === 'x' && overflowX > 0) {\n const dPan = localDelta / overflowX;\n (img as any)._ct.panX = clamp((img as any)._ct.panX - dPan, 0, 1);\n }\n if (axis === 'y' && overflowY > 0) {\n const dPan = localDelta / overflowY;\n (img as any)._ct.panY = clamp((img as any)._ct.panY - dPan, 0, 1);\n }\n\n // Update layout - this applies panX/panY to img.left/top\n updateCoverLayout(g);\n \n // Mark dirty for live repaint\n (g as any).dirty = true;\n (img as any).dirty = true;\n g.setCoords();\n}\n\n/**\n * Convert world-space delta to the crop group's local (unrotated) axes.\n */\nfunction worldDeltaToLocal(dx: number, dy: number, angleDeg: number): { x: number; y: number } {\n const rad = fabric.util.degreesToRadians(angleDeg || 0);\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n return {\n x: dx * cos + dy * sin,\n y: -dx * sin + dy * cos,\n };\n}\n\n/**\n * Convert local-space delta to world-space delta using the crop group's rotation.\n */\nfunction localDeltaToWorld(dx: number, dy: number, angleDeg: number): { x: number; y: number } {\n const rad = fabric.util.degreesToRadians(angleDeg || 0);\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n return {\n x: dx * cos - dy * sin,\n y: dx * sin + dy * cos,\n };\n}\n\nfunction getOppositeAnchor(aCoords: any, corner: string): fabric.Point {\n return corner === 'br'\n ? aCoords.tl\n : corner === 'bl'\n ? aCoords.tr\n : corner === 'tr'\n ? aCoords.bl\n : aCoords.br;\n}\n\nfunction getCornerDefaultSigns(corner: string): { x: number; y: number } {\n switch (corner) {\n case 'tr':\n return { x: 1, y: -1 };\n case 'bl':\n return { x: -1, y: 1 };\n case 'br':\n return { x: 1, y: 1 };\n case 'tl':\n default:\n return { x: -1, y: -1 };\n }\n}\n\n/**\n * Returns resize cursor that rotates with object angle so handle direction matches visual orientation.\n */\nfunction getRotatedControlCursor(controlKey: string, target?: fabric.FabricObject | null): string {\n const cursors = ['ns-resize', 'nesw-resize', 'ew-resize', 'nwse-resize', 'ns-resize', 'nesw-resize', 'ew-resize', 'nwse-resize'];\n const baseOffsetByControl: Record<string, number> = {\n mt: 0,\n tr: 1,\n mr: 2,\n br: 3,\n mb: 4,\n bl: 5,\n ml: 6,\n tl: 7,\n };\n\n const baseOffset = baseOffsetByControl[controlKey] ?? 0;\n const angle = ((target?.angle ?? 0) % 360 + 360) % 360;\n const angleSteps = Math.round(angle / 45) % 8;\n return cursors[(baseOffset + angleSteps) % 8] ?? 'move';\n}\n\nconst makeRotatedCursorStyleHandler = (controlKey: string) => {\n return (_eventData: any, _control: fabric.Control, target: fabric.FabricObject) =>\n getRotatedControlCursor(controlKey, target);\n};\n\n/**\n * Resize frame from corner (corner handles) - resizes the frame box, image scales to cover\n */\nfunction resizeFrameFromCorner(\n eventData: any,\n transform: any,\n _x: number,\n _y: number\n): boolean {\n const g = transform.target as fabric.Group;\n const ct = (g as any).__cropData;\n if (!ct || !(g as any)._ct?.isCropGroup) return false;\n\n const canvas = g.canvas;\n if (!canvas) return false;\n\n const e = getDomEvent(eventData);\n if (!e) return false;\n\n const pointer = canvas.getPointer(e);\n g.setCoords();\n const a = (g as any).aCoords;\n if (!a) return false;\n\n const corner = transform.corner as string; // 'tl','tr','br','bl'\n const anchor = getOppositeAnchor(a, corner);\n\n const MIN_W = 20;\n const MIN_H = 20;\n const angle = g.angle || 0;\n\n const deltaWorldX = pointer.x - anchor.x;\n const deltaWorldY = pointer.y - anchor.y;\n const localDelta = worldDeltaToLocal(deltaWorldX, deltaWorldY, angle);\n const defaultSigns = getCornerDefaultSigns(corner);\n\n const signX = Math.abs(localDelta.x) < 0.001 ? defaultSigns.x : (localDelta.x >= 0 ? 1 : -1);\n const signY = Math.abs(localDelta.y) < 0.001 ? defaultSigns.y : (localDelta.y >= 0 ? 1 : -1);\n\n const newW = Math.max(MIN_W, Math.abs(localDelta.x));\n const newH = Math.max(MIN_H, Math.abs(localDelta.y));\n\n ct.frameW = newW;\n ct.frameH = newH;\n\n const centerLocal = {\n x: signX * (newW / 2),\n y: signY * (newH / 2),\n };\n const centerWorld = localDeltaToWorld(centerLocal.x, centerLocal.y, angle);\n\n g.set({\n left: anchor.x + centerWorld.x,\n top: anchor.y + centerWorld.y,\n originX: 'center',\n originY: 'center',\n width: newW,\n height: newH,\n });\n\n updateCoverLayout(g);\n canvas.requestRenderAll();\n return true;\n}\n\n/**\n * Resize frame from corner with aspect ratio locked (simple scale mode).\n * Updates ct.frameW/frameH so object:modified can persist the same way as crop handles.\n */\nfunction resizeFrameFromCornerUniform(\n eventData: any,\n transform: any,\n _x: number,\n _y: number\n): boolean {\n const g = transform.target as fabric.Group;\n const ct = (g as any).__cropData;\n if (!ct || !(g as any)._ct?.isCropGroup) return false;\n\n const canvas = g.canvas;\n if (!canvas) return false;\n\n const e = getDomEvent(eventData);\n if (!e) return false;\n\n const pointer = canvas.getPointer(e);\n g.setCoords();\n const a = (g as any).aCoords;\n if (!a) return false;\n\n const corner = transform.corner as string;\n const anchor = getOppositeAnchor(a, corner);\n const defaultSigns = getCornerDefaultSigns(corner);\n\n const MIN_SIZE = 20;\n const baseW = Math.max(MIN_SIZE, ct.frameW || g.width || 1);\n const baseH = Math.max(MIN_SIZE, ct.frameH || g.height || 1);\n\n const angle = g.angle || 0;\n const deltaWorldX = pointer.x - anchor.x;\n const deltaWorldY = pointer.y - anchor.y;\n const localDelta = worldDeltaToLocal(deltaWorldX, deltaWorldY, angle);\n\n const signX = Math.abs(localDelta.x) < 0.001 ? defaultSigns.x : (localDelta.x >= 0 ? 1 : -1);\n const signY = Math.abs(localDelta.y) < 0.001 ? defaultSigns.y : (localDelta.y >= 0 ? 1 : -1);\n\n const rawW = Math.max(MIN_SIZE, Math.abs(localDelta.x));\n const rawH = Math.max(MIN_SIZE, Math.abs(localDelta.y));\n const scaleFromW = rawW / baseW;\n const scaleFromH = rawH / baseH;\n const s = Math.min(scaleFromW, scaleFromH);\n const newW = Math.max(MIN_SIZE, baseW * s);\n const newH = Math.max(MIN_SIZE, baseH * s);\n\n ct.frameW = newW;\n ct.frameH = newH;\n\n const centerLocal = {\n x: signX * (newW / 2),\n y: signY * (newH / 2),\n };\n const centerWorld = localDeltaToWorld(centerLocal.x, centerLocal.y, angle);\n\n g.set({\n left: anchor.x + centerWorld.x,\n top: anchor.y + centerWorld.y,\n originX: 'center',\n originY: 'center',\n width: newW,\n height: newH,\n });\n\n updateCoverLayout(g);\n canvas.requestRenderAll();\n return true;\n}\n\n/**\n * Resize frame from side (side handles) - crops by resizing the mask box\n */\nfunction resizeFrameFromSide(\n g: fabric.Group,\n side: 'ml' | 'mr' | 'mt' | 'mb',\n localDx: number,\n localDy: number\n) {\n const ct = (g as any).__cropData;\n if (!ct) return;\n\n const minSize = 30;\n\n // Move center along local axis to keep dragged edge under cursor\n const moveCenterAlongLocalAxis = (obj: fabric.Group, localX: number, localY: number) => {\n const worldDelta = localDeltaToWorld(localX, localY, obj.angle || 0);\n obj.set({ left: (obj.left || 0) + worldDelta.x, top: (obj.top || 0) + worldDelta.y });\n };\n\n if (side === 'mr') {\n ct.frameW = Math.max(minSize, ct.frameW + localDx);\n moveCenterAlongLocalAxis(g, localDx / 2, 0);\n }\n if (side === 'ml') {\n ct.frameW = Math.max(minSize, ct.frameW - localDx);\n moveCenterAlongLocalAxis(g, localDx / 2, 0);\n }\n if (side === 'mb') {\n ct.frameH = Math.max(minSize, ct.frameH + localDy);\n moveCenterAlongLocalAxis(g, 0, localDy / 2);\n }\n if (side === 'mt') {\n ct.frameH = Math.max(minSize, ct.frameH - localDy);\n moveCenterAlongLocalAxis(g, 0, localDy / 2);\n }\n\n updateCoverLayout(g);\n}\n\n/**\n * Install custom controls for Canva-like masked image behavior\n */\nexport function installCanvaMaskControls(g: fabric.Group) {\n const ct = (g as any).__cropData;\n if (ct) (ct as any).fit = undefined; // cover (crop)\n\n g.setControlsVisibility({\n mt: true,\n mb: true,\n ml: true,\n mr: true,\n tl: true,\n tr: true,\n bl: true,\n br: true,\n mtr: true,\n });\n\n g.padding = 0;\n\n const makeSideControl = (\n key: 'ml' | 'mr' | 'mt' | 'mb',\n x: number,\n y: number,\n cursor: string,\n side: 'ml' | 'mr' | 'mt' | 'mb'\n ) => {\n return new fabric.Control({\n x,\n y,\n cursorStyle: cursor,\n cursorStyleHandler: makeRotatedCursorStyleHandler(key),\n actionName: 'crop',\n actionHandler: (eventData: any, transform: any) => {\n const t = transform.target as fabric.Group;\n const canvas = t.canvas;\n\n if (canvas && (canvas as any).__editLockRef) {\n (canvas as any).__editLockRef.current = true;\n }\n\n const { localDx, localDy } = getLocalDeltaStable(t, eventData);\n (t as any).__lockScaleDuringCrop = true;\n\n resizeFrameFromSide(t, side, localDx, localDy);\n\n t.canvas?.requestRenderAll();\n return true;\n },\n });\n };\n\n g.controls.ml = makeSideControl('ml', -0.5, 0, 'ew-resize', 'ml');\n g.controls.mr = makeSideControl('mr', 0.5, 0, 'ew-resize', 'mr');\n g.controls.mt = makeSideControl('mt', 0, -0.5, 'ns-resize', 'mt');\n g.controls.mb = makeSideControl('mb', 0, 0.5, 'ns-resize', 'mb');\n\n g.lockScalingX = true;\n g.lockScalingY = true;\n\n const makeCornerControl = (key: 'tl' | 'tr' | 'bl' | 'br', x: number, y: number, cursor: string) => {\n return new fabric.Control({\n x,\n y,\n cursorStyle: cursor,\n cursorStyleHandler: makeRotatedCursorStyleHandler(key),\n actionName: 'resize',\n actionHandler: (eventData: any, transform: any) => {\n const t = transform.target as fabric.Group;\n const canvas = t.canvas;\n\n if (canvas && (canvas as any).__editLockRef) {\n (canvas as any).__editLockRef.current = true;\n }\n\n return resizeFrameFromCorner(eventData, transform, 0, 0);\n },\n });\n };\n\n g.controls.tl = makeCornerControl('tl', -0.5, -0.5, 'nwse-resize');\n g.controls.tr = makeCornerControl('tr', 0.5, -0.5, 'nesw-resize');\n g.controls.bl = makeCornerControl('bl', -0.5, 0.5, 'nesw-resize');\n g.controls.br = makeCornerControl('br', 0.5, 0.5, 'nwse-resize');\n\n (g as any).__hasCustomControls = true;\n}\n\n/**\n * Simple scale mode (crop handles unchecked): corner handles only, aspect ratio locked.\n * Does not change fit/clip — image stays clipped to the frame (cover); only the controls change (no pan/zoom handles).\n * Custom control updates ct.frameW/frameH so save/reload works like crop handles.\n */\nexport function setSimpleScaleControls(g: fabric.Group) {\n if (!(g as any).__cropGroup) return;\n\n const ct = (g as any).__cropData;\n if (ct) {\n updateCoverLayout(g);\n }\n\n g.lockScalingX = true;\n g.lockScalingY = true;\n\n g.setControlsVisibility({\n mt: false,\n mb: false,\n ml: false,\n mr: false,\n tl: true,\n tr: true,\n bl: true,\n br: true,\n mtr: true,\n });\n\n g.padding = 0;\n\n const makeCornerControl = (key: 'tl' | 'tr' | 'bl' | 'br', x: number, y: number, cursor: string) =>\n new fabric.Control({\n x,\n y,\n cursorStyle: cursor,\n cursorStyleHandler: makeRotatedCursorStyleHandler(key),\n actionName: 'resize',\n actionHandler: (eventData: any, transform: any) => {\n const t = transform.target as fabric.Group;\n if (t.canvas && (t.canvas as any).__editLockRef) {\n (t.canvas as any).__editLockRef.current = true;\n }\n return resizeFrameFromCornerUniform(eventData, transform, 0, 0);\n },\n });\n\n g.controls.tl = makeCornerControl('tl', -0.5, -0.5, 'nwse-resize');\n g.controls.tr = makeCornerControl('tr', 0.5, -0.5, 'nesw-resize');\n g.controls.bl = makeCornerControl('bl', -0.5, 0.5, 'nesw-resize');\n g.controls.br = makeCornerControl('br', 0.5, 0.5, 'nwse-resize');\n\n (g as any).__hasCustomControls = true;\n g.setCoords();\n}\n\n/**\n * Creates a Canva-like masked image element:\n * - Group contains: image + frame (optional visible border)\n * - clipPath is the frame shape (absolutePositioned)\n * - Maintains \"object-fit: cover\" via img._ct = { panX, panY } normalized [0..1]\n */\nexport async function createMaskedImageElement({\n url,\n image,\n frameW = 300,\n frameH = 200,\n shape = 'roundRect', // 'circle' | 'roundRect' | 'rect'\n rx = 24, // only for roundRect\n stroke = null, // border if you want: \"#999\"\n strokeWidth = 0,\n left = 100,\n top = 100,\n angle = 0,\n opacity = 1,\n selectable = true,\n evented = true,\n visible = true,\n panX = 0.5,\n panY = 0.5,\n zoom = 1,\n}: {\n url?: string;\n image?: fabric.FabricImage;\n frameW?: number;\n frameH?: number;\n shape?: 'circle' | 'roundRect' | 'rect';\n rx?: number;\n stroke?: string | null;\n strokeWidth?: number;\n left?: number;\n top?: number;\n angle?: number;\n opacity?: number;\n selectable?: boolean;\n evented?: boolean;\n visible?: boolean;\n panX?: number;\n panY?: number;\n zoom?: number;\n}): Promise<fabric.Group> {\n // Use provided image or load from URL\n // CRITICAL: Use proxy URL to avoid CORS taint issues in export\n const img = image || (url ? await fabric.FabricImage.fromURL(getProxiedImageUrl(url), { crossOrigin: 'anonymous' }) : null);\n if (!img) {\n throw new Error('Either url or image must be provided');\n }\n\n // frame (clip)\n const frame =\n shape === 'circle'\n ? new fabric.Ellipse({\n rx: frameW / 2,\n ry: frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n })\n : new fabric.Rect({\n width: frameW,\n height: frameH,\n rx,\n ry: rx,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n });\n\n // Optional visible border (separate object so clipPath stays clean)\n const border =\n stroke\n ? (shape === 'circle'\n ? new fabric.Ellipse({\n rx: frameW / 2,\n ry: frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n fill: 'transparent',\n stroke,\n strokeWidth,\n selectable: false,\n evented: false,\n })\n : new fabric.Rect({\n width: frameW,\n height: frameH,\n rx,\n ry: rx,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n fill: 'transparent',\n stroke,\n strokeWidth,\n selectable: false,\n evented: false,\n }))\n : null;\n\n // Put image initially centered; we will scale+offset with updateCover()\n // IMPORTANT: children should not receive events - they should be non-interactive\n // CRITICAL: Don't set opacity on the image - opacity should only be on the group\n // Setting opacity on both group and child would double-apply it\n img.set({\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false, // group is selectable; image should not be\n evented: false,\n opacity: 1, // CRITICAL: Always 1 for child - group handles opacity\n // Preserve image-level caching settings if they exist\n objectCaching: (img as any).objectCaching ?? false,\n noScaleCache: (img as any).noScaleCache ?? true,\n });\n\n // Store crop state on image (normalized pan 0..1, zoom >= 1)\n (img as any)._ct = { panX: panX ?? 0.5, panY: panY ?? 0.5, zoom: zoom ?? 1 };\n\n // IMPORTANT: children should not receive events\n img.set({ selectable: false, evented: false });\n border?.set({ selectable: false, evented: false });\n\n // Ensure the crop group is marked and children are absolutely non-targetable\n img.set({ \n evented: false, \n selectable: false, \n hasControls: false, \n hasBorders: false \n });\n if (border) {\n border.set({ evented: false, selectable: false });\n }\n\n const groupItems = border ? [img, border] : [img];\n const g = new fabric.Group(groupItems, {\n left,\n top,\n originX: 'center',\n originY: 'center',\n angle,\n opacity,\n visible,\n evented: true,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n subTargetCheck: false, // don't look inside group\n interactive: true, // CRITICAL: Keep interactive for proper control detection\n perPixelTargetFind: false,\n objectCaching: false,\n });\n\n // CRITICAL: Disable Fabric v6's auto-layout that recalculates group dimensions from children.\n // Crop groups use explicit frameW/frameH — the layout manager would override those with\n // child-based bounds, making small images (e.g. bullets) grow to the SVG's natural size.\n if ((g as any).layoutManager) {\n // Use FixedLayout strategy if available, otherwise disable performLayout\n if (typeof fabric.FixedLayout === 'function') {\n (g as any).layoutManager.strategy = new fabric.FixedLayout();\n } else {\n // Fallback: stub out performLayout to prevent auto-resize\n (g as any).layoutManager.performLayout = () => {};\n }\n }\n\n // Attach our frame data to group. Default fit = 'cover' so image is clipped to frame (matches editor).\n (g as any).__cropData = {\n shape,\n rx,\n frameW,\n frameH,\n fit: 'cover', // clip image to frame; use 'contain' only when simple-scale mode is set\n _img: img,\n _border: border,\n };\n \n // Mark as crop group for promotion logic\n (g as any)._ct = (g as any)._ct || {};\n (g as any)._ct.isCropGroup = true;\n (g as any).__cropGroup = true; // Keep for backwards compatibility\n\n // ClipPath: use group-local positioning for better live resize behavior\n // Group-local is recommended over absolutePositioned for live updates\n const clip = shape === 'circle'\n ? new fabric.Ellipse({\n rx: frameW / 2,\n ry: frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n // CRITICAL: Make clipPath non-interactive so it doesn't intercept clicks\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n })\n : new fabric.Rect({\n width: frameW,\n height: frameH,\n rx,\n ry: rx,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n // CRITICAL: Make clipPath non-interactive so it doesn't intercept clicks\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n // Use group-local positioning (not absolutePositioned) for better live updates\n (clip as any).absolutePositioned = false;\n (clip as any).excludeFromExport = true; // Don't export clipPath\n g.clipPath = clip;\n \n // CRITICAL: Ensure crop group is always selectable and evented\n g.set({\n selectable: true,\n evented: true,\n hasControls: true,\n hasBorders: true,\n interactive: true,\n subTargetCheck: false,\n });\n\n // CRITICAL: Set group dimensions to frame size initially\n // This ensures controls are positioned correctly from the start\n g.set({\n width: frameW,\n height: frameH,\n });\n \n // setCoords() will recalculate everything needed for controls\n g.setCoords();\n\n // apply initial layout\n updateCoverLayout(g);\n\n // Default: simple scale (corners only). Enable \"Crop handles\" in Image Settings for crop mode.\n setSimpleScaleControls(g);\n\n return g;\n}\n","import * as fabric from 'fabric';\nimport { getObjectId } from './fabricUtils';\n\nexport interface SnapGuide {\n type: 'vertical' | 'horizontal';\n position: number;\n start?: number;\n end?: number;\n /** When true, guide is the current snap target (e.g. grid column/row boundary); draw with emphasis. */\n active?: boolean;\n /** Distance in pixels (e.g. from moving edge to guide); shown on the guide when set. */\n distance?: number;\n}\n\ninterface SnapPoints {\n left: number;\n right: number;\n top: number;\n bottom: number;\n centerX: number;\n centerY: number;\n}\n\nfunction getObjectSnapPoints(obj: fabric.FabricObject): SnapPoints {\n try {\n obj.setCoords();\n const bounds = obj.getBoundingRect();\n return {\n left: bounds.left,\n right: bounds.left + bounds.width,\n top: bounds.top,\n bottom: bounds.top + bounds.height,\n centerX: bounds.left + bounds.width / 2,\n centerY: bounds.top + bounds.height / 2,\n };\n } catch {\n const left = obj.left ?? 0;\n const top = obj.top ?? 0;\n const width = (obj.width ?? 0) * (obj.scaleX ?? 1);\n const height = (obj.height ?? 0) * (obj.scaleY ?? 1);\n return {\n left,\n right: left + width,\n top,\n bottom: top + height,\n centerX: left + width / 2,\n centerY: top + height / 2,\n };\n }\n}\n\ninterface SnapCandidate {\n delta: number;\n distance: number;\n guide: SnapGuide;\n}\n\nexport function calculateSnapGuides(\n movingObj: fabric.FabricObject,\n canvas: fabric.Canvas,\n canvasWidth: number,\n canvasHeight: number,\n snapToGuides: boolean,\n snapThreshold: number\n): { guides: SnapGuide[]; snapDx: number; snapDy: number } {\n if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };\n\n const threshold = snapThreshold || 5;\n const newGuides: SnapGuide[] = [];\n const horizontalSnaps: SnapCandidate[] = [];\n const verticalSnaps: SnapCandidate[] = [];\n\n const moving = getObjectSnapPoints(movingObj);\n const movingId = getObjectId(movingObj);\n const canvasCenterX = canvasWidth / 2;\n const canvasCenterY = canvasHeight / 2;\n\n const addVerticalSnap = (objPoint: number, targetPoint: number, guide: SnapGuide) => {\n const distance = Math.abs(objPoint - targetPoint);\n if (distance < threshold) {\n verticalSnaps.push({ delta: targetPoint - objPoint, distance, guide });\n }\n };\n\n const addHorizontalSnap = (objPoint: number, targetPoint: number, guide: SnapGuide) => {\n const distance = Math.abs(objPoint - targetPoint);\n if (distance < threshold) {\n horizontalSnaps.push({ delta: targetPoint - objPoint, distance, guide });\n }\n };\n\n // Canvas edge snapping\n addVerticalSnap(moving.left, 0, { type: 'vertical', position: 0 });\n addVerticalSnap(moving.right, canvasWidth, { type: 'vertical', position: canvasWidth });\n addHorizontalSnap(moving.top, 0, { type: 'horizontal', position: 0 });\n addHorizontalSnap(moving.bottom, canvasHeight, { type: 'horizontal', position: canvasHeight });\n\n addVerticalSnap(moving.centerX, canvasCenterX, { type: 'vertical', position: canvasCenterX });\n addHorizontalSnap(moving.centerY, canvasCenterY, { type: 'horizontal', position: canvasCenterY });\n\n const rawObjects = canvas.getObjects();\n const allObjects: fabric.FabricObject[] = [];\n\n for (const obj of rawObjects) {\n if (obj === movingObj) continue;\n const objId = getObjectId(obj);\n if (objId === '__background__') continue;\n if (movingObj.type === 'activeselection') {\n const activeSelection = movingObj as fabric.ActiveSelection;\n if (activeSelection.contains(obj)) continue;\n }\n if (movingObj instanceof fabric.Group && typeof (movingObj as any).getObjects === 'function') {\n const inner = (movingObj as fabric.Group).getObjects();\n if (inner.includes(obj)) continue;\n }\n if (objId && objId === movingId) continue;\n allObjects.push(obj);\n }\n\n for (const otherObj of allObjects) {\n const other = getObjectSnapPoints(otherObj);\n\n addVerticalSnap(moving.left, other.left, { type: 'vertical', position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });\n addVerticalSnap(moving.right, other.right, { type: 'vertical', position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });\n addVerticalSnap(moving.left, other.right, { type: 'vertical', position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });\n addVerticalSnap(moving.right, other.left, { type: 'vertical', position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });\n addVerticalSnap(moving.centerX, other.centerX, { type: 'vertical', position: other.centerX, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });\n\n addHorizontalSnap(moving.top, other.top, { type: 'horizontal', position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });\n addHorizontalSnap(moving.bottom, other.bottom, { type: 'horizontal', position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });\n addHorizontalSnap(moving.top, other.bottom, { type: 'horizontal', position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });\n addHorizontalSnap(moving.bottom, other.top, { type: 'horizontal', position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });\n addHorizontalSnap(moving.centerY, other.centerY, { type: 'horizontal', position: other.centerY, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });\n }\n\n // Only show guides for the closest snap (sibling/edge we're snapping to) with distance\n let snapDx = 0;\n let snapDy = 0;\n\n if (verticalSnaps.length > 0) {\n verticalSnaps.sort((a, b) => a.distance - b.distance);\n const bestSnap = verticalSnaps[0];\n snapDx = bestSnap.delta;\n newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });\n }\n\n if (horizontalSnaps.length > 0) {\n horizontalSnaps.sort((a, b) => a.distance - b.distance);\n const bestSnap = horizontalSnaps[0];\n snapDy = bestSnap.delta;\n newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });\n }\n\n return { guides: newGuides, snapDx, snapDy };\n}\n\nexport function calculateScaleSnapGuides(\n scalingObj: fabric.FabricObject,\n corner: string,\n canvas: fabric.Canvas,\n canvasWidth: number,\n canvasHeight: number,\n snapToGuides: boolean,\n snapThreshold: number\n): SnapGuide[] {\n if (!snapToGuides) return [];\n\n const threshold = snapThreshold || 5;\n const newGuides: SnapGuide[] = [];\n const scaling = getObjectSnapPoints(scalingObj);\n const scalingId = getObjectId(scalingObj);\n const canvasCenterX = canvasWidth / 2;\n const canvasCenterY = canvasHeight / 2;\n\n const resizingLeft = corner.includes('l');\n const resizingRight = corner.includes('r');\n const resizingTop = corner.includes('t');\n const resizingBottom = corner.includes('b');\n\n const checkVerticalSnap = (edgePosition: number, targetPosition: number, guide: SnapGuide): boolean => {\n const dist = Math.abs(edgePosition - targetPosition);\n if (dist < threshold) {\n newGuides.push({ ...guide, distance: Math.round(dist) });\n return true;\n }\n return false;\n };\n\n const checkHorizontalSnap = (edgePosition: number, targetPosition: number, guide: SnapGuide): boolean => {\n const dist = Math.abs(edgePosition - targetPosition);\n if (dist < threshold) {\n newGuides.push({ ...guide, distance: Math.round(dist) });\n return true;\n }\n return false;\n };\n\n // Canvas edge/center snapping\n if (resizingLeft) {\n checkVerticalSnap(scaling.left, 0, { type: 'vertical', position: 0 });\n checkVerticalSnap(scaling.left, canvasCenterX, { type: 'vertical', position: canvasCenterX });\n }\n if (resizingRight) {\n checkVerticalSnap(scaling.right, canvasWidth, { type: 'vertical', position: canvasWidth });\n checkVerticalSnap(scaling.right, canvasCenterX, { type: 'vertical', position: canvasCenterX });\n }\n if (resizingTop) {\n checkHorizontalSnap(scaling.top, 0, { type: 'horizontal', position: 0 });\n checkHorizontalSnap(scaling.top, canvasCenterY, { type: 'horizontal', position: canvasCenterY });\n }\n if (resizingBottom) {\n checkHorizontalSnap(scaling.bottom, canvasHeight, { type: 'horizontal', position: canvasHeight });\n checkHorizontalSnap(scaling.bottom, canvasCenterY, { type: 'horizontal', position: canvasCenterY });\n }\n\n // Element-to-element snapping\n const rawObjects = canvas.getObjects();\n \n for (const obj of rawObjects) {\n if (obj === scalingObj) continue;\n const objId = getObjectId(obj);\n if (objId === '__background__') continue;\n if (objId && objId === scalingId) continue;\n\n const other = getObjectSnapPoints(obj);\n\n if (resizingLeft) {\n checkVerticalSnap(scaling.left, other.left, { \n type: 'vertical', position: other.left,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n checkVerticalSnap(scaling.left, other.right, { \n type: 'vertical', position: other.right,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n checkVerticalSnap(scaling.left, other.centerX, { \n type: 'vertical', position: other.centerX,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n }\n if (resizingRight) {\n checkVerticalSnap(scaling.right, other.left, { \n type: 'vertical', position: other.left,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n checkVerticalSnap(scaling.right, other.right, { \n type: 'vertical', position: other.right,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n checkVerticalSnap(scaling.right, other.centerX, { \n type: 'vertical', position: other.centerX,\n start: Math.min(scaling.top, other.top),\n end: Math.max(scaling.bottom, other.bottom)\n });\n }\n\n if (resizingTop) {\n checkHorizontalSnap(scaling.top, other.top, { \n type: 'horizontal', position: other.top,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n checkHorizontalSnap(scaling.top, other.bottom, { \n type: 'horizontal', position: other.bottom,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n checkHorizontalSnap(scaling.top, other.centerY, { \n type: 'horizontal', position: other.centerY,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n }\n if (resizingBottom) {\n checkHorizontalSnap(scaling.bottom, other.top, { \n type: 'horizontal', position: other.top,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n checkHorizontalSnap(scaling.bottom, other.bottom, { \n type: 'horizontal', position: other.bottom,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n checkHorizontalSnap(scaling.bottom, other.centerY, { \n type: 'horizontal', position: other.centerY,\n start: Math.min(scaling.left, other.left),\n end: Math.max(scaling.right, other.right)\n });\n }\n }\n\n // Deduplicate guides\n const seen = new Set<string>();\n return newGuides.filter((guide) => {\n const key = `${guide.type}-${guide.position.toFixed(1)}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n\n/** Minimum size after snap so box doesn't collapse */\nconst MIN_SNAP_BOX = 20;\n\nconst SNAP_HYSTERESIS_PX = 6;\n\nexport type ActiveSnapState = { left?: number; right?: number; top?: number; bottom?: number } | null;\n\nexport interface ApplyScaleSnapOptions {\n /** Hysteresis (px) before releasing from a snap. Default 6. */\n hysteresis?: number;\n /** Ref to hold sticky snap values per edge; clear on mouseup. */\n activeSnapRef?: { current: ActiveSnapState };\n /** If true, round only the snapped edge(s) during drag; if false, round whole box (mouseup). */\n roundSnappedOnly?: boolean;\n /** Extra X positions (e.g. layout breakpoints: 1 col, 2 col width) for left/right edge snap. */\n verticalTargetsExtra?: number[];\n /** Extra Y positions (e.g. 1 row, 2 row height) for top/bottom edge snap. */\n horizontalTargetsExtra?: number[];\n}\n\n/**\n * Apply scale snapping to a box (e.g. group bounds rect during resize).\n * Only snaps the moving edge(s) for the given corner. Uses sticky snap + hysteresis to avoid ping-pong.\n */\nexport function applyScaleSnapToBox(\n box: { left: number; top: number; width: number; height: number },\n corner: string,\n canvas: fabric.Canvas,\n canvasWidth: number,\n canvasHeight: number,\n snapToGuides: boolean,\n snapThreshold: number,\n excludeObjectId?: string,\n options?: ApplyScaleSnapOptions\n): { left: number; top: number; width: number; height: number } {\n const hysteresis = options?.hysteresis ?? SNAP_HYSTERESIS_PX;\n const activeSnapRef = options?.activeSnapRef;\n const roundSnappedOnly = options?.roundSnappedOnly ?? false;\n\n if (!snapToGuides || !canvas) {\n if (roundSnappedOnly) return { ...box };\n return {\n left: Math.round(box.left),\n top: Math.round(box.top),\n width: Math.round(box.width),\n height: Math.round(box.height),\n };\n }\n const threshold = snapThreshold || 5;\n const releaseDist = threshold + hysteresis;\n const right = box.left + box.width;\n const bottom = box.top + box.height;\n const canvasCenterX = canvasWidth / 2;\n const canvasCenterY = canvasHeight / 2;\n\n const resizingLeft = corner.includes('l');\n const resizingRight = corner.includes('r');\n const resizingTop = corner.includes('t');\n const resizingBottom = corner.includes('b');\n\n const verticalTargets: number[] = [0, canvasWidth, canvasCenterX];\n const horizontalTargets: number[] = [0, canvasHeight, canvasCenterY];\n if (options?.verticalTargetsExtra?.length) verticalTargets.push(...options.verticalTargetsExtra);\n if (options?.horizontalTargetsExtra?.length) horizontalTargets.push(...options.horizontalTargetsExtra);\n\n const rawObjects = canvas.getObjects();\n for (const obj of rawObjects) {\n const objId = getObjectId(obj);\n if (objId === '__background__') continue;\n if (excludeObjectId && objId === excludeObjectId) continue;\n const pts = getObjectSnapPoints(obj);\n verticalTargets.push(pts.left, pts.right, pts.centerX);\n horizontalTargets.push(pts.top, pts.bottom, pts.centerY);\n }\n\n const findBestSnap = (edgePos: number, targets: number[]): { value: number; dist: number } => {\n let best = edgePos;\n let bestDist = threshold + 1;\n for (const t of targets) {\n const d = Math.abs(edgePos - t);\n if (d < bestDist) {\n bestDist = d;\n best = t;\n }\n }\n return { value: best, dist: bestDist };\n };\n\n let left = box.left;\n let top = box.top;\n let width = box.width;\n let height = box.height;\n let snappedLeft = false;\n let snappedRight = false;\n let snappedTop = false;\n let snappedBottom = false;\n\n if (resizingLeft) {\n const stick = activeSnapRef?.current?.left;\n if (stick !== undefined) {\n const dist = Math.abs(box.left - stick);\n if (dist <= hysteresis) {\n left = stick;\n width = right - left;\n snappedLeft = true;\n } else if (dist > releaseDist) {\n if (activeSnapRef.current) delete activeSnapRef.current.left;\n const { value, dist: d } = findBestSnap(box.left, verticalTargets);\n if (d <= threshold) {\n if (!activeSnapRef?.current) activeSnapRef!.current = {};\n activeSnapRef!.current!.left = value;\n left = value;\n width = right - left;\n snappedLeft = true;\n } else {\n left = box.left;\n width = right - left;\n }\n } else {\n left = box.left;\n width = right - left;\n }\n } else {\n const { value, dist } = findBestSnap(box.left, verticalTargets);\n if (dist <= threshold) {\n if (activeSnapRef) {\n if (!activeSnapRef.current) activeSnapRef.current = {};\n activeSnapRef.current.left = value;\n }\n left = value;\n width = right - left;\n snappedLeft = true;\n } else {\n left = box.left;\n width = right - left;\n }\n }\n }\n if (resizingRight) {\n const stick = activeSnapRef?.current?.right;\n if (stick !== undefined) {\n const dist = Math.abs(right - stick);\n if (dist <= hysteresis) {\n width = stick - left;\n snappedRight = true;\n } else if (dist > releaseDist) {\n if (activeSnapRef?.current) delete activeSnapRef.current.right;\n const { value, dist: d } = findBestSnap(right, verticalTargets);\n if (d <= threshold) {\n if (!activeSnapRef?.current) activeSnapRef!.current = {};\n activeSnapRef!.current!.right = value;\n width = value - left;\n snappedRight = true;\n } else {\n width = box.width;\n }\n } else {\n width = box.width;\n }\n } else {\n const { value, dist } = findBestSnap(right, verticalTargets);\n if (dist <= threshold) {\n if (activeSnapRef) {\n if (!activeSnapRef.current) activeSnapRef.current = {};\n activeSnapRef.current.right = value;\n }\n width = value - left;\n snappedRight = true;\n } else {\n width = box.width;\n }\n }\n }\n if (resizingTop) {\n const stick = activeSnapRef?.current?.top;\n if (stick !== undefined) {\n const dist = Math.abs(box.top - stick);\n if (dist <= hysteresis) {\n top = stick;\n height = bottom - top;\n snappedTop = true;\n } else if (dist > releaseDist) {\n if (activeSnapRef?.current) delete activeSnapRef.current.top;\n const { value, dist: d } = findBestSnap(box.top, horizontalTargets);\n if (d <= threshold) {\n if (!activeSnapRef?.current) activeSnapRef!.current = {};\n activeSnapRef!.current!.top = value;\n top = value;\n height = bottom - top;\n snappedTop = true;\n } else {\n top = box.top;\n height = bottom - top;\n }\n } else {\n top = box.top;\n height = bottom - top;\n }\n } else {\n const { value, dist } = findBestSnap(box.top, horizontalTargets);\n if (dist <= threshold) {\n if (activeSnapRef) {\n if (!activeSnapRef.current) activeSnapRef.current = {};\n activeSnapRef.current.top = value;\n }\n top = value;\n height = bottom - top;\n snappedTop = true;\n } else {\n top = box.top;\n height = bottom - top;\n }\n }\n }\n if (resizingBottom) {\n const stick = activeSnapRef?.current?.bottom;\n if (stick !== undefined) {\n const dist = Math.abs(bottom - stick);\n if (dist <= hysteresis) {\n height = stick - top;\n snappedBottom = true;\n } else if (dist > releaseDist) {\n if (activeSnapRef?.current) delete activeSnapRef.current.bottom;\n const { value, dist: d } = findBestSnap(bottom, horizontalTargets);\n if (d <= threshold) {\n if (!activeSnapRef?.current) activeSnapRef!.current = {};\n activeSnapRef!.current!.bottom = value;\n height = value - top;\n snappedBottom = true;\n } else {\n height = box.height;\n }\n } else {\n height = box.height;\n }\n } else {\n const { value, dist } = findBestSnap(bottom, horizontalTargets);\n if (dist <= threshold) {\n if (activeSnapRef) {\n if (!activeSnapRef.current) activeSnapRef.current = {};\n activeSnapRef.current.bottom = value;\n }\n height = value - top;\n snappedBottom = true;\n } else {\n height = box.height;\n }\n }\n }\n\n width = Math.max(MIN_SNAP_BOX, width);\n height = Math.max(MIN_SNAP_BOX, height);\n if (resizingLeft && !resizingRight) left = right - width;\n if (resizingTop && !resizingBottom) top = bottom - height;\n\n if (roundSnappedOnly) {\n return {\n left: snappedLeft ? Math.round(left) : left,\n top: snappedTop ? Math.round(top) : top,\n width: snappedRight ? Math.round(width) : width,\n height: snappedBottom ? Math.round(height) : height,\n };\n }\n return {\n left: Math.round(left),\n top: Math.round(top),\n width: Math.round(width),\n height: Math.round(height),\n };\n}\n","import { ShapeType } from '@/types/editor';\n\nexport type CoreShapeType = 'rounded-rect' | 'circle' | 'triangle';\nexport type NormalizedShapeType = CoreShapeType | 'unsupported';\n\nexport const TRIANGLE_STROKE_MITER_LIMIT = 1_000_000;\n\nexport interface RoundedRectRadiiInput {\n rx?: number;\n rxTL?: number;\n rxTR?: number;\n rxBR?: number;\n rxBL?: number;\n}\n\nexport interface RoundedRectRadii {\n tl: number;\n tr: number;\n br: number;\n bl: number;\n}\n\nconst toSafeNumber = (value: number | undefined, fallback = 0): number =>\n Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;\n\nexport function normalizeShapeType(shapeType: ShapeType | string | undefined): NormalizedShapeType {\n if (shapeType === 'rectangle' || shapeType === 'rounded-rect') return 'rounded-rect';\n if (shapeType === 'circle') return 'circle';\n if (shapeType === 'triangle') return 'triangle';\n return 'unsupported';\n}\n\nexport function hasIndividualCornerRadii(radii: RoundedRectRadiiInput): boolean {\n return (radii.rxTL != null && radii.rxTL > 0) || (radii.rxTR != null && radii.rxTR > 0) || (radii.rxBR != null && radii.rxBR > 0) || (radii.rxBL != null && radii.rxBL > 0);\n}\n\nexport function getRoundedRectRadii(\n width: number,\n height: number,\n radii: RoundedRectRadiiInput\n): RoundedRectRadii {\n const maxR = Math.max(0, Math.min(width, height) / 2);\n const uniform = toSafeNumber(radii.rx, 0);\n\n return {\n tl: Math.min(toSafeNumber(radii.rxTL, uniform), maxR),\n tr: Math.min(toSafeNumber(radii.rxTR, uniform), maxR),\n br: Math.min(toSafeNumber(radii.rxBR, uniform), maxR),\n bl: Math.min(toSafeNumber(radii.rxBL, uniform), maxR),\n };\n}\n\nexport function buildRoundedRectPath(\n width: number,\n height: number,\n radii: RoundedRectRadii\n): string {\n const { tl, tr, br, bl } = radii;\n\n return [\n `M ${tl} 0`,\n `L ${width - tr} 0`,\n tr > 0 ? `A ${tr} ${tr} 0 0 1 ${width} ${tr}` : `L ${width} 0`,\n `L ${width} ${height - br}`,\n br > 0 ? `A ${br} ${br} 0 0 1 ${width - br} ${height}` : `L ${width} ${height}`,\n `L ${bl} ${height}`,\n bl > 0 ? `A ${bl} ${bl} 0 0 1 0 ${height - bl}` : `L 0 ${height}`,\n `L 0 ${tl}`,\n tl > 0 ? `A ${tl} ${tl} 0 0 1 ${tl} 0` : `L 0 0`,\n 'Z',\n ].join(' ');\n}\n\nexport function getTrianglePoints(x: number, y: number, width: number, height: number): { x: number; y: number }[] {\n return [\n { x: x + width / 2, y },\n { x: x + width, y: y + height },\n { x, y: y + height },\n ];\n}\n\n/**\n * Build a rounded triangle SVG path.\n * Vertices: top-center, bottom-right, bottom-left.\n * rTop = radius at top vertex, rBR = bottom-right, rBL = bottom-left.\n * Each corner is replaced with a quadratic bezier curve when radius > 0.\n */\nexport function buildRoundedTrianglePath(\n w: number,\n h: number,\n rTop: number,\n rBR: number,\n rBL: number\n): string {\n // Triangle vertices (local coords, origin top-left)\n const A = { x: w / 2, y: 0 }; // top\n const B = { x: w, y: h }; // bottom-right\n const C = { x: 0, y: h }; // bottom-left\n\n const maxR = Math.min(w, h) / 2;\n const rt = Math.min(Math.max(0, rTop), maxR);\n const rbr = Math.min(Math.max(0, rBR), maxR);\n const rbl = Math.min(Math.max(0, rBL), maxR);\n\n // Helper: get unit vector from p1 to p2\n const unitVec = (p1: { x: number; y: number }, p2: { x: number; y: number }) => {\n const dx = p2.x - p1.x;\n const dy = p2.y - p1.y;\n const len = Math.sqrt(dx * dx + dy * dy);\n return len > 0 ? { x: dx / len, y: dy / len } : { x: 0, y: 0 };\n };\n\n // For each corner, we offset along the two adjacent edges by `r`\n // and draw a quadratic bezier through the original vertex.\n const cornerSegment = (\n prev: { x: number; y: number },\n curr: { x: number; y: number },\n next: { x: number; y: number },\n r: number\n ): string => {\n if (r <= 0) {\n return `L ${curr.x} ${curr.y}`;\n }\n const uPrev = unitVec(curr, prev);\n const uNext = unitVec(curr, next);\n const startX = curr.x + uPrev.x * r;\n const startY = curr.y + uPrev.y * r;\n const endX = curr.x + uNext.x * r;\n const endY = curr.y + uNext.y * r;\n return `L ${startX} ${startY} Q ${curr.x} ${curr.y} ${endX} ${endY}`;\n };\n\n // Start from mid-point of edge C→A (bottom-left to top)\n const uCA = unitVec(C, A);\n const startX = C.x + uCA.x * (rbl > 0 ? rbl : 0);\n const startY = C.y + uCA.y * (rbl > 0 ? rbl : 0);\n\n // If bottom-left has no rounding, start from C directly \n const actualStartX = rbl > 0 ? startX : C.x;\n const actualStartY = rbl > 0 ? startY : C.y;\n\n const parts: string[] = [\n `M ${actualStartX} ${actualStartY}`,\n cornerSegment(C, A, B, rt), // top corner\n cornerSegment(A, B, C, rbr), // bottom-right corner\n cornerSegment(B, C, A, rbl), // bottom-left corner\n 'Z',\n ];\n\n return parts.join(' ');\n}\n","import * as fabric from 'fabric';\nimport { CanvasElement } from '@/types/editor';\nimport { setObjectData } from './fabricUtils';\nimport { getMinTextWidth } from './textMeasurement';\nimport {\n buildRoundedRectPath as buildCanonicalRoundedRectPath,\n getRoundedRectRadii,\n hasIndividualCornerRadii,\n normalizeShapeType,\n buildRoundedTrianglePath,\n TRIANGLE_STROKE_MITER_LIMIT,\n} from './shapeGeometry';\nimport { applyTriangleCacheSettings } from './triangleCache';\n\nexport function buildRoundedRectPath(w: number, h: number, tl: number, tr: number, br: number, bl: number): string {\n return buildCanonicalRoundedRectPath(w, h, getRoundedRectRadii(w, h, { rxTL: tl, rxTR: tr, rxBR: br, rxBL: bl }));\n}\n\n\nexport function createShape(element: CanvasElement): fabric.FabricObject | null {\n const fill = element.fill || 'transparent';\n const stroke = element.stroke || 'transparent';\n const strokeWidth = element.strokeWidth || 0;\n const w = Number(element.width);\n const h = Number(element.height);\n const shapeType = normalizeShapeType(element.shapeType);\n\n switch (shapeType) {\n case 'rounded-rect': {\n if (hasIndividualCornerRadii(element)) {\n const radii = getRoundedRectRadii(w, h, {\n rx: element.rx ?? 0,\n rxTL: element.rxTL,\n rxTR: element.rxTR,\n rxBR: element.rxBR,\n rxBL: element.rxBL,\n });\n const path = buildCanonicalRoundedRectPath(w, h, radii);\n return new fabric.Path(path, {\n fill,\n stroke,\n strokeWidth,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n objectCaching: true,\n });\n }\n\n const maxR = Math.min(w, h) / 2;\n const rx = Math.min(Math.max(0, Number(element.rx ?? 0)), maxR);\n const rySource = element.ry ?? element.rx ?? 0;\n const ry = Math.min(Math.max(0, Number(rySource)), maxR);\n\n if (rx === 0 && ry === 0) {\n return new fabric.Rect({\n width: w,\n height: h,\n fill,\n stroke,\n strokeWidth,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n objectCaching: true,\n });\n }\n\n return new fabric.Rect({\n width: w,\n height: h,\n fill,\n stroke,\n strokeWidth,\n rx,\n ry,\n strokeUniform: true,\n strokeLineJoin: 'round',\n strokeLineCap: 'round',\n objectCaching: true,\n });\n }\n\n case 'circle': {\n const radius = Math.min(w, h) / 2;\n return new fabric.Circle({\n radius,\n fill,\n stroke,\n strokeWidth,\n originX: 'left',\n originY: 'top',\n objectCaching: true,\n strokeUniform: true,\n strokeLineJoin: 'round',\n strokeLineCap: 'round',\n lockUniScaling: true,\n });\n }\n\n case 'triangle': {\n const rTop = Math.max(0, Number(element.triRTop ?? 0));\n const rBR = Math.max(0, Number(element.triRBR ?? 0));\n const rBL = Math.max(0, Number(element.triRBL ?? 0));\n const path = buildRoundedTrianglePath(w, h, rTop, rBR, rBL);\n\n const triangle = new fabric.Path(path, {\n fill,\n stroke,\n strokeWidth,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n objectCaching: false,\n });\n\n return triangle;\n }\n\n default:\n return new fabric.Rect({\n width: w,\n height: h,\n fill,\n stroke,\n strokeWidth,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n objectCaching: true,\n });\n }\n}\n\nexport function createText(element: CanvasElement): fabric.FabricObject {\n const overflowPolicy = element.overflowPolicy || 'grow-and-push';\n let text = element.text || 'Text';\n let fontSize = element.fontSize || 16;\n const minFontSize = element.minFontSize || 8;\n const maxLines = element.maxLines || 3;\n \n const baseWidth = element.width && element.width > 0 ? element.width : 200;\n const baseHeight = element.height;\n const fixedWidth = Math.max(baseWidth, 1);\n const splitByGrapheme = overflowPolicy === 'auto-shrink'\n ? false\n : (element.splitByGrapheme ?? (element.wordWrap === 'break-word'));\n \n // For auto-shrink: keep width fixed, prevent implicit wrapping, and reduce font size until it fits.\n if (overflowPolicy === 'auto-shrink') {\n const explicitLineCount = Math.max(1, text.split('\\n').length);\n while (fontSize > 1) {\n const testTextbox = new fabric.Textbox(text, {\n width: fixedWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n splitByGrapheme: false,\n });\n testTextbox.initDimensions();\n \n const textHeight = testTextbox.height || 0;\n const renderedLineCount = testTextbox.textLines?.length || 1;\n const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;\n const fitsHeight = !baseHeight || textHeight <= baseHeight;\n // Also check that no line overflows the fixed width (single long word case)\n const actualTextboxWidth = testTextbox.width ?? fixedWidth;\n const widthDidGrow = actualTextboxWidth > fixedWidth + 0.5;\n const lineWidths = (testTextbox as any).__lineWidths as number[] | undefined;\n const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;\n const fitsWidth = !widthDidGrow && maxLineWidth <= fixedWidth + 1;\n\n if (hasNoImplicitWrap && fitsHeight && fitsWidth) {\n break;\n }\n fontSize--;\n }\n }\n \n // For max-lines-ellipsis: truncate text to fit max lines\n if (overflowPolicy === 'max-lines-ellipsis') {\n const originalText = element.text || 'Text';\n const countLines = (testText: string): number => {\n const tb = new fabric.Textbox(testText, {\n width: element.width,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n lineHeight: element.lineHeight || 1.2,\n splitByGrapheme: element.splitByGrapheme ?? (element.wordWrap === 'break-word'),\n });\n tb.initDimensions();\n return tb.textLines?.length || 1;\n };\n \n let low = 0;\n let high = originalText.length;\n let result = originalText;\n \n while (low < high) {\n const mid = Math.floor((low + high + 1) / 2);\n const testText = originalText.slice(0, mid) + '...';\n const lineCount = countLines(testText);\n \n if (lineCount <= maxLines) {\n low = mid;\n result = testText;\n } else {\n high = mid - 1;\n }\n }\n \n if (result.endsWith('...') && result.length > 3) {\n const beforeEllipsis = result.slice(0, -3).trimEnd();\n result = beforeEllipsis + '...';\n }\n \n text = result;\n }\n \n const targetWidth = overflowPolicy === 'auto-shrink'\n ? fixedWidth\n : Math.max(baseWidth, getMinTextWidth(element));\n const targetScaleX = element.scaleX ?? 1;\n const targetScaleY = element.scaleY ?? 1;\n\n const textbox = new fabric.Textbox(text, {\n width: targetWidth,\n scaleX: targetScaleX,\n scaleY: targetScaleY,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fill: element.fill || '#1a1a1a',\n fontWeight: (element.fontWeight as number) || 400,\n textAlign: (element.textAlign as any) || 'left',\n fontStyle: element.fontStyle || 'normal',\n underline: element.underline ?? false,\n linethrough: element.linethrough ?? false,\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n objectCaching: false,\n noScaleCache: true,\n splitByGrapheme,\n });\n \n textbox.initDimensions();\n \n textbox.set({ \n width: targetWidth,\n scaleX: targetScaleX,\n scaleY: targetScaleY,\n });\n textbox.setCoords();\n \n const widthAfterSet = textbox.width ?? 0;\n const scaleXAfterSet = textbox.scaleX ?? 1;\n const scaleYAfterSet = textbox.scaleY ?? 1;\n \n if (Math.abs(widthAfterSet - targetWidth) > 0.01 || \n Math.abs(scaleXAfterSet - targetScaleX) > 0.01 ||\n Math.abs(scaleYAfterSet - targetScaleY) > 0.01) {\n (textbox as any).width = targetWidth;\n (textbox as any).scaleX = targetScaleX;\n (textbox as any).scaleY = targetScaleY;\n textbox.setCoords();\n }\n \n textbox.dirty = true;\n return textbox;\n}\n\nexport function createLine(element: CanvasElement): fabric.FabricObject {\n const lineLength = element.width * (element.scaleX ?? 1);\n return new fabric.Line([0, 0, lineLength, 0], {\n stroke: element.stroke || '#1a1a1a',\n strokeWidth: element.strokeWidth || 2,\n strokeDashArray: element.strokeDashArray,\n scaleX: 1,\n scaleY: 1,\n });\n}\n\nexport function createFabricObject(element: CanvasElement): fabric.FabricObject | null {\n let obj: fabric.FabricObject | null = null;\n\n switch (element.type) {\n case 'shape':\n obj = createShape(element);\n break;\n case 'text':\n obj = createText(element);\n break;\n case 'line':\n obj = createLine(element);\n break;\n default:\n return null;\n }\n\n if (obj) {\n obj.set({\n originX: 'left',\n originY: 'top',\n });\n \n const isLine = element.type === 'line';\n obj.set({\n left: element.left,\n top: element.top,\n angle: element.angle ?? 0,\n skewX: isLine ? 0 : (element.skewX ?? 0),\n skewY: isLine ? 0 : (element.skewY ?? 0),\n scaleX: isLine ? 1 : (element.scaleX ?? 1),\n scaleY: isLine ? 1 : (element.scaleY ?? 1),\n opacity: element.opacity ?? 1,\n selectable: element.selectable !== false,\n evented: element.evented !== false,\n visible: element.visible !== false,\n hasControls: element.selectable !== false,\n hasBorders: element.selectable !== false,\n lockMovementX: element.selectable === false,\n lockMovementY: element.selectable === false,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n setObjectData(obj, element.id);\n }\n\n return obj;\n}\n\nexport function createFabricObjectForGroupMember(element: CanvasElement): fabric.FabricObject | null {\n let obj: fabric.FabricObject | null = null;\n\n switch (element.type) {\n case 'shape':\n obj = createShape(element);\n break;\n case 'text':\n obj = createText(element);\n break;\n case 'line':\n obj = createLine(element);\n break;\n case 'image':\n obj = new fabric.Rect({\n width: element.width,\n height: element.height,\n fill: 'transparent',\n stroke: 'transparent',\n strokeWidth: 0,\n });\n break;\n default:\n return null;\n }\n\n if (obj) {\n const isLine = element.type === 'line';\n const isText = element.type === 'text';\n const skipScale = isLine || isText;\n obj.set({\n originX: 'left',\n originY: 'top',\n left: element.left,\n top: element.top,\n angle: element.angle ?? 0,\n skewX: skipScale ? 0 : (element.skewX ?? 0),\n skewY: skipScale ? 0 : (element.skewY ?? 0),\n scaleX: skipScale ? 1 : (element.scaleX ?? 1),\n scaleY: skipScale ? 1 : (element.scaleY ?? 1),\n opacity: element.opacity ?? 1,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n setObjectData(obj, element.id);\n }\n\n return obj;\n}\n","/**\n * QR Code smart element renderer — isomorphic.\n * QR library is injected to avoid environment-specific imports.\n */\nimport type { SmartElementDefinition } from '../types';\n\nexport interface QRCodeLibrary {\n create: (text: string, options: { errorCorrectionLevel: string }) => {\n modules: { size: number; data: Uint8Array | number[] };\n };\n}\n\n/** Module-level injection point for the QR code library */\nlet _qrLib: QRCodeLibrary | null = null;\n\nexport function injectQRCodeLibrary(lib: QRCodeLibrary) {\n _qrLib = lib;\n}\n\nfunction generateQRMatrix(text: string, errorCorrectionLevel: string): { modules: boolean[][]; size: number } {\n const lib = _qrLib;\n if (!lib) return { modules: [[true]], size: 1 };\n try {\n const qr = lib.create(text, { errorCorrectionLevel });\n const size = qr.modules.size;\n const data = qr.modules.data;\n const modules: boolean[][] = [];\n for (let row = 0; row < size; row++) {\n const rowData: boolean[] = [];\n for (let col = 0; col < size; col++) {\n rowData.push(data[row * size + col] === 1);\n }\n modules.push(rowData);\n }\n return { modules, size };\n } catch (e) {\n console.warn('QR generation failed:', e);\n return { modules: [[true]], size: 1 };\n }\n}\n\nfunction renderQRSvg(props: Record<string, any>, width: number, height: number): string {\n const value = props.value || 'https://example.com';\n const fgColor = props.fgColor || '#000000';\n const bgColor = props.bgColor || '#FFFFFF';\n const errorCorrection = props.errorCorrection || 'M';\n const margin = props.margin ?? 2;\n\n const { modules, size } = generateQRMatrix(value, errorCorrection);\n const totalSize = size + margin * 2;\n const cellSize = Math.min(width, height) / totalSize;\n\n let pathData = '';\n for (let row = 0; row < size; row++) {\n for (let col = 0; col < size; col++) {\n if (modules[row]?.[col]) {\n const x = (col + margin) * cellSize;\n const y = (row + margin) * cellSize;\n pathData += `M${x},${y}h${cellSize}v${cellSize}h${-cellSize}Z `;\n }\n }\n }\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n <rect width=\"${width}\" height=\"${height}\" fill=\"${bgColor}\"/>\n <path d=\"${pathData}\" fill=\"${fgColor}\"/>\n</svg>`;\n}\n\nexport const qrCodeDefinition: SmartElementDefinition = {\n type: 'qrcode',\n label: 'QR Code',\n icon: 'QrCode',\n defaultWidth: 150,\n defaultHeight: 150,\n properties: [\n { key: 'value', label: 'Value / URL', type: 'text', default: 'https://example.com', bindable: true },\n { key: 'fgColor', label: 'Foreground', type: 'color', default: '#000000' },\n { key: 'bgColor', label: 'Background', type: 'color', default: '#FFFFFF' },\n {\n key: 'errorCorrection', label: 'Error Correction', type: 'select', default: 'M',\n options: [\n { value: 'L', label: 'Low (7%)' },\n { value: 'M', label: 'Medium (15%)' },\n { value: 'Q', label: 'Quartile (25%)' },\n { value: 'H', label: 'High (30%)' },\n ],\n },\n { key: 'margin', label: 'Margin', type: 'number', default: 2, min: 0, max: 10, step: 1 },\n ],\n render: renderQRSvg,\n};\n","import type { SmartElementDefinition } from '../types';\n\nfunction renderStarPath(cx: number, cy: number, outerR: number, innerR: number): string {\n const points: [number, number][] = [];\n for (let i = 0; i < 10; i++) {\n const angle = (Math.PI / 2) * -1 + (Math.PI / 5) * i;\n const r = i % 2 === 0 ? outerR : innerR;\n points.push([cx + r * Math.cos(angle), cy + r * Math.sin(angle)]);\n }\n return 'M' + points.map(p => `${p[0].toFixed(2)},${p[1].toFixed(2)}`).join('L') + 'Z';\n}\n\nfunction renderRatingSvg(props: Record<string, any>, width: number, height: number): string {\n const value = Math.max(0, Math.min(props.maxStars || 5, props.value ?? 3));\n const maxStars = props.maxStars || 5;\n const filledColor = props.filledColor || '#FBBF24';\n const emptyColor = props.emptyColor || '#D1D5DB';\n const starSpacing = props.spacing ?? 4;\n\n const totalSpacing = starSpacing * (maxStars - 1);\n const starSize = Math.min((width - totalSpacing) / maxStars, height);\n const outerR = starSize / 2;\n const innerR = outerR * 0.4;\n const cy = height / 2;\n\n let stars = '';\n for (let i = 0; i < maxStars; i++) {\n const cx = outerR + i * (starSize + starSpacing);\n const path = renderStarPath(cx, cy, outerR, innerR);\n if (i < Math.floor(value)) {\n stars += `<path d=\"${path}\" fill=\"${filledColor}\"/>`;\n } else if (i < value) {\n const fraction = value - Math.floor(value);\n const clipId = `clip-${i}`;\n stars += `<defs><clipPath id=\"${clipId}\"><rect x=\"${cx - outerR}\" y=\"0\" width=\"${starSize * fraction}\" height=\"${height}\"/></clipPath></defs>`;\n stars += `<path d=\"${path}\" fill=\"${emptyColor}\"/>`;\n stars += `<path d=\"${path}\" fill=\"${filledColor}\" clip-path=\"url(#${clipId})\"/>`;\n } else {\n stars += `<path d=\"${path}\" fill=\"${emptyColor}\"/>`;\n }\n }\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n ${stars}\n</svg>`;\n}\n\nexport const ratingDefinition: SmartElementDefinition = {\n type: 'rating',\n label: 'Star Rating',\n icon: 'Star',\n defaultWidth: 200,\n defaultHeight: 40,\n properties: [\n { key: 'value', label: 'Rating Value', type: 'number', default: 3.5, min: 0, max: 10, step: 0.5, bindable: true },\n { key: 'maxStars', label: 'Max Stars', type: 'number', default: 5, min: 1, max: 10, step: 1 },\n { key: 'filledColor', label: 'Filled Color', type: 'color', default: '#FBBF24' },\n { key: 'emptyColor', label: 'Empty Color', type: 'color', default: '#D1D5DB' },\n { key: 'spacing', label: 'Star Spacing', type: 'number', default: 4, min: 0, max: 20, step: 1 },\n ],\n render: renderRatingSvg,\n};\n","import type { SmartElementDefinition } from '../types';\n\nfunction renderProgressSvg(props: Record<string, any>, width: number, height: number): string {\n const value = Math.max(0, Math.min(100, props.value ?? 65));\n const trackColor = props.trackColor || '#E5E7EB';\n const fillColor = props.fillColor || '#3B82F6';\n const borderRadius = Math.min(props.borderRadius ?? Math.round(height / 2), height / 2);\n\n const fillWidth = (width * value) / 100;\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n <rect width=\"${width}\" height=\"${height}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"${trackColor}\"/>\n <rect width=\"${Math.max(fillWidth, borderRadius * 2)}\" height=\"${height}\" rx=\"${borderRadius}\" ry=\"${borderRadius}\" fill=\"${fillColor}\" style=\"${fillWidth < borderRadius * 2 ? `clip-path: inset(0 ${width - fillWidth}px 0 0)` : ''}\"/>\n</svg>`;\n}\n\nexport const progressDefinition: SmartElementDefinition = {\n type: 'progress',\n label: 'Progress Bar',\n icon: 'BarChart3',\n defaultWidth: 250,\n defaultHeight: 28,\n properties: [\n { key: 'value', label: 'Value (%)', type: 'number', default: 65, min: 0, max: 100, step: 1, bindable: true },\n { key: 'fillColor', label: 'Fill Color', type: 'color', default: '#3B82F6' },\n { key: 'trackColor', label: 'Track Color', type: 'color', default: '#E5E7EB' },\n { key: 'borderRadius', label: 'Corner Radius', type: 'number', default: 14, min: 0, max: 50, step: 1 },\n ],\n render: renderProgressSvg,\n};\n","/** Shared utilities for smart element renderers */\n\nexport function escapeXml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n}\n\n/**\n * Estimate text width using average character widths for sans-serif.\n */\nexport function estimateTextWidth(text: string, fontSize: number, fontWeight: string): number {\n const ratio = fontWeight === '700' ? 0.62 : fontWeight === '600' ? 0.6 : 0.56;\n return text.length * fontSize * ratio;\n}\n","import type { SmartElementDefinition } from '../types';\nimport { escapeXml } from '../utils';\n\nfunction renderTableSvg(props: Record<string, any>, width: number, height: number): string {\n const rows = Math.max(1, Math.min(props.rows ?? 3, 20));\n const cols = Math.max(1, Math.min(props.cols ?? 3, 10));\n const fontFamily = props.fontFamily || 'sans-serif';\n const headerBg = props.headerBg || '#1a1a1a';\n const headerText = props.headerText || '#ffffff';\n const cellBg = props.cellBg || '#ffffff';\n const cellText = props.cellText || '#1a1a1a';\n const borderColor = props.borderColor || '#e5e7eb';\n const borderWidth = props.borderWidth ?? 1;\n const fontSize = props.fontSize ?? 12;\n const showHeader = props.showHeader !== false;\n const headerData = props.headerValues || '';\n const cellData = props.cellValues || '';\n\n const totalRows = showHeader ? rows + 1 : rows;\n const cellW = width / cols;\n const cellH = height / totalRows;\n\n const headerValues = headerData ? headerData.split(',').map((s: string) => s.trim()) : [];\n const cellValues = cellData ? cellData.split(',').map((s: string) => s.trim()) : [];\n\n let svg = '';\n\n for (let r = 0; r < totalRows; r++) {\n const isHeader = showHeader && r === 0;\n const bg = isHeader ? headerBg : cellBg;\n const textColor = isHeader ? headerText : cellText;\n const fontWeight = isHeader ? 'bold' : 'normal';\n const y = r * cellH;\n\n for (let c = 0; c < cols; c++) {\n const x = c * cellW;\n svg += `<rect x=\"${x}\" y=\"${y}\" width=\"${cellW}\" height=\"${cellH}\" fill=\"${bg}\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\"/>`;\n\n let text = '';\n if (isHeader) {\n text = headerValues[c] || `Col ${c + 1}`;\n } else {\n const dataRow = showHeader ? r - 1 : r;\n const idx = dataRow * cols + c;\n text = cellValues[idx] || '';\n }\n\n if (text) {\n const maxChars = Math.max(3, Math.floor(cellW / (fontSize * 0.55)));\n const displayText = text.length > maxChars ? text.slice(0, maxChars - 1) + '…' : text;\n svg += `<text x=\"${x + cellW / 2}\" y=\"${y + cellH / 2}\" text-anchor=\"middle\" dominant-baseline=\"central\" font-size=\"${fontSize}\" font-family=\"${escapeXml(fontFamily)}\" font-weight=\"${fontWeight}\" fill=\"${textColor}\">${escapeXml(displayText)}</text>`;\n }\n }\n }\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">${svg}</svg>`;\n}\n\nexport const tableDefinition: SmartElementDefinition = {\n type: 'table',\n label: 'Table',\n icon: 'Table',\n defaultWidth: 400,\n defaultHeight: 200,\n properties: [\n { key: 'rows', label: 'Data Rows', type: 'number', default: 3, min: 1, max: 20, step: 1 },\n { key: 'cols', label: 'Columns', type: 'number', default: 3, min: 1, max: 10, step: 1 },\n {\n key: 'showHeader', label: 'Header Row', type: 'select', default: 'true',\n options: [{ value: 'true', label: 'Show' }, { value: 'false', label: 'Hide' }],\n },\n { key: 'headerValues', label: 'Header Labels', type: 'text', default: 'Name,Value,Status', bindable: true },\n { key: 'cellValues', label: 'Cell Data (comma-sep)', type: 'text', default: 'Item 1,100,Active,Item 2,200,Pending,Item 3,300,Done', bindable: true },\n { key: 'fontSize', label: 'Font Size', type: 'number', default: 12, min: 8, max: 24, step: 1 },\n { key: 'headerBg', label: 'Header Background', type: 'color', default: '#1a1a1a' },\n { key: 'headerText', label: 'Header Text', type: 'color', default: '#ffffff' },\n { key: 'cellBg', label: 'Cell Background', type: 'color', default: '#ffffff' },\n { key: 'cellText', label: 'Cell Text', type: 'color', default: '#1a1a1a' },\n { key: 'borderColor', label: 'Border Color', type: 'color', default: '#e5e7eb' },\n { key: 'borderWidth', label: 'Border Width', type: 'number', default: 1, min: 0, max: 4, step: 0.5 },\n ],\n render: (props, width, height) => {\n const normalizedProps = { ...props, showHeader: props.showHeader !== 'false' };\n return renderTableSvg(normalizedProps, width, height);\n },\n};\n","import type { SmartElementDefinition } from '../types';\nimport { escapeXml } from '../utils';\n\nfunction getInitials(name: string, maxChars: number): string {\n if (!name.trim()) return '?';\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 1) return parts[0].slice(0, maxChars).toUpperCase();\n return parts.slice(0, maxChars).map(p => p[0]).join('').toUpperCase();\n}\n\nfunction renderAvatarSvg(props: Record<string, any>, width: number, height: number): string {\n const name = props.name || 'John Doe';\n const bgColor = props.bgColor || '#6366f1';\n const textColor = props.textColor || '#ffffff';\n const fontSize = props.fontSize ?? Math.min(width, height) * 0.38;\n const fontFamily = props.fontFamily || 'sans-serif';\n const shape = props.shape || 'circle';\n const maxInitials = props.maxInitials ?? 2;\n const borderColor = props.borderColor || 'transparent';\n const borderWidth = props.borderWidth ?? 0;\n\n const initials = getInitials(name, maxInitials);\n const cx = width / 2;\n const cy = height / 2;\n const r = Math.min(width, height) / 2 - borderWidth;\n\n let shapeSvg = '';\n if (shape === 'circle') {\n shapeSvg = `<circle cx=\"${cx}\" cy=\"${cy}\" r=\"${r}\" fill=\"${bgColor}\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\"/>`;\n } else {\n const inset = borderWidth / 2;\n const rx = shape === 'rounded' ? Math.min(width, height) * 0.15 : 0;\n shapeSvg = `<rect x=\"${inset}\" y=\"${inset}\" width=\"${width - borderWidth}\" height=\"${height - borderWidth}\" rx=\"${rx}\" fill=\"${bgColor}\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\"/>`;\n }\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\" viewBox=\"0 0 ${width} ${height}\">\n ${shapeSvg}\n <text x=\"${cx}\" y=\"${cy}\" text-anchor=\"middle\" dominant-baseline=\"central\" font-size=\"${fontSize}\" font-family=\"${escapeXml(fontFamily)}\" font-weight=\"600\" fill=\"${textColor}\">${initials}</text>\n</svg>`;\n}\n\nexport const avatarDefinition: SmartElementDefinition = {\n type: 'avatar',\n label: 'Avatar',\n icon: 'UserCircle',\n defaultWidth: 80,\n defaultHeight: 80,\n properties: [\n { key: 'name', label: 'Name', type: 'text', default: 'John Doe', bindable: true },\n { key: 'maxInitials', label: 'Max Initials', type: 'number', default: 2, min: 1, max: 3, step: 1 },\n {\n key: 'shape', label: 'Shape', type: 'select', default: 'circle',\n options: [\n { value: 'circle', label: 'Circle' },\n { value: 'rounded', label: 'Rounded Square' },\n { value: 'square', label: 'Square' },\n ],\n },\n { key: 'bgColor', label: 'Background', type: 'color', default: '#6366f1' },\n { key: 'textColor', label: 'Text Color', type: 'color', default: '#ffffff' },\n { key: 'fontSize', label: 'Font Size', type: 'number', default: 30, min: 8, max: 100, step: 1 },\n { key: 'borderColor', label: 'Border Color', type: 'color', default: '#ffffff' },\n { key: 'borderWidth', label: 'Border Width', type: 'number', default: 0, min: 0, max: 10, step: 1 },\n ],\n render: renderAvatarSvg,\n};\n","import type { SmartElementDefinition } from '../types';\nimport { escapeXml, estimateTextWidth } from '../utils';\n\nexport interface BadgeRenderResult {\n svg: string;\n /** If overflow is 'grow', this is the computed ideal width */\n computedWidth?: number;\n /** Anchor mode — used by the caller to adjust x position */\n anchor?: 'left' | 'center' | 'right';\n}\n\nfunction renderBadgeInternal(\n props: Record<string, any>,\n width: number,\n height: number\n): BadgeRenderResult {\n const text = props.text || 'Badge';\n const bgColor = props.bgColor || '#6366f1';\n const textColor = props.textColor || '#ffffff';\n const borderRadius = props.borderRadius ?? Math.min(width, height) / 2;\n let fontSize = props.fontSize ?? Math.max(10, height * 0.45);\n const borderColor = props.borderColor || 'transparent';\n const borderWidth = props.borderWidth ?? 0;\n const fontWeight = props.fontWeight || '600';\n const fontFamily = props.fontFamily || 'sans-serif';\n const icon = props.icon || 'none';\n const overflow = props.overflow || 'grow';\n const anchor = props.anchor || 'center';\n\n const hPad = Math.max(12, height * 0.4);\n const iconSize = fontSize * 1.1;\n const iconGap = icon !== 'none' ? iconSize + 6 : 0;\n\n const textWidth = estimateTextWidth(text, fontSize, fontWeight);\n const contentWidth = iconGap + textWidth;\n const idealWidth = contentWidth + hPad * 2;\n\n let finalWidth = width;\n let displayText = text;\n let computedWidth: number | undefined;\n\n if (overflow === 'grow') {\n finalWidth = Math.max(width, Math.ceil(idealWidth));\n computedWidth = finalWidth;\n } else {\n finalWidth = width;\n const availableTextWidth = width - hPad * 2 - iconGap;\n while (fontSize > 6 && estimateTextWidth(text, fontSize, fontWeight) > availableTextWidth) {\n fontSize--;\n }\n if (estimateTextWidth(text, fontSize, fontWeight) > availableTextWidth) {\n const maxChars = Math.max(2, Math.floor(availableTextWidth / (fontSize * 0.55)));\n displayText = text.length > maxChars ? text.slice(0, maxChars - 1) + '…' : text;\n }\n }\n\n const rx = Math.min(borderRadius, Math.min(finalWidth, height) / 2);\n const inset = borderWidth / 2;\n\n let iconSvg = '';\n const iconX = hPad;\n const iconY = height / 2;\n const textX = icon !== 'none' ? hPad + iconGap + (finalWidth - hPad * 2 - iconGap) / 2 : finalWidth / 2;\n\n if (icon === 'dot') {\n const dotR = fontSize * 0.25;\n iconSvg = `<circle cx=\"${iconX + iconSize / 2}\" cy=\"${iconY}\" r=\"${dotR}\" fill=\"${textColor}\"/>`;\n } else if (icon === 'check') {\n const s = iconSize * 0.7;\n const ox = iconX + (iconSize - s) / 2;\n const oy = iconY - s / 2;\n iconSvg = `<polyline points=\"${ox + s * 0.15},${oy + s * 0.55} ${ox + s * 0.4},${oy + s * 0.8} ${ox + s * 0.85},${oy + s * 0.2}\" fill=\"none\" stroke=\"${textColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>`;\n } else if (icon === 'x') {\n const s = iconSize * 0.55;\n const ox = iconX + (iconSize - s) / 2;\n const oy = iconY - s / 2;\n iconSvg = `<line x1=\"${ox}\" y1=\"${oy}\" x2=\"${ox + s}\" y2=\"${oy + s}\" stroke=\"${textColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>\n <line x1=\"${ox + s}\" y1=\"${oy}\" x2=\"${ox}\" y2=\"${oy + s}\" stroke=\"${textColor}\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>`;\n }\n\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${finalWidth}\" height=\"${height}\" viewBox=\"0 0 ${finalWidth} ${height}\">\n <rect x=\"${inset}\" y=\"${inset}\" width=\"${finalWidth - borderWidth}\" height=\"${height - borderWidth}\" rx=\"${rx}\" fill=\"${bgColor}\" stroke=\"${borderColor}\" stroke-width=\"${borderWidth}\"/>\n ${iconSvg}\n <text x=\"${textX}\" y=\"${height / 2}\" text-anchor=\"middle\" dominant-baseline=\"central\" font-size=\"${fontSize}\" font-family=\"${escapeXml(fontFamily)}\" font-weight=\"${fontWeight}\" fill=\"${textColor}\">${escapeXml(displayText)}</text>\n</svg>`;\n\n return { svg, computedWidth, anchor };\n}\n\nfunction renderBadgeSvg(props: Record<string, any>, width: number, height: number): string {\n return renderBadgeInternal(props, width, height).svg;\n}\n\n/**\n * Render badge and return computed dimensions (used for auto-grow).\n */\nexport function renderBadgeWithSize(\n props: Record<string, any>,\n width: number,\n height: number\n): BadgeRenderResult {\n return renderBadgeInternal(props, width, height);\n}\n\nexport const badgeDefinition: SmartElementDefinition = {\n type: 'badge',\n label: 'Badge',\n icon: 'Tag',\n defaultWidth: 120,\n defaultHeight: 32,\n properties: [\n { key: 'text', label: 'Text', type: 'text', default: 'Badge', bindable: true },\n { key: 'bgColor', label: 'Background', type: 'color', default: '#6366f1' },\n { key: 'textColor', label: 'Text Color', type: 'color', default: '#ffffff' },\n { key: 'fontSize', label: 'Font Size', type: 'number', default: 14, min: 8, max: 36, step: 1 },\n {\n key: 'fontWeight', label: 'Font Weight', type: 'select', default: '600',\n options: [\n { value: '400', label: 'Normal' },\n { value: '600', label: 'Semi Bold' },\n { value: '700', label: 'Bold' },\n ],\n },\n { key: 'borderRadius', label: 'Border Radius', type: 'number', default: 16, min: 0, max: 50, step: 1 },\n {\n key: 'overflow', label: 'Overflow', type: 'select', default: 'grow',\n options: [\n { value: 'grow', label: 'Auto Grow' },\n { value: 'shrink', label: 'Auto Shrink' },\n ],\n },\n {\n key: 'anchor', label: 'Grow Anchor', type: 'select', default: 'center',\n options: [\n { value: 'left', label: 'Left (grow right)' },\n { value: 'center', label: 'Center (grow both)' },\n { value: 'right', label: 'Right (grow left)' },\n ],\n },\n {\n key: 'icon', label: 'Icon', type: 'select', default: 'none',\n options: [\n { value: 'none', label: 'None' },\n { value: 'dot', label: 'Dot' },\n { value: 'check', label: 'Checkmark' },\n { value: 'x', label: 'Cross' },\n ],\n },\n { key: 'borderColor', label: 'Border Color', type: 'color', default: '#4f46e5' },\n { key: 'borderWidth', label: 'Border Width', type: 'number', default: 0, min: 0, max: 4, step: 0.5 },\n ],\n render: renderBadgeSvg,\n};\n","/**\n * Smart Element Registry — Isomorphic, single source of truth.\n * Used by BOTH client (Vite/browser) and server (Node.js/EC2).\n */\n\nimport type { SmartElementDefinition, SmartElementType } from './types';\nimport { qrCodeDefinition } from './renderers/qrcode';\nimport { ratingDefinition } from './renderers/rating';\nimport { progressDefinition } from './renderers/progress';\nimport { tableDefinition } from './renderers/table';\nimport { avatarDefinition } from './renderers/avatar';\nimport { badgeDefinition } from './renderers/badge';\n\nconst registry = new Map<SmartElementType, SmartElementDefinition>();\n\nregistry.set('qrcode', qrCodeDefinition);\nregistry.set('rating', ratingDefinition);\nregistry.set('progress', progressDefinition);\nregistry.set('table', tableDefinition);\nregistry.set('avatar', avatarDefinition);\nregistry.set('badge', badgeDefinition);\n\n/** Get a smart element definition by type */\nexport function getSmartElementDef(type: SmartElementType): SmartElementDefinition | undefined {\n return registry.get(type);\n}\n\n/** Get all registered smart element definitions */\nexport function getAllSmartElements(): SmartElementDefinition[] {\n return Array.from(registry.values());\n}\n\n/** Register a new smart element type (for user-created elements) */\nexport function registerSmartElement(def: SmartElementDefinition) {\n registry.set(def.type, def);\n}\n\n/**\n * Render a smart element to SVG string.\n */\nexport function renderSmartElementToSvg(\n type: SmartElementType,\n props: Record<string, any>,\n width: number,\n height: number\n): string | null {\n const def = registry.get(type);\n if (!def) return null;\n return def.render(props, width, height);\n}\n\n/**\n * Render a smart element to data URI.\n */\nexport function renderSmartElementToDataUri(\n type: SmartElementType,\n props: Record<string, any>,\n width: number,\n height: number\n): string | null {\n const svg = renderSmartElementToSvg(type, props, width, height);\n if (!svg) return null;\n return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;\n}\n","/**\n * Gradient utilities for Fabric.js, PDF export, and server-side rendering.\n */\nimport { GradientConfig, GradientStop, isGradientConfig } from '@/types/editor';\nimport * as fabric from 'fabric';\n\n/**\n * Convert angle in degrees to x1,y1,x2,y2 coordinates for a linear gradient.\n * The coordinates are in the range [0,1] (normalized).\n */\nfunction angleToCoords(angleDeg: number): { x1: number; y1: number; x2: number; y2: number } {\n const rad = (angleDeg * Math.PI) / 180;\n // CSS angle: 0deg = bottom-to-top, 90deg = left-to-right\n // Fabric: coords relative to object bounds\n const x1 = 0.5 - Math.sin(rad) * 0.5;\n const y1 = 0.5 + Math.cos(rad) * 0.5;\n const x2 = 0.5 + Math.sin(rad) * 0.5;\n const y2 = 0.5 - Math.cos(rad) * 0.5;\n return { x1, y1, x2, y2 };\n}\n\n/**\n * Normalize gradient stops for engines that require deterministic ordering.\n * - clamps offsets to [0,1]\n * - sorts by offset\n * - ensures boundary stops at 0 and 1 for full-range interpolation\n */\nfunction normalizeGradientStops(stops: GradientStop[]): GradientStop[] {\n const normalized = stops\n .map((stop) => ({\n color: stop.color,\n offset: Math.max(0, Math.min(1, Number(stop.offset) || 0)),\n }))\n .sort((a, b) => a.offset - b.offset);\n\n if (normalized.length === 0) return normalized;\n\n if (normalized[0].offset > 0) {\n normalized.unshift({ color: normalized[0].color, offset: 0 });\n }\n if (normalized[normalized.length - 1].offset < 1) {\n normalized.push({ color: normalized[normalized.length - 1].color, offset: 1 });\n }\n\n return normalized;\n}\n\n/**\n * Convert a GradientConfig to a Fabric.js Gradient object.\n */\nexport function gradientToFabric(\n gradient: GradientConfig,\n width: number,\n height: number,\n): fabric.Gradient<'linear'> | fabric.Gradient<'radial'> {\n const colorStops = normalizeGradientStops(gradient.stops).map((s) => ({\n offset: s.offset,\n color: s.color,\n }));\n\n if (gradient.type === 'linear' || gradient.type === 'conic') {\n // Conic gradients aren't natively supported by Fabric, fallback to linear\n const coords = angleToCoords(gradient.angle ?? 90);\n return new fabric.Gradient({\n type: 'linear',\n coords: {\n x1: coords.x1 * width,\n y1: coords.y1 * height,\n x2: coords.x2 * width,\n y2: coords.y2 * height,\n },\n colorStops,\n });\n }\n\n // Radial gradient\n const cx = (gradient.cx ?? 0.5) * width;\n const cy = (gradient.cy ?? 0.5) * height;\n const r = (gradient.r ?? 0.5) * Math.max(width, height);\n return new fabric.Gradient({\n type: 'radial',\n coords: {\n x1: cx,\n y1: cy,\n r1: 0,\n x2: cx,\n y2: cy,\n r2: r,\n },\n colorStops,\n });\n}\n\n/**\n * Convert GradientConfig to CSS background string.\n */\nexport function gradientToCss(g: GradientConfig): string {\n const stopsStr = g.stops.map(s => `${s.color} ${Math.round(s.offset * 100)}%`).join(', ');\n if (g.type === 'linear') {\n return `linear-gradient(${g.angle ?? 90}deg, ${stopsStr})`;\n }\n if (g.type === 'radial') {\n const cx = (g.cx ?? 0.5) * 100;\n const cy = (g.cy ?? 0.5) * 100;\n return `radial-gradient(circle at ${cx}% ${cy}%, ${stopsStr})`;\n }\n if (g.type === 'conic') {\n const cx = (g.cx ?? 0.5) * 100;\n const cy = (g.cy ?? 0.5) * 100;\n return `conic-gradient(from ${g.angle ?? 0}deg at ${cx}% ${cy}%, ${stopsStr})`;\n }\n return `linear-gradient(90deg, ${stopsStr})`;\n}\n\n/**\n * Convert GradientConfig to SVG gradient definition string.\n * Returns { defXml, fillUrl } where defXml goes into <defs> and fillUrl is the fill attribute.\n */\nexport function gradientToSvgDef(\n g: GradientConfig,\n id: string,\n width: number,\n height: number,\n): { defXml: string; fillUrl: string } {\n const stopsXml = g.stops\n .map(s => `<stop offset=\"${s.offset}\" stop-color=\"${s.color}\" />`)\n .join('\\n ');\n\n if (g.type === 'linear' || g.type === 'conic') {\n const coords = angleToCoords(g.angle ?? 90);\n const defXml = `\n <linearGradient id=\"${id}\" x1=\"${coords.x1}\" y1=\"${coords.y1}\" x2=\"${coords.x2}\" y2=\"${coords.y2}\">\n ${stopsXml}\n </linearGradient>`;\n return { defXml, fillUrl: `url(#${id})` };\n }\n\n // Radial\n const cx = g.cx ?? 0.5;\n const cy = g.cy ?? 0.5;\n const r = g.r ?? 0.5;\n const defXml = `\n <radialGradient id=\"${id}\" cx=\"${cx}\" cy=\"${cy}\" r=\"${r}\" fx=\"${cx}\" fy=\"${cy}\">\n ${stopsXml}\n </radialGradient>`;\n return { defXml, fillUrl: `url(#${id})` };\n}\n\n/**\n * Parse hex color to RGB.\n */\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } | null {\n const match = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/.exec(hex);\n if (!match) return null;\n return {\n r: parseInt(match[1], 16),\n g: parseInt(match[2], 16),\n b: parseInt(match[3], 16),\n };\n}\n\n/**\n * For jsPDF: apply gradient using GState and shading.\n * jsPDF doesn't have great native gradient API, so we use advancedAPI pattern.\n */\nexport function applyGradientToPdf(\n pdf: any,\n gradient: GradientConfig,\n x: number,\n y: number,\n width: number,\n height: number,\n): void {\n // jsPDF supports linear and radial gradients via the advancedAPI\n const colors = gradient.stops.map(s => {\n const rgb = hexToRgb(s.color);\n return rgb || { r: 0, g: 0, b: 0 };\n });\n const offsets = gradient.stops.map(s => s.offset);\n\n if (gradient.type === 'linear' || gradient.type === 'conic') {\n const coords = angleToCoords(gradient.angle ?? 90);\n const x1 = x + coords.x1 * width;\n const y1 = y + coords.y1 * height;\n const x2 = x + coords.x2 * width;\n const y2 = y + coords.y2 * height;\n\n try {\n // Use jsPDF's linearGradient if available (advancedAPI)\n if (typeof pdf.linearGradient === 'function') {\n const grad = pdf.linearGradient(x1, y1, x2, y2);\n for (let i = 0; i < colors.length; i++) {\n grad.addColorStop(offsets[i], [colors[i].r, colors[i].g, colors[i].b]);\n }\n grad.fill();\n return;\n }\n } catch (_) {\n // fallback to first color\n }\n }\n\n // Fallback: use first stop color\n const first = colors[0] || { r: 0, g: 0, b: 0 };\n pdf.setFillColor(first.r, first.g, first.b);\n}\n","import { useEffect, useRef, useCallback, useState, forwardRef, useImperativeHandle, useMemo } from 'react';\nimport { flushSync } from 'react-dom';\nimport { useEditorStore, setEditingText } from '@/store/editorStore';\nimport { CanvasElement, PageSettings, ProjectSettings, createDefaultElement, findParentGroup, findNodeById, getAllElementIds, isElement, CanvasNode, GroupNode, isGroup, updateNodeInTree, findCommonAncestorGroup, isStackLayoutMode, isGradientConfig } from '@/types/editor';\nimport { registerFabricCanvas, unregisterFabricCanvas, setActivePageCanvas } from '@/store/fabricCanvasRegistry';\nimport { preloadAllFonts, clearFontCacheAndRerender, clearFabricCharCache, setupFontLoadingListener, ensureFontLoaded, waitForFontsReady, waitUntilFontsAvailable } from '@/lib/fontLoader';\nimport { createMaskedImageElement, updateCoverLayout, installCanvaMaskControls, setSimpleScaleControls, panImageInside, getLocalDeltaStable, applyControlSizeForZoom } from '@/lib/canvaMaskedImage';\nimport { getObjectId, getFabricGroupId, setObjectData, setFabricGroupId } from '@/lib/fabricUtils';\nimport { calculateSnapGuides, calculateScaleSnapGuides, applyScaleSnapToBox, SnapGuide, type ActiveSnapState } from '@/lib/snapGuides';\nimport { createFabricObject, createFabricObjectForGroupMember, createShape, createText, createLine, buildRoundedRectPath } from '@/lib/fabricObjectCreators';\nimport { buildRoundedTrianglePath, TRIANGLE_STROKE_MITER_LIMIT } from '@/lib/shapeGeometry';\nimport { applyTriangleCacheSettings } from '@/lib/triangleCache';\nimport { createImagePlaceholder, createImagePlaceholderForGroup, getProxiedImageUrl, createImageClipPath, normalizeSvgImageDimensions, isSvgImage, getNormalizedSvgUrl } from '@/lib/canvasImageLoader';\nimport { renderSmartElementToDataUri } from '@/lib/smartElements';\nimport { getNodeBounds, getNodeBoundsFromChildren, getAbsoluteBounds, absoluteToStorePosition, getEffectiveGroupDisplay, getGroupContentBox, reflowPageFromRoot, reflowFromGroup, setDragBoundsOverride, clearDragBoundsOverride, computeGroupResizeLayoutKey, getGroupLayoutSnapTargets, computeDiscreteGridSnappedBox, getDiscreteGridSnapGuides, getOrderedFlexOrGridChildren, isGridGroupForResize, type BoundsOptions } from '@/lib/layoutEngine';\nimport { getMinTextWidth, clearMeasurementCache } from '@/lib/textMeasurement';\nimport { flattenChildren } from '@/types/editor';\nimport * as fabric from 'fabric';\nimport { gradientToFabric } from '@/lib/gradientUtils';\n\n/** Compute new left/top/width/height from Fabric transform (original + scale + corner). Use this instead of getBoundingRect(true) so left/top handles shrink correctly. */\nfunction computeBoxFromTransform(\n target: fabric.FabricObject,\n transform: { corner: string; original: { left?: number; top?: number }; width?: number; height?: number },\n opts: { minW?: number; minH?: number } = {}\n): { left: number; top: number; width: number; height: number } {\n const minW = opts.minW ?? 20;\n const minH = opts.minH ?? 20;\n const corner = transform.corner;\n const orig = transform.original;\n const sx = Math.abs((target as any).scaleX ?? 1);\n const sy = Math.abs((target as any).scaleY ?? 1);\n const origW = transform.width ?? (target as any).width ?? 0;\n const origH = transform.height ?? (target as any).height ?? 0;\n let newW = Math.max(minW, origW * sx);\n let newH = Math.max(minH, origH * sy);\n const isLeftHandle = corner === 'ml' || corner === 'tl' || corner === 'bl';\n const isTopHandle = corner === 'mt' || corner === 'tl' || corner === 'tr';\n const isWidthOnly = corner === 'ml' || corner === 'mr';\n const isHeightOnly = corner === 'mt' || corner === 'mb';\n if (isWidthOnly) newH = origH;\n if (isHeightOnly) newW = origW;\n let left = orig?.left ?? (target as any).left ?? 0;\n let top = orig?.top ?? (target as any).top ?? 0;\n if (isLeftHandle) left = left + (origW - newW);\n if (isTopHandle) top = top + (origH - newH);\n return { left, top, width: newW, height: newH };\n}\n\n\n// ============================================\n// COMPONENT\n// ============================================\nexport interface ElementBounds {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface PageCanvasRef {\n fabricCanvas: fabric.Canvas | null;\n discardActiveObject: () => void;\n /** Enter text editing on the given element (or active object if single Textbox). Optionally insert a character (e.g. first keypress). Returns true if editing was started. */\n enterTextEditing: (elementId?: string, charToInsert?: string) => boolean;\n /** Get the actual rendered bounds of an element by ID */\n getElementBounds: (elementId: string) => ElementBounds | null;\n /** Get bounds for all elements */\n getAllElementBounds: () => Map<string, ElementBounds>;\n}\n\nexport type CanvasMode = 'editor' | 'preview' | 'export';\n\ninterface PageCanvasProps {\n pageId: string;\n elements: CanvasElement[];\n pageSettings: PageSettings;\n projectSettings: ProjectSettings;\n canvasWidth: number;\n canvasHeight: number;\n isActive: boolean;\n workspaceZoom: number;\n selectedIds: string[];\n activeTool: string;\n onDragStart?: (elements: CanvasElement[], mouseEvent: MouseEvent, pageId: string) => void;\n onDragEnd?: () => void;\n onActivate?: () => void;\n // New props for unified canvas\n mode?: CanvasMode;\n dynamicFieldIds?: string[]; // Element IDs that are bound to dynamic fields\n onDynamicFieldClick?: (elementId: string) => void; // Callback when clicking a dynamic field element\n /** Bumped by store on updateNode/updateElement so sync runs when panel changes something */\n canvasUpdateVersion?: number;\n /** Top-level page children (tree); when provided, positions sync using absolute bounds (div-like relative positioning) */\n pageChildren?: CanvasNode[];\n /** Called once when fonts are loaded and canvas is ready (editor only). Use to reflow with correct text dimensions. */\n onReady?: () => void;\n}\n\nexport const PageCanvas = forwardRef<PageCanvasRef, PageCanvasProps>(\n (\n {\n pageId,\n elements,\n pageSettings,\n projectSettings,\n canvasWidth,\n canvasHeight,\n isActive,\n workspaceZoom,\n selectedIds,\n activeTool,\n onDragStart,\n onDragEnd,\n onActivate,\n mode = 'editor',\n dynamicFieldIds = [],\n onDynamicFieldClick,\n canvasUpdateVersion = 0,\n pageChildren,\n onReady,\n },\n ref\n ) => {\n // Determine interaction capabilities based on mode\n const isEditorMode = mode === 'editor';\n const isPreviewMode = mode === 'preview';\n const isExportMode = mode === 'export';\n const allowEditing = isEditorMode;\n const allowSelection = isEditorMode;\n const allowDynamicFieldClick = isPreviewMode && dynamicFieldIds.length > 0;\n const canvasElRef = useRef<HTMLCanvasElement>(null);\n const fabricRef = useRef<fabric.Canvas | null>(null);\n const elementsRef = useRef<CanvasElement[]>(elements);\n const selectedIdsRef = useRef<string[]>(selectedIds);\n const isActiveRef = useRef(isActive);\n const isRebuildingRef = useRef(false);\n const transformingIdsRef = useRef<Set<string>>(new Set());\n const justModifiedIdsRef = useRef<Set<string>>(new Set()); // IDs that were just modified, skip next sync\n const previousVisibilityRef = useRef<Map<string, boolean>>(new Map()); // Track previous visibility state\n const isSyncingSelectionToFabricRef = useRef(false); // Prevent feedback loop when syncing selection\n const editingTextIdRef = useRef<string | null>(null); // ID of element currently being text-edited\n const syncLockedRef = useRef(false); // Global sync lock to prevent store→canvas writes during transform\n const editLockRef = useRef(false); // Edit lock to prevent selection clearing during/after transforms\n const editLockCountRef = useRef(0); // Counter to track lock generations\n const didTransformRef = useRef(false); // Track if a transform happened during this pointer session\n const pendingSyncRef = useRef(false); // Track if sync was deferred due to lock\n const doSyncRef = useRef<(() => void) | null>(null); // Store sync function to run later\n const visibilityUpdateInProgressRef = useRef(false); // Flag to prevent layout recalculation during visibility updates\n const prevCanvasUpdateVersionRef = useRef(canvasUpdateVersion); // Detect panel-driven updates so we apply left/top\n const syncTriggeredByPanelRef = useRef(false); // When true: discard selection before applying updates so positions apply to loose objects (no shift)\n const hasRunPostReadyReflowForPageRef = useRef<string | null>(null);\n const hasClearedCachesBeforeFirstSyncRef = useRef(false); // clear Fabric/measurement caches once right before first sync (fresh metrics)\n\n const [guides, setGuides] = useState<SnapGuide[]>([]);\n const [gridResizeLabel, setGridResizeLabel] = useState<{ cols: number; rows: number; x: number; y: number } | null>(null);\n const [ready, setReady] = useState(false);\n // Increment to request unlock+sync from effect (avoids calling doSync in setTimeout which can trigger \"Maximum update depth\" via panels setRef)\n const [unlockRequestId, setUnlockRequestId] = useState(0);\n\n /** Pass to getNodeBounds/getGroupContentBox so root groups with width/height 'inherit' resolve to page size. */\n const pageBoundsOptions: BoundsOptions = useMemo(\n () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),\n [canvasWidth, canvasHeight]\n );\n\n // Group bounds overlay: resize state and wrapper ref for client→canvas conversion\n const groupOverlayWrapperRef = useRef<HTMLDivElement>(null);\n const [groupResizeState, setGroupResizeState] = useState<{\n groupId: string;\n handle: 'e' | 's' | 'se';\n startBounds: { left: number; top: number; width: number; height: number };\n startClientX: number;\n startClientY: number;\n } | null>(null);\n // Live bounds during group drag so the overlay moves with the group (not only after release)\n const [groupOverlayLiveBounds, setGroupOverlayLiveBounds] = useState<{ left: number; top: number; width: number; height: number } | null>(null);\n const setGroupOverlayLiveBoundsRef = useRef(setGroupOverlayLiveBounds);\n const skipSelectionClearOnDiscardRef = useRef(false);\n // Prevent stale async image reloads (e.g. rapid SVG recolor changes) from inserting duplicate objects\n const imageReloadRequestSeqRef = useRef<Map<string, number>>(new Map());\n const containerResizeRafRef = useRef<number | null>(null);\n const groupBoundsResizingRef = useRef(false);\n /** Latest group bounds during drag; no store write until mouseup (avoids lag). */\n const lastGroupBoundsBoxRef = useRef<{ groupId: string; left: number; top: number; width: number; height: number } | null>(null);\n /** Group we've already frozen for wrap resize (freeze once per drag, not every event). */\n const wrapResizeFrozenGroupIdRef = useRef<string | null>(null);\n /** Cached node + transformMode for current resize group (avoid store read every pointer move). */\n const wrapResizeNodeRef = useRef<GroupNode | null>(null);\n const wrapResizeTransformModeRef = useRef<boolean>(false);\n /** Throttle guide updates during group resize to once per frame (reduces setState jitter). */\n const groupBoundsGuidesRafRef = useRef<number | null>(null);\n /** Last layout key during wrap resize; reflow only when this changes (cols / width bucket). */\n const lastGroupResizeLayoutKeyRef = useRef<string | null>(null);\n /** Last applied box per child during conditional reflow (diff to avoid redundant Fabric updates). */\n const lastGroupResizeAppliedRef = useRef<Map<string, { left: number; top: number; width: number; height: number }>>(new Map());\n /** Sticky snap state during group resize; cleared on mouseup to avoid ping-pong. */\n const groupResizeActiveSnapRef = useRef<ActiveSnapState>(null);\n /** Discrete grid: current column/row count during resize (midpoint hysteresis); cleared on mouseup. */\n const lastDiscreteGridColsRef = useRef<number | null>(null);\n const lastDiscreteGridRowsRef = useRef<number | null>(null);\n /** Discrete grid: box at drag start for anchoring the fixed edge; cleared on mouseup. */\n const discreteGridDragStartBoxRef = useRef<{ left: number; top: number; width: number; height: number } | null>(null);\n /** Discrete grid: fixed row height during drag (avoids jitter from text wrap); cleared on mouseup. */\n const lastDiscreteGridCellHRef = useRef<number | null>(null);\n /** Last dimensions synced during text edit (for live reflow throttle). */\n const lastTextEditDimensionsRef = useRef<{ id: string; w: number; h: number } | null>(null);\n /** Target of the current resize/scale gesture; used to detect child resize inside section group (object:modified often reports group as target). */\n const lastResizeScaleTargetRef = useRef<fabric.FabricObject | null>(null);\n setGroupOverlayLiveBoundsRef.current = setGroupOverlayLiveBounds;\n\n const {\n selectElements,\n clearSelection,\n addElement,\n updateNode,\n commitHistory,\n setActiveTool,\n showSections,\n canvas,\n hoveredGroupId,\n } = useEditorStore();\n const storeSelectedIds = canvas.selectedIds ?? [];\n\n // Current page tree (for selected group overlay)\n const currentPage = useMemo(() => (canvas.pages ?? []).find((p) => p.id === pageId), [canvas.pages, pageId]);\n\n // When exactly one group (or empty group) is selected, show its bounds + overlay resize. Never expand to children — selection is only the bounds rect.\n const selectedGroup = useMemo((): GroupNode | null => {\n const ids = storeSelectedIds ?? [];\n const children = currentPage?.children ?? [];\n if (!currentPage || ids.length === 0) return null;\n for (const id of ids) {\n const node = findNodeById(children, id);\n if (node && isGroup(node)) return node as GroupNode;\n }\n const firstId = ids[0];\n const parent = findParentGroup(children, firstId);\n if (!parent) return null;\n const memberIds = new Set(getAllElementIds(parent.children ?? []));\n const allSelectedAreInGroup = ids.every((id) => memberIds.has(id) || id === parent.id);\n return allSelectedAreInGroup ? parent : null;\n }, [currentPage, storeSelectedIds]);\n\n // Keep refs up to date\n useEffect(() => {\n elementsRef.current = elements;\n }, [elements]);\n\n useEffect(() => {\n selectedIdsRef.current = selectedIds;\n }, [selectedIds]);\n\n useEffect(() => {\n isActiveRef.current = isActive;\n if (isActive && fabricRef.current) {\n setActivePageCanvas(pageId);\n }\n }, [isActive, pageId]);\n\n // Helper to get object ID\n const getObjId = useCallback((obj: fabric.FabricObject): string | undefined => {\n return (obj as any).__docuforgeId;\n }, []);\n\n // Expose Fabric canvas through ref\n // IMPORTANT: Empty dependency array to prevent ref object identity changes\n // which can cause infinite loops with inline ref callbacks\n useImperativeHandle(ref, () => ({\n fabricCanvas: fabricRef.current,\n discardActiveObject: () => {\n if (fabricRef.current) {\n // CRITICAL: Don't discard if edits are locked (during handle drag)\n if (editLockRef.current) {\n return; // Do nothing during transform\n }\n fabricRef.current.discardActiveObject();\n fabricRef.current.requestRenderAll();\n }\n },\n enterTextEditing: (elementId?: string, charToInsert?: string): boolean => {\n const fc = fabricRef.current;\n if (!fc || !allowEditing) return false;\n let textbox: fabric.Textbox | null = null;\n if (elementId) {\n const obj = fc.getObjects().find((o) => getObjectId(o) === elementId);\n if (obj instanceof fabric.Textbox) textbox = obj;\n } else {\n const active = fc.getActiveObject();\n if (active instanceof fabric.Textbox) textbox = active;\n else if (active instanceof fabric.ActiveSelection && active.getObjects().length === 1 && active.getObjects()[0] instanceof fabric.Textbox) textbox = active.getObjects()[0] as fabric.Textbox;\n }\n if (!textbox) return false;\n const id = getObjectId(textbox);\n if (id) editingTextIdRef.current = id;\n textbox.enterEditing();\n if (charToInsert != null && charToInsert.length > 0) {\n const iText = textbox as fabric.IText;\n const start = iText.selectionStart ?? (iText.text?.length ?? 0);\n iText.insertChars(charToInsert, undefined, start);\n }\n fc.requestRenderAll();\n return true;\n },\n getElementBounds: (elementId: string): ElementBounds | null => {\n const canvas = fabricRef.current;\n if (!canvas) return null;\n \n const obj = canvas.getObjects().find(o => getObjId(o) === elementId);\n if (!obj) return null;\n \n // Use object properties directly (in canvas coordinate space, not viewport)\n // For textboxes, width/height reflect actual rendered dimensions after initDimensions()\n const width = (obj.width ?? 0) * (obj.scaleX ?? 1);\n const height = (obj.height ?? 0) * (obj.scaleY ?? 1);\n \n // CRITICAL: Account for origin point (center vs top-left)\n // For crop groups with originX: 'center', left/top represent the center\n let left = obj.left ?? 0;\n let top = obj.top ?? 0;\n \n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n // Crop groups use center origin, so convert to top-left\n left = left - width / 2;\n top = top - height / 2;\n } else if (obj.originX === 'center' || obj.originY === 'center') {\n // For any center-origin object, convert to top-left\n const offsetX = obj.originX === 'center' ? width / 2 : 0;\n const offsetY = obj.originY === 'center' ? height / 2 : 0;\n left = left - offsetX;\n top = top - offsetY;\n }\n \n return { left, top, width, height };\n },\n getAllElementBounds: (): Map<string, ElementBounds> => {\n const canvas = fabricRef.current;\n const result = new Map<string, ElementBounds>();\n if (!canvas) return result;\n \n canvas.getObjects().forEach(obj => {\n const id = getObjId(obj);\n if (id && id !== '__background__') {\n // Use object properties directly (in canvas coordinate space, not viewport)\n const width = (obj.width ?? 0) * (obj.scaleX ?? 1);\n const height = (obj.height ?? 0) * (obj.scaleY ?? 1);\n \n // CRITICAL: Account for origin point (center vs top-left)\n // For crop groups with originX: 'center', left/top represent the center\n let left = obj.left ?? 0;\n let top = obj.top ?? 0;\n \n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n // Crop groups use center origin, so convert to top-left\n left = left - width / 2;\n top = top - height / 2;\n } else if (obj.originX === 'center' || obj.originY === 'center') {\n // For any center-origin object, convert to top-left\n const offsetX = obj.originX === 'center' ? width / 2 : 0;\n const offsetY = obj.originY === 'center' ? height / 2 : 0;\n left = left - offsetX;\n top = top - offsetY;\n }\n \n result.set(id, { left, top, width, height });\n }\n });\n \n return result;\n },\n }), [getObjId]);\n\n // === SNAP GUIDES ===\n const calculateSnapGuidesCallback = useCallback(\n (movingObj: fabric.FabricObject): { guides: SnapGuide[]; snapDx: number; snapDy: number } => {\n const fabricCanvas = fabricRef.current;\n if (!fabricCanvas) return { guides: [], snapDx: 0, snapDy: 0 };\n return calculateSnapGuides(\n movingObj,\n fabricCanvas,\n canvasWidth,\n canvasHeight,\n projectSettings.snapToGuides,\n projectSettings.snapThreshold\n );\n },\n [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]\n );\n\n // === SNAP GUIDES FOR SCALING ===\n const calculateScaleSnapGuidesCallback = useCallback(\n (scalingObj: fabric.FabricObject, corner: string): SnapGuide[] => {\n const fabricCanvas = fabricRef.current;\n if (!fabricCanvas) return [];\n return calculateScaleSnapGuides(\n scalingObj,\n corner,\n fabricCanvas,\n canvasWidth,\n canvasHeight,\n projectSettings.snapToGuides,\n projectSettings.snapThreshold\n );\n },\n [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]\n );\n\n // Helper to check if we're currently transforming (must be accessible to all useEffects)\n const isTransforming = useCallback((canvas: fabric.Canvas | null) => {\n if (!canvas) return false;\n return !!(canvas as any)._currentTransform || !!(canvas as any).__isUserTransforming;\n }, []);\n\n // === INIT FABRIC CANVAS ===\n useEffect(() => {\n if (!canvasElRef.current) return;\n\n const zoom = workspaceZoom || 1;\n const scaledWidth = canvasWidth * zoom;\n const scaledHeight = canvasHeight * zoom;\n\n const fabricCanvas = new fabric.Canvas(canvasElRef.current, {\n // Start with zoomed dimensions immediately\n width: scaledWidth,\n height: scaledHeight,\n selection: allowSelection,\n preserveObjectStacking: true,\n renderOnAddRemove: false,\n // In preview/export mode, disable all interactions\n interactive: isEditorMode || isPreviewMode,\n enableRetinaScaling: true,\n // Turn OFF sub-targeting at the canvas level (this is the big hammer)\n subTargetCheck: false,\n // Ensure canvas is not doing \"per-pixel\" target find (it makes images win hits)\n perPixelTargetFind: false,\n targetFindTolerance: 20, // Increased to help picking controls/edges (especially corners)\n // Ensure controls are above overlay for better hit-testing\n controlsAboveOverlay: true,\n // Transparent so underlay (page bg + group bgs) shows through\n backgroundColor: 'transparent',\n });\n \n // CRITICAL: In preview mode, disable selection entirely to prevent selection borders\n if (!allowSelection) {\n fabricCanvas.selection = false;\n // Also prevent any objects from being selected\n fabricCanvas.on('selection:created', () => {\n fabricCanvas.discardActiveObject();\n });\n fabricCanvas.on('selection:updated', () => {\n fabricCanvas.discardActiveObject();\n });\n }\n\n // Apply viewport transform immediately during init\n fabricCanvas.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);\n \n // Store editLockRef on canvas for access from control handlers\n (fabricCanvas as any).__editLockRef = editLockRef;\n\n // Page background is drawn in the HTML underlay; section group backgrounds are drawn via fabric.Group _renderBackground (no separate rect)\n\n // Clip to bounds (original dimensions)\n fabricCanvas.clipPath = new fabric.Rect({\n left: 0,\n top: 0,\n width: canvasWidth,\n height: canvasHeight,\n absolutePositioned: true,\n });\n\n fabricRef.current = fabricCanvas;\n registerFabricCanvas(pageId, fabricCanvas);\n // Don't setReady yet — wait for fonts so first sync creates/updates text with correct metrics (fixes text overflow on load)\n\n // === FONT LOADING: strict sequence — no sync until fonts are ready (no delays, standard rAF) ===\n // In preview mode we get fonts from the elements prop (Templates/Use page); in editor mode from the store\n const initFonts = async () => {\n try {\n await preloadAllFonts();\n let fontFamilies: string[] = [];\n if (isPreviewMode && elements.length > 0) {\n fontFamilies = [...new Set(\n elements\n .filter((el): el is CanvasElement => isElement(el) && el.type === 'text')\n .map((el) => (el as CanvasElement).fontFamily)\n .filter(Boolean)\n )] as string[];\n await Promise.all(fontFamilies.map((f) => ensureFontLoaded(f)));\n } else {\n const state = useEditorStore.getState();\n const page = state.canvas.pages.find((p) => p.id === pageId);\n if (page) {\n const pageElements = flattenChildren(page.children);\n fontFamilies = [...new Set(\n pageElements\n .filter((el): el is CanvasElement => isElement(el) && el.type === 'text')\n .map((el) => (el as CanvasElement).fontFamily)\n .filter(Boolean)\n )] as string[];\n await Promise.all(fontFamilies.map((f) => ensureFontLoaded(f)));\n }\n }\n await waitForFontsReady();\n await waitUntilFontsAvailable(fontFamilies, { timeoutMs: 3500, pollIntervalMs: 60 });\n await new Promise<void>((r) => requestAnimationFrame(() => r()));\n clearFabricCharCache();\n clearMeasurementCache();\n setReady(true);\n if (onReady) onReady();\n } catch (e) {\n setReady(true);\n if (onReady) onReady();\n }\n };\n initFonts();\n\n // Setup font loading listener to catch late-loading fonts; persist text width/height so text doesn't overflow on save/reload\n const persistTextDimensionsAfterFontLoad = (canvas: fabric.Canvas) => {\n const state = useEditorStore.getState();\n const page = state.canvas.pages.find((p) => p.id === pageId);\n if (!page) return;\n const elements = flattenChildren(page.children);\n canvas.getObjects().forEach((obj) => {\n if (obj instanceof fabric.Textbox) {\n const id = getObjectId(obj);\n if (!id) return;\n const w = obj.width ?? 0;\n const h = obj.height ?? 0;\n const el = elements.find((e) => e.id === id);\n if (!el) return;\n const storeW = (el as CanvasElement).width ?? 0;\n const storeH = (el as CanvasElement).height ?? 0;\n const updates: Partial<CanvasElement> = {};\n const shouldKeepFixedHeight = (el as CanvasElement).overflowPolicy === 'auto-shrink';\n // Don't overwrite width when user chose 'inherit' or 'auto'\n if (!shouldKeepFixedHeight && w > 0 && typeof storeW === 'number' && Math.abs(w - storeW) > 0.1) updates.width = w;\n // Auto-shrink: allow height to SHRINK (for stack reflow) but never grow\n if (shouldKeepFixedHeight) {\n if (h > 0 && typeof storeH === 'number' && h < storeH - 0.5) updates.height = h;\n } else {\n if (h > 0 && (typeof storeH !== 'number' || Math.abs(h - storeH) > 0.1)) updates.height = h;\n }\n if (Object.keys(updates).length > 0) {\n state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });\n }\n }\n });\n };\n const fontCleanup = setupFontLoadingListener(fabricCanvas, persistTextDimensionsAfterFontLoad);\n (fabricCanvas as any).__fontCleanup = fontCleanup;\n\n // === EVENTS ===\n \n \n // Transform state tracking (bullet-proof guard)\n // Store on canvas so it's accessible everywhere\n (fabricCanvas as any).__isUserTransforming = false;\n \n fabricCanvas.on('mouse:down', () => {\n if ((fabricCanvas as any)._currentTransform) {\n (fabricCanvas as any).__isUserTransforming = true;\n }\n });\n \n fabricCanvas.on('mouse:up', () => {\n (fabricCanvas as any).__isUserTransforming = false;\n });\n \n // Also track on object events\n fabricCanvas.on('object:scaling', () => {\n (fabricCanvas as any).__isUserTransforming = true;\n });\n \n fabricCanvas.on('object:moving', () => {\n (fabricCanvas as any).__isUserTransforming = true;\n didTransformRef.current = true; // Mark that a transform happened\n // Update group overlay immediately during drag: if a group is selected, show live bounds of whatever is moving\n const active = fabricCanvas.getActiveObject();\n if (!active) return;\n const state = useEditorStore.getState();\n const canvasPage = state.canvas.pages?.find((p) => p.id === pageId);\n const children = canvasPage?.children ?? [];\n if (!canvasPage) return;\n const ids = state.canvas.selectedIds ?? [];\n let selectedGroup: GroupNode | null = null;\n for (const id of ids) {\n const node = findNodeById(children, id);\n if (node && isGroup(node)) {\n selectedGroup = node as GroupNode;\n break;\n }\n }\n if (!selectedGroup) {\n const firstId = ids[0];\n const parent = firstId ? findParentGroup(children, firstId) : null;\n if (parent) {\n const memberIds = new Set(getAllElementIds(parent.children ?? []));\n const allSelectedInGroup = ids.every((id) => memberIds.has(id) || id === parent.id);\n if (allSelectedInGroup) selectedGroup = parent;\n }\n }\n if (!selectedGroup) return;\n const rect = active.getBoundingRect();\n if (rect.width > 0 && rect.height > 0) {\n const bounds = { left: rect.left, top: rect.top, width: rect.width, height: rect.height };\n flushSync(() => {\n setGroupOverlayLiveBoundsRef.current(bounds);\n });\n }\n });\n \n fabricCanvas.on('object:rotating', () => {\n (fabricCanvas as any).__isUserTransforming = true;\n didTransformRef.current = true; // Mark that a transform happened\n });\n \n // Selection sync TO store\n const syncSelectionToStore = () => {\n // Skip if we're currently syncing FROM store TO fabric (prevents feedback loop)\n // Also skip in preview/export mode - no selection sync needed\n if (!isActiveRef.current || isRebuildingRef.current || isSyncingSelectionToFabricRef.current || !allowSelection) return;\n\n const active = fabricCanvas.getActiveObject();\n\n // Regular selection (single element or ActiveSelection for multi-select)\n let ids = fabricCanvas\n .getActiveObjects()\n .map((o) => getObjectId(o))\n .filter((id): id is string => !!id && id !== '__background__');\n\n // Section group (fabric.Group) selected: include group id + all member element ids so UI and logic see \"group + elements\" selected\n if (ids.length === 1 && active && active instanceof fabric.Group && (active as any).__docuforgeSectionGroup) {\n const groupId = ids[0];\n const state = useEditorStore.getState();\n const currentPage = state.canvas.pages?.find((p) => p.id === pageId);\n const children = currentPage?.children ?? [];\n const node = findNodeById(children, groupId);\n if (node && isGroup(node)) {\n const memberIds = getAllElementIds((node as GroupNode).children ?? []);\n ids = [groupId, ...memberIds];\n }\n }\n\n // Single element selected (and not a section group): do not expand to group — show that element's properties and allow moving it\n if (ids.length === 1) {\n selectElements(ids, false, false);\n return;\n }\n\n // Preserve group id when Fabric selection matches a group's members (e.g. after group move we recreate selection)\n if (ids.length > 1) {\n const state = useEditorStore.getState();\n const currentPage = state.canvas.pages?.find((p) => p.id === pageId);\n const children = currentPage?.children ?? [];\n const currentSelectedIds = state.canvas.selectedIds ?? [];\n for (const sid of currentSelectedIds) {\n const node = findNodeById(children, sid);\n if (node && isGroup(node)) {\n const memberIds = new Set(getAllElementIds((node as GroupNode).children ?? []));\n const fabricIdSet = new Set(ids);\n if (memberIds.size === fabricIdSet.size && [...memberIds].every((id) => fabricIdSet.has(id))) {\n selectElements([sid, ...ids], false, true);\n return;\n }\n }\n }\n }\n selectElements(ids, false, true);\n };\n\n fabricCanvas.on('selection:created', () => {\n syncSelectionToStore();\n const activeObj = fabricCanvas.getActiveObject();\n if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);\n // Crop group: install controls based on element.useCropHandles\n if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && ((activeObj as any)._ct?.isCropGroup || (activeObj as any).__cropGroup)) {\n const objId = getObjectId(activeObj);\n const element = elementsRef.current.find((el) => el.id === objId);\n if (element?.useCropHandles) {\n installCanvaMaskControls(activeObj as any);\n } else {\n setSimpleScaleControls(activeObj as any);\n }\n }\n });\n fabricCanvas.on('selection:updated', () => {\n syncSelectionToStore();\n const activeObj = fabricCanvas.getActiveObject();\n if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);\n // Crop group: install controls based on element.useCropHandles\n if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && ((activeObj as any)._ct?.isCropGroup || (activeObj as any).__cropGroup)) {\n const objId = getObjectId(activeObj);\n const element = elementsRef.current.find((el) => el.id === objId);\n if (element?.useCropHandles) {\n installCanvaMaskControls(activeObj as any);\n } else {\n setSimpleScaleControls(activeObj as any);\n }\n }\n });\n\n fabricCanvas.on('selection:cleared', () => {\n if (!isActiveRef.current || isRebuildingRef.current || !allowSelection) return;\n setGroupOverlayLiveBoundsRef.current(null);\n groupBoundsResizingRef.current = false;\n if (skipSelectionClearOnDiscardRef.current) {\n skipSelectionClearOnDiscardRef.current = false;\n return;\n }\n editLockRef.current = false;\n syncLockedRef.current = false;\n clearSelection();\n });\n\n let dragStarted = false;\n\n const markTransforming = (target?: fabric.FabricObject | null) => {\n if (!target) return;\n \n // If it's a fabric.Group, mark all member element IDs as transforming\n if (target instanceof fabric.Group) {\n target.getObjects().forEach(obj => {\n const id = getObjectId(obj);\n if (id) transformingIdsRef.current.add(id);\n });\n return;\n }\n \n if (target instanceof fabric.ActiveSelection) {\n target.getObjects().forEach((o) => {\n const id = getObjectId(o);\n if (id) transformingIdsRef.current.add(id);\n });\n return;\n }\n const id = getObjectId(target);\n if (id) transformingIdsRef.current.add(id);\n };\n\n const clearTransforming = () => {\n transformingIdsRef.current.clear();\n };\n\n // Force \"hit → promote to crop group\" BEFORE Fabric finalizes target\n // This is the key difference: we do it in :before, and also handle move\n const isCropGroup = (o: any) => !!(o?._ct?.isCropGroup || o?.__cropGroup);\n\n const promoteToCropGroup = (opt: any) => {\n const t = opt.target;\n \n // CRITICAL: Only handle mouse:down events, not mouse:move (hover)\n // This prevents auto-selection on hover\n if (opt.e?.type !== 'mousedown' && opt.e?.type !== 'pointerdown' && opt.e?.type !== 'touchstart') {\n // For mouse:move, only set hover target, don't select\n if (t && isCropGroup(t)) {\n (fabricCanvas as any)._hoveredTarget = t;\n } else if (t?.group && isCropGroup(t.group)) {\n (fabricCanvas as any)._hoveredTarget = t.group;\n }\n return;\n }\n \n // CRITICAL: If no target, try to find crop group at click position\n if (!t) {\n const pointer = fabricCanvas.getPointer(opt.e);\n const objects = fabricCanvas.getObjects();\n // Find crop group at pointer position\n for (const obj of objects) {\n if (isCropGroup(obj) && obj.containsPoint(pointer)) {\n fabricCanvas.setActiveObject(obj);\n opt.target = obj;\n (fabricCanvas as any)._hoveredTarget = obj;\n return;\n }\n }\n return;\n }\n\n // If clicked a child inside a crop group\n const g = t.group;\n if (g && isCropGroup(g)) {\n fabricCanvas.setActiveObject(g);\n opt.target = g; // override Fabric's target for this event\n (fabricCanvas as any)._hoveredTarget = g; // helps with hover/control hit-tests\n return;\n }\n\n // If clicked the crop group itself, ensure it's selected\n if (isCropGroup(t)) {\n fabricCanvas.setActiveObject(t);\n // CRITICAL: Ensure crop group is selectable and evented\n t.set({\n selectable: true,\n evented: true,\n hasControls: true,\n hasBorders: true,\n });\n t.setCoords();\n }\n };\n\n // Edit lock helpers - prevent selection clearing during/after transforms\n const lockEdits = () => {\n editLockRef.current = true;\n editLockCountRef.current++;\n };\n \n const unlockEditsSoon = () => {\n // Unlock after React effects and store updates have committed.\n // Don't call doSync from here - that can trigger \"Maximum update depth\" (panels setRef during commit).\n // Instead: set refs and request a tick; an effect runs the sync so it happens after commit.\n const token = editLockCountRef.current;\n requestAnimationFrame(() => {\n setTimeout(() => {\n if (editLockCountRef.current !== token) return;\n editLockRef.current = false;\n if (pendingSyncRef.current) {\n pendingSyncRef.current = false;\n setUnlockRequestId((n) => n + 1);\n }\n }, 10);\n });\n };\n \n // Force unlock edits immediately (for when adding new elements)\n const forceUnlockEdits = () => {\n editLockCountRef.current++;\n editLockRef.current = false;\n syncLockedRef.current = false; // Also unlock sync\n \n // CRITICAL: Always run sync when forcing unlock (even if not pending)\n // This ensures latest state is applied immediately\n if (doSyncRef.current) {\n pendingSyncRef.current = false;\n doSyncRef.current();\n } else {\n // If sync function doesn't exist, the effect will run on next render\n // But we should still clear the pending flag\n pendingSyncRef.current = false;\n }\n };\n \n // Expose forceUnlockEdits to canvas for external use\n (fabricCanvas as any).__forceUnlockEdits = forceUnlockEdits;\n \n // Also expose via global registry for easy access\n if (typeof window !== 'undefined') {\n // Ensure registry exists and is a Map\n if (!(window as any).__fabricCanvasRegistry || !((window as any).__fabricCanvasRegistry instanceof Map)) {\n (window as any).__fabricCanvasRegistry = new Map();\n }\n (window as any).__fabricCanvasRegistry.set(pageId, {\n canvas: fabricCanvas,\n forceUnlockEdits,\n });\n }\n\n fabricCanvas.on('mouse:down', (opt) => {\n // CRITICAL: Lock selection on click (not just drag) for crop groups\n const target = opt.target as any;\n const cropGroup = target?._ct?.isCropGroup || target?.__cropGroup \n ? target \n : target?.group && ((target.group as any)._ct?.isCropGroup || (target.group as any).__cropGroup)\n ? target.group\n : null;\n \n if (cropGroup) {\n // Lock edits immediately on click (even if just a click, not drag)\n lockEdits();\n didTransformRef.current = false; // Reset transform flag\n \n // Force selection immediately (so controls appear)\n fabricCanvas.setActiveObject(cropGroup);\n cropGroup.setCoords();\n fabricCanvas.requestRenderAll();\n }\n });\n\n fabricCanvas.on('mouse:down:before', (opt) => {\n // CRITICAL: Prevent Fabric from clearing selection during handle drag\n // Only lock when actually dragging a control (not just clicking)\n if (editLockRef.current) {\n // During handle drag, prevent Fabric from processing the click\n // This prevents deselection during resize/crop operations\n const active = fabricCanvas.getActiveObject() as any;\n if (active && ((active as any)._ct?.isCropGroup || (active as any).__cropGroup)) {\n // Keep crop group selected during drag\n opt.target = active;\n if (opt.e) {\n opt.e.preventDefault?.();\n opt.e.stopPropagation?.();\n }\n }\n }\n \n // Lock sync and edits as soon as a transform is about to start\n if ((fabricCanvas as any)._currentTransform) {\n (fabricCanvas as any).__isUserTransforming = true;\n syncLockedRef.current = true; // Lock sync during transform\n lockEdits(); // Lock edits to prevent selection clearing\n }\n // Also promote crop groups\n promoteToCropGroup(opt);\n });\n fabricCanvas.on('mouse:move:before', promoteToCropGroup);\n\n // Safety: prevent Fabric scaling during crop operations\n // NOTE: Crop group click locking is now handled in the debug mouse:down handler above\n fabricCanvas.on('mouse:down', () => {\n // Lock edits if transform is active\n if ((fabricCanvas as any)._currentTransform) {\n lockEdits();\n didTransformRef.current = true; // Transform is already active\n }\n \n const o = fabricCanvas.getActiveObject();\n if (!o) return;\n (o as any).__lockScaleDuringCrop = false;\n });\n\n // Side handles now resize frame directly via actionHandler\n // No need for mouse:move handler anymore\n\n fabricCanvas.on('mouse:up', (e) => {\n clearTransforming();\n setGuides([]);\n dragStarted = false;\n \n // Clear last pointer and crop lock for crop groups\n const activeObj = fabricCanvas.getActiveObject();\n if (activeObj && ((activeObj as any).__cropGroup || (activeObj as any)._ct?.isCropGroup)) {\n // Clear crop lock\n (activeObj as any).__lockScaleDuringCrop = false;\n \n // Clear crop drag mode\n if ((activeObj as any).__cropDrag) {\n delete (activeObj as any).__cropDrag;\n }\n \n // Clear pointer tracking (stored on target object)\n if ((activeObj as any).__lastPointerForCrop) {\n delete (activeObj as any).__lastPointerForCrop;\n }\n }\n \n // CRITICAL: If it was just a click (no transform), unlock immediately\n // This allows selection to be released properly\n if (!didTransformRef.current) {\n // Just a click - unlock immediately to allow deselection\n editLockRef.current = false;\n syncLockedRef.current = false;\n } else {\n // Transform happened - clear lock after a short delay\n setTimeout(() => {\n editLockRef.current = false;\n syncLockedRef.current = false;\n }, 50);\n }\n \n // Reset transform flag for next pointer session\n didTransformRef.current = false;\n \n // CRITICAL: Always ensure unlock happens eventually (safety net)\n // This prevents editLock from being stuck true forever\n setTimeout(() => {\n if (!(fabricCanvas as any)._currentTransform) {\n // No active transform - safe to unlock\n editLockRef.current = false;\n syncLockedRef.current = false;\n }\n }, 100);\n \n // Unlock sync and edits after Fabric finalizes transform (on next tick)\n setTimeout(() => {\n syncLockedRef.current = false;\n }, 0);\n unlockEditsSoon(); // Unlock edits after React effects settle\n \n // In preview mode, handle click on dynamic field elements\n if (allowDynamicFieldClick && onDynamicFieldClick && e.target) {\n const clickedId = getObjectId(e.target);\n if (clickedId && dynamicFieldIds.includes(clickedId)) {\n onDynamicFieldClick(clickedId);\n }\n }\n });\n\n const markSimpleTransform = (e: any) => {\n if (!isActiveRef.current) return;\n markTransforming(e.target);\n };\n \n // Identity test: assign stable ID to crop groups\n fabricCanvas.on('object:added', (e) => {\n const obj = e.target;\n if (obj && ((obj as any).__cropGroup || (obj as any)._ct?.isCropGroup)) {\n if (!(obj as any).__cropGroupId) {\n (obj as any).__cropGroupId = `crop-${Date.now()}-${Math.random()}`;\n }\n }\n });\n \n // Debug: log selection clearing\n fabricCanvas.on('selection:cleared', () => {\n // Selection cleared - handled by sync logic\n });\n \n // Helpers: prevent ActiveSelection during group-bounds resize by freezing child selection\n function freezeChildrenSelection(canvas: fabric.Canvas, groupNode: GroupNode) {\n const childIds = getAllElementIds(groupNode.children ?? []);\n childIds.forEach((childId) => {\n const fabricChild = canvas.getObjects().find((o) => getObjectId(o) === childId);\n if (fabricChild) {\n (fabricChild as any).__wasSelectable = fabricChild.selectable;\n (fabricChild as any).__wasEvented = fabricChild.evented;\n fabricChild.selectable = false;\n fabricChild.evented = false;\n }\n });\n }\n function unfreezeChildrenSelection(canvas: fabric.Canvas, groupNode: GroupNode) {\n const childIds = getAllElementIds(groupNode.children ?? []);\n childIds.forEach((childId) => {\n const fabricChild = canvas.getObjects().find((o) => getObjectId(o) === childId);\n if (fabricChild && (fabricChild as any).__wasSelectable !== undefined) {\n fabricChild.selectable = !!(fabricChild as any).__wasSelectable;\n fabricChild.evented = !!(fabricChild as any).__wasEvented;\n delete (fabricChild as any).__wasSelectable;\n delete (fabricChild as any).__wasEvented;\n }\n });\n }\n\n fabricCanvas.on('object:scaling', (e) => {\n if (!isActiveRef.current) return;\n const t = e.target;\n if (t) lastResizeScaleTargetRef.current = t;\n markTransforming(t as any);\n didTransformRef.current = true; // Mark that a transform happened\n\n const obj = t;\n if (!obj) return;\n\n if (\n obj instanceof fabric.Rect ||\n obj instanceof fabric.Path ||\n obj instanceof fabric.Circle ||\n obj instanceof fabric.Triangle ||\n obj instanceof fabric.Line\n ) {\n obj.set({ strokeUniform: true });\n }\n\n // Live dimension baking for shapes that need corner/stroke geometry preservation\n // This makes scaling look correct DURING the drag, not just after release\n const objId = getObjectId(obj);\n const sourceEl = objId ? elementsRef.current.find(el => el.id === objId) : null;\n const isCornerShape = sourceEl?.type === 'shape' && (\n sourceEl.shapeType === 'circle' ||\n sourceEl.shapeType === 'rounded-rect' ||\n sourceEl.shapeType === 'triangle'\n );\n\n if (isCornerShape) {\n obj.set({ objectCaching: false });\n obj.dirty = true;\n }\n\n if (isCornerShape && !(obj instanceof fabric.FabricImage)) {\n const sx = obj.scaleX ?? 1;\n const sy = obj.scaleY ?? 1;\n if (Math.abs(sx - 1) > 0.001 || Math.abs(sy - 1) > 0.001) {\n const intrW = obj.width ?? 1;\n const intrH = obj.height ?? 1;\n let newW = Math.max(1, intrW * Math.abs(sx));\n let newH = Math.max(1, intrH * Math.abs(sy));\n\n if (obj instanceof fabric.Circle) {\n const diameter = Math.max(1, Math.min(newW, newH));\n newW = diameter;\n newH = diameter;\n (obj as fabric.Circle).set({ radius: diameter / 2 });\n }\n\n if (obj instanceof fabric.Triangle) {\n const triCenter = obj.getCenterPoint();\n obj.set({\n width: newW,\n height: newH,\n scaleX: 1,\n scaleY: 1,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n });\n obj.setPositionByOrigin(triCenter, 'center', 'center');\n } else if (obj instanceof fabric.Rect) {\n // Re-clamp corner radii to new dimensions\n const maxR = Math.min(newW, newH) / 2;\n const curRx = Math.min((obj as fabric.Rect).rx ?? 0, maxR);\n const curRy = Math.min((obj as fabric.Rect).ry ?? 0, maxR);\n obj.set({ width: newW, height: newH, scaleX: 1, scaleY: 1, rx: curRx, ry: curRy });\n } else if (obj instanceof fabric.Path && (sourceEl?.shapeType === 'rounded-rect' || sourceEl?.shapeType === 'triangle')) {\n const centerBeforeBake = obj.getCenterPoint();\n const triRTop = Math.max(0, Number(sourceEl.triRTop ?? 0));\n const triRBR = Math.max(0, Number(sourceEl.triRBR ?? 0));\n const triRBL = Math.max(0, Number(sourceEl.triRBL ?? 0));\n const pathStr = sourceEl.shapeType === 'rounded-rect'\n ? buildRoundedRectPath(\n newW,\n newH,\n Math.max(0, Number(sourceEl.rxTL ?? sourceEl.rx ?? 0)),\n Math.max(0, Number(sourceEl.rxTR ?? sourceEl.rx ?? 0)),\n Math.max(0, Number(sourceEl.rxBR ?? sourceEl.rx ?? 0)),\n Math.max(0, Number(sourceEl.rxBL ?? sourceEl.rx ?? 0))\n )\n : buildRoundedTrianglePath(newW, newH, triRTop, triRBR, triRBL);\n\n const tempPath = new fabric.Path(pathStr);\n const isTrianglePath = sourceEl.shapeType === 'triangle';\n const tempWidth = tempPath.width ?? newW;\n const tempHeight = tempPath.height ?? newH;\n const targetWidth = isTrianglePath ? newW : tempWidth;\n const targetHeight = isTrianglePath ? newH : tempHeight;\n const tempPathOffset = (tempPath as any).pathOffset ?? new fabric.Point(tempWidth / 2, tempHeight / 2);\n\n (obj as fabric.Path).set({\n path: (tempPath as any).path,\n pathOffset: tempPathOffset,\n width: targetWidth,\n height: targetHeight,\n scaleX: 1,\n scaleY: 1,\n ...(isTrianglePath\n ? {\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n objectCaching: false,\n }\n : {}),\n });\n (obj as fabric.Path).setPositionByOrigin(centerBeforeBake, 'center', 'center');\n } else {\n obj.set({ width: newW, height: newH, scaleX: 1, scaleY: 1 });\n }\n\n obj.setCoords();\n obj.dirty = true;\n }\n }\n \n // Prevent Fabric scaling during crop operations\n if ((obj as any).__lockScaleDuringCrop || (obj as any).__cropDrag) {\n // Cancel any scaling changes Fabric tried to apply\n obj.set({ scaleX: 1, scaleY: 1 });\n obj.setCoords();\n // Preserve selection\n fabricCanvas.setActiveObject(obj);\n return;\n }\n \n // Crop groups: both modes use custom controls that update frame; block Fabric default scaling\n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n const transform = (e as any).transform;\n const corner = transform?.corner || '';\n const isCorner = ['tl', 'tr', 'bl', 'br'].includes(corner);\n const isSide = ['mt', 'mb', 'ml', 'mr'].includes(corner);\n if (isCorner) {\n obj.set({ scaleX: 1, scaleY: 1 });\n obj.setCoords();\n fabricCanvas.setActiveObject(obj);\n return;\n } else if (isSide) {\n obj.set({ scaleX: 1, scaleY: 1 });\n obj.setCoords();\n fabricCanvas.setActiveObject(obj);\n return;\n }\n setTimeout(() => {\n (obj as any).__isInternalCropUpdate = false;\n }, 0);\n return;\n }\n\n // Native groups: no proxy rect; scaling is handled by Fabric.Group object:modified\n if (false && (obj as any).__groupBounds && obj instanceof fabric.Rect) {\n groupBoundsResizingRef.current = true;\n const objId = getObjectId(obj);\n if (objId) {\n const transform = (e as any).transform;\n const cornerRaw = transform?.corner ?? '';\n const corner = cornerRaw || (obj as any).__lastResizeCorner || 'mr';\n (obj as any).__lastResizeCorner = corner;\n const state = useEditorStore.getState();\n const page = state.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenForSnap = page?.children ?? [];\n let node = wrapResizeNodeRef.current?.id === objId ? wrapResizeNodeRef.current : null;\n let transformMode = wrapResizeNodeRef.current?.id === objId ? wrapResizeTransformModeRef.current : false;\n if (!node) {\n node = findNodeById(pageChildrenForSnap, objId) as GroupNode | null;\n transformMode = !!(node && isGroup(node)); // Native Fabric groups always use transform (scale) on resize\n if (node && isGroup(node)) {\n wrapResizeNodeRef.current = node as GroupNode;\n wrapResizeTransformModeRef.current = transformMode;\n }\n }\n\n // Wrap mode: only update bounds rect every event; snap to page/element bounds; reflow on mouse release.\n if (!transformMode && node && isGroup(node)) {\n if (wrapResizeFrozenGroupIdRef.current !== objId) {\n wrapResizeFrozenGroupIdRef.current = objId;\n lastDiscreteGridColsRef.current = null;\n lastDiscreteGridRowsRef.current = null;\n discreteGridDragStartBoxRef.current = null;\n lastDiscreteGridCellHRef.current = null;\n setGridResizeLabel(null);\n freezeChildrenSelection(fabricCanvas, node as GroupNode);\n obj.set({ originX: 'left', originY: 'top', lockScalingFlip: true, angle: 0, skewX: 0, skewY: 0, stroke: undefined });\n }\n let box: { left: number; top: number; width: number; height: number };\n if (transform?.corner != null && transform?.original != null) {\n box = computeBoxFromTransform(obj, transform, { minW: 20, minH: 20 });\n } else {\n obj.setCoords();\n const b = obj.getBoundingRect();\n box = { left: b.left, top: b.top, width: Math.max(20, b.width), height: Math.max(20, b.height) };\n }\n const groupNode = node as GroupNode;\n const orderedChildren = getOrderedFlexOrGridChildren(groupNode);\n const isGrid = isGridGroupForResize(groupNode);\n const useDiscreteGrid = isGrid && orderedChildren.length > 0 && (groupNode.resizeMode === 'discreteGrid' || groupNode.resizeMode !== 'freeform');\n if (useDiscreteGrid) {\n if (discreteGridDragStartBoxRef.current === null) {\n const lastBox = lastGroupBoundsBoxRef.current?.groupId === objId ? lastGroupBoundsBoxRef.current : null;\n const fallback = lastBox ?? (() => {\n const b = getNodeBounds(groupNode, pageChildrenForSnap, pageBoundsOptions);\n return { left: b.left, top: b.top, width: Math.max(20, b.width ?? 0), height: Math.max(20, b.height ?? 0) };\n })();\n discreteGridDragStartBoxRef.current = (box.width >= 20 && box.height >= 20) ? { ...box } : { ...fallback };\n const childHeights = orderedChildren.map((c) => (getNodeBounds(c, pageChildrenForSnap, pageBoundsOptions).height ?? 0) || 20);\n lastDiscreteGridCellHRef.current = Math.max(...childHeights, 1);\n }\n const fixedCellH = lastDiscreteGridCellHRef.current ?? undefined;\n const { snappedBox, cols, rows } = computeDiscreteGridSnappedBox(\n groupNode,\n pageChildrenForSnap,\n box\n );\n box = snappedBox;\n const anchorBox = discreteGridDragStartBoxRef.current ?? box;\n const { vertical: vGuides, horizontal: hGuides } = getDiscreteGridSnapGuides(groupNode, pageChildrenForSnap, anchorBox, cols, rows, fixedCellH ?? undefined);\n const gridGuides: SnapGuide[] = [\n ...vGuides.map((v) => ({ type: 'vertical' as const, position: v.position, start: box.top - 48, end: box.top + box.height + 48, active: v.active })),\n ...hGuides.map((h) => ({ type: 'horizontal' as const, position: h.position, start: box.left - 48, end: box.left + box.width + 48, active: h.active })),\n ];\n setGuides(gridGuides);\n setGridResizeLabel({ cols, rows, x: box.left + box.width / 2, y: box.top - 32 });\n } else {\n const layoutSnap = getGroupLayoutSnapTargets(groupNode, pageChildrenForSnap, box);\n box = applyScaleSnapToBox(box, corner, fabricCanvas, canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold, objId, {\n hysteresis: 6,\n activeSnapRef: groupResizeActiveSnapRef,\n roundSnappedOnly: true,\n verticalTargetsExtra: layoutSnap.verticalTargets,\n horizontalTargetsExtra: layoutSnap.horizontalTargets,\n });\n }\n lastGroupBoundsBoxRef.current = { groupId: objId, ...box };\n obj.set({ left: box.left, top: box.top, width: box.width, height: box.height, scaleX: 1, scaleY: 1, angle: 0, skewX: 0, skewY: 0, stroke: undefined });\n obj.setCoords();\n if (!useDiscreteGrid && groupBoundsGuidesRafRef.current === null) {\n groupBoundsGuidesRafRef.current = requestAnimationFrame(() => {\n groupBoundsGuidesRafRef.current = null;\n const active = fabricCanvas.getActiveObject();\n const c = (active as any)?.__lastResizeCorner ?? corner;\n if (active) setGuides(calculateScaleSnapGuidesCallback(active, c));\n });\n }\n if (containerResizeRafRef.current === null) {\n containerResizeRafRef.current = requestAnimationFrame(() => {\n containerResizeRafRef.current = null;\n const fc = fabricRef.current;\n const active = fc?.getActiveObject();\n const pending = lastGroupBoundsBoxRef.current;\n const groupNodeRAF = wrapResizeNodeRef.current;\n if (!fc || active !== obj || !(obj as any).__groupBounds || !pending || pending.groupId !== objId || !groupNodeRAF || groupNodeRAF.id !== objId) return;\n const isDiscreteGridResize = (groupNodeRAF.resizeMode === 'discreteGrid' || groupNodeRAF.resizeMode !== 'freeform') && isGridGroupForResize(groupNodeRAF);\n if (isDiscreteGridResize) {\n fc.requestRenderAll();\n return;\n }\n const pageChildrenAfter = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const layoutKey = computeGroupResizeLayoutKey(groupNodeRAF, pending.width, pending.height, 16, pageChildrenAfter);\n if (layoutKey === lastGroupResizeLayoutKeyRef.current) {\n fc.requestRenderAll();\n return;\n }\n lastGroupResizeLayoutKeyRef.current = layoutKey;\n setDragBoundsOverride({ [pending.groupId]: { left: pending.left, top: pending.top, width: pending.width, height: pending.height } });\n const reflowUpdates = reflowFromGroup(pageChildrenAfter, pending.groupId);\n const contentBox = getGroupContentBox(groupNodeRAF, pageChildrenAfter, pageBoundsOptions);\n clearDragBoundsOverride();\n const applied = lastGroupResizeAppliedRef.current;\n const EPS = 0.5;\n const WIDTH_BUCKET = 8;\n const childIds = getAllElementIds(groupNodeRAF.children ?? []);\n for (const childId of childIds) {\n const fabricObj = fc.getObjects().find((o) => getObjectId(o) === childId);\n if (!fabricObj) continue;\n const childNode = findNodeById(pageChildrenAfter, childId);\n if (!childNode) continue;\n const u = reflowUpdates.get(childId);\n const relLeft = u?.left ?? (childNode.left ?? 0);\n const relTop = u?.top ?? (childNode.top ?? 0);\n const absLeft = contentBox.left + relLeft;\n const absTop = contentBox.top + relTop;\n const size = getNodeBounds(childNode, pageChildrenAfter, pageBoundsOptions);\n const w = Math.max(1, u?.width ?? size.width);\n const h = Math.max(1, u?.height ?? size.height);\n const next = { left: absLeft, top: absTop, width: w, height: h };\n const prev = applied.get(childId);\n const changed = !prev || Math.abs(prev.left - next.left) > EPS || Math.abs(prev.top - next.top) > EPS || Math.abs(prev.width - next.width) > EPS || Math.abs(prev.height - next.height) > EPS;\n if (!changed) continue;\n applied.set(childId, next);\n fabricObj.set({ left: absLeft, top: absTop, scaleX: 1, scaleY: 1 });\n if (fabricObj instanceof fabric.Textbox) {\n const prevW = prev?.width ?? 0;\n const bucketChanged = Math.floor(prevW / WIDTH_BUCKET) !== Math.floor(w / WIDTH_BUCKET);\n if (bucketChanged || !prev) {\n fabricObj.set({ width: w, objectCaching: false });\n fabricObj.initDimensions();\n (fabricObj as any).dirty = true;\n }\n fabricObj.setCoords();\n } else {\n if ('width' in fabricObj && 'height' in fabricObj) (fabricObj as any).set({ width: w, height: h });\n fabricObj.setCoords();\n }\n }\n fc.requestRenderAll();\n });\n }\n fabricCanvas.requestRenderAll();\n return;\n }\n\n // Transform mode: freeze children so Fabric never builds ActiveSelection during drag\n if (transformMode && node && isGroup(node)) {\n freezeChildrenSelection(fabricCanvas, node);\n }\n\n // Transform mode or non-group: use RAF for scale/axis-locked logic\n if (containerResizeRafRef.current !== null) cancelAnimationFrame(containerResizeRafRef.current);\n containerResizeRafRef.current = requestAnimationFrame(() => {\n containerResizeRafRef.current = null;\n const fc = fabricRef.current;\n const active = fc?.getActiveObject();\n if (!fc || active !== obj || !(obj as any).__groupBounds) return;\n const stateInner = useEditorStore.getState();\n const pageInner = stateInner.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenInner = pageInner?.children ?? [];\n const nodeInner = findNodeById(pageChildrenInner, objId) as GroupNode | null;\n const transformModeInner = !!(nodeInner && isGroup(nodeInner)); // Native Fabric groups always transform on resize\n\n if (transformModeInner && nodeInner && isGroup(nodeInner)) {\n // Transform mode: scale children (Photoshop-like). No reflow; bake rect and scale each child's pos/size.\n const cornerInner = (obj as any).__lastResizeCorner ?? '';\n const isWidthOnly = cornerInner === 'ml' || cornerInner === 'mr';\n const isHeightOnly = cornerInner === 'mt' || cornerInner === 'mb';\n const b = obj.getBoundingRect();\n const scaleX = isHeightOnly ? 1 : (obj.scaleX ?? 1);\n const scaleY = isWidthOnly ? 1 : (obj.scaleY ?? 1);\n const contentBox = getGroupContentBox(nodeInner, pageChildrenInner, pageBoundsOptions);\n const childUpdates: { id: string; left: number; top: number; width: number; height: number }[] = [];\n for (const child of nodeInner.children ?? []) {\n const childId = isElement(child) ? child.id : (child as GroupNode).id;\n const childNode = findNodeById(pageChildrenInner, childId);\n if (!childNode) continue;\n const abs = getAbsoluteBounds(childNode, pageChildrenInner);\n const relLeft = abs.left - contentBox.left;\n const relTop = abs.top - contentBox.top;\n const bounds = getNodeBounds(childNode, pageChildrenInner);\n childUpdates.push({\n id: childId,\n left: relLeft * scaleX,\n top: relTop * scaleY,\n width: Math.max(1, bounds.width * scaleX),\n height: Math.max(1, bounds.height * scaleY),\n });\n }\n for (const u of childUpdates) {\n useEditorStore.getState().updateNode(u.id, { left: u.left, top: u.top, width: u.width, height: u.height }, { recordHistory: false, skipLayoutRecalc: true });\n }\n useEditorStore.getState().updateNode(objId, { left: b.left, top: b.top }, { recordHistory: false, skipLayoutRecalc: true });\n obj.set({ left: b.left, top: b.top, scaleX: 1, scaleY: 1 });\n obj.setCoords();\n const stateAfter = useEditorStore.getState();\n const pageAfter = stateAfter.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenAfter = pageAfter?.children ?? [];\n const groupAfter = findNodeById(pageChildrenAfter, objId);\n if (groupAfter && isGroup(groupAfter)) {\n const childIds = getAllElementIds((groupAfter as GroupNode).children ?? []);\n for (const childId of childIds) {\n const fabricObj = fc.getObjects().find((o) => getObjectId(o) === childId);\n if (!fabricObj) continue;\n const childNode = findNodeById(pageChildrenAfter, childId);\n if (!childNode) continue;\n const abs = getAbsoluteBounds(childNode, pageChildrenAfter);\n const size = getNodeBounds(childNode, pageChildrenAfter, pageBoundsOptions);\n fabricObj.set({ left: abs.left, top: abs.top, width: size.width, height: size.height, scaleX: 1, scaleY: 1 });\n fabricObj.setCoords();\n if (fabricObj instanceof fabric.Textbox) {\n fabricObj.set({ objectCaching: false });\n (fabricObj as any).dirty = true;\n }\n }\n }\n fc.requestRenderAll();\n return;\n }\n\n // Wrap mode: axis-locked resize, reflow children (Textbox-like)\n const cornerRAF = (obj as any).__lastResizeCorner ?? '';\n const isWidthOnly = cornerRAF === 'ml' || cornerRAF === 'mr';\n const isHeightOnly = cornerRAF === 'mt' || cornerRAF === 'mb';\n if (isWidthOnly) {\n obj.set({ scaleY: 1 });\n obj.setCoords();\n } else if (isHeightOnly) {\n obj.set({ scaleX: 1 });\n obj.setCoords();\n }\n const b = obj.getBoundingRect();\n useEditorStore.getState().updateNode(objId, { left: b.left, top: b.top }, { recordHistory: false, skipLayoutRecalc: true });\n obj.set({ left: b.left, top: b.top, scaleX: 1, scaleY: 1 });\n obj.setCoords();\n const stateAfter = useEditorStore.getState();\n const pageAfter = stateAfter.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenAfter = pageAfter?.children ?? [];\n const groupAfter = findNodeById(pageChildrenAfter, objId);\n if (groupAfter && isGroup(groupAfter)) {\n const childIds = getAllElementIds((groupAfter as GroupNode).children ?? []);\n for (const childId of childIds) {\n const fabricObj = fc.getObjects().find((o) => getObjectId(o) === childId);\n if (!fabricObj) continue;\n const childNode = findNodeById(pageChildrenAfter, childId);\n if (!childNode) continue;\n const abs = getAbsoluteBounds(childNode, pageChildrenAfter);\n const size = getNodeBounds(childNode, pageChildrenAfter, pageBoundsOptions);\n fabricObj.set({\n left: abs.left,\n top: abs.top,\n width: size.width,\n height: size.height,\n scaleX: 1,\n scaleY: 1,\n });\n fabricObj.setCoords();\n if (fabricObj instanceof fabric.Textbox) {\n fabricObj.set({ width: Math.max(1, size.width), objectCaching: false });\n fabricObj.initDimensions();\n (fabricObj as any).dirty = true;\n }\n }\n }\n fc.requestRenderAll();\n });\n }\n return;\n }\n\n // ActiveSelection (group or multiselect): scaling is handled by Fabric; we persist on object:modified.\n // Get the corner/control being used for scaling\n const transform = (e as any).transform;\n const corner = transform?.corner || '';\n \n const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);\n setGuides(scaleGuides);\n });\n\n // Textbox uses 'resizing' event instead of 'scaling' when changing width\n fabricCanvas.on('object:resizing', (e) => {\n if (!isActiveRef.current) return;\n const t = e.target;\n if (t) lastResizeScaleTargetRef.current = t;\n markTransforming(t as any);\n \n const obj = t;\n if (!obj) return;\n \n // Get the corner/control being used for resizing\n const transform = (e as any).transform;\n const corner = transform?.corner || '';\n \n const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);\n setGuides(scaleGuides);\n });\n fabricCanvas.on('object:rotating', (e) => {\n markSimpleTransform(e);\n didTransformRef.current = true; // Mark that a transform happened\n });\n fabricCanvas.on('object:skewing', (e) => {\n markSimpleTransform(e);\n didTransformRef.current = true; // Mark that a transform happened\n });\n\n fabricCanvas.on('object:moving', (e) => {\n if (!isActiveRef.current) return;\n markTransforming(e.target as any);\n didTransformRef.current = true; // Mark that a transform happened\n\n if (!dragStarted) {\n dragStarted = true;\n const selectedEls = elementsRef.current.filter((el) => selectedIdsRef.current.includes(el.id));\n const mouseEvent = e.e as MouseEvent;\n if (selectedEls.length > 0 && mouseEvent) {\n onDragStart?.(selectedEls, mouseEvent, pageId);\n }\n }\n\n const obj = e.target;\n if (!obj) return;\n const snapTarget = fabricCanvas.getActiveObject() ?? obj;\n\n const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);\n setGuides(newGuides);\n\n if (snapDx !== 0 || snapDy !== 0) {\n snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });\n }\n });\n\n // Debounce timer for persisting crop group updates\n let cropGroupSaveTimer: NodeJS.Timeout | null = null;\n\n fabricCanvas.on('object:modified', (e: { target?: fabric.FabricObject }) => {\n try {\n dragStarted = false;\n setGuides([]);\n setGroupOverlayLiveBoundsRef.current(null);\n onDragEnd?.();\n\n // CRITICAL: Lock immediately so any re-render from our store update sees edit locked.\n lockEdits();\n\n const modifiedTarget = e.target;\n const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;\n const modifiedTargetElement = modifiedTargetId\n ? elementsRef.current.find((el) => el.id === modifiedTargetId)\n : null;\n\n if (modifiedTarget && modifiedTargetElement?.type === 'shape') {\n if (modifiedTargetElement.shapeType === 'triangle') {\n modifiedTarget.set({\n objectCaching: false,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n });\n } else {\n modifiedTarget.set({ objectCaching: true });\n }\n modifiedTarget.dirty = true;\n modifiedTarget.setCoords();\n }\n\n // IDs we add to justModifiedIdsRef this run; clear them after a delay so future syncs can apply non-position updates\n const modifiedIdsThisRound = new Set<string>();\n\n // Only update store in editor mode\n if (!allowEditing) {\n unlockEditsSoon();\n return;\n }\n\n const active = fabricCanvas.getActiveObject();\n const activeId = active ? getObjectId(active) : null;\n const activeObjectsForLog = fabricCanvas.getActiveObjects();\n if (typeof console !== 'undefined' && console.log) {\n console.log('[object:modified] activeId=', activeId, 'isGroup=', active instanceof fabric.Group, 'isActiveSelection=', active instanceof fabric.ActiveSelection, 'activeObjectsCount=', activeObjectsForLog?.length ?? 0);\n }\n\n // When the modified object is a single element (e.g. text) that belongs to a section group, Fabric often\n // reports the child as active when the user actually dragged the group. Prioritise group position: if the\n // section group moved, update only the group's left/top and return. Otherwise persist child (resize).\n if (active && activeId && activeId !== '__background__' && !(active instanceof fabric.Group)) {\n const pageChildrenForParent = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const parentGroup = findParentGroup(pageChildrenForParent, activeId);\n if (parentGroup && isGroup(parentGroup) && parentGroup.backgroundColor) {\n let fabricSectionGroup: fabric.Group | null = (active as any).group && (active as any).group instanceof fabric.Group ? (active as any).group : null;\n if (!fabricSectionGroup || !(fabricSectionGroup as any).__docuforgeSectionGroup) {\n fabricSectionGroup = fabricCanvas.getObjects().find((o) => getObjectId(o) === parentGroup.id && (o as any).__docuforgeSectionGroup) as fabric.Group ?? null;\n }\n if (fabricSectionGroup && (fabricSectionGroup as any).__docuforgeSectionGroup) {\n const fabricGroupId = getObjectId(fabricSectionGroup);\n const groupId = parentGroup.id;\n const pageChildrenSec = pageChildrenForParent;\n const groupNodeForCompare = fabricGroupId ? findNodeById(pageChildrenSec, fabricGroupId) : null;\n const storeBounds = groupNodeForCompare ? getAbsoluteBounds(groupNodeForCompare, pageChildrenSec) : null;\n const storeCenterX = storeBounds ? storeBounds.left + storeBounds.width / 2 : (fabricSectionGroup.left ?? 0);\n const storeCenterY = storeBounds ? storeBounds.top + storeBounds.height / 2 : (fabricSectionGroup.top ?? 0);\n const fabricCenterX = fabricSectionGroup.left ?? 0;\n const fabricCenterY = fabricSectionGroup.top ?? 0;\n const groupMoved = Math.abs(fabricCenterX - storeCenterX) > 2 || Math.abs(fabricCenterY - storeCenterY) > 2;\n if (groupMoved) {\n lastResizeScaleTargetRef.current = null;\n const movedGroupId = getObjectId(fabricSectionGroup);\n if (movedGroupId) {\n const w = (fabricSectionGroup.width ?? 0) * (fabricSectionGroup.scaleX ?? 1);\n const h = (fabricSectionGroup.height ?? 0) * (fabricSectionGroup.scaleY ?? 1);\n const groupLeft = fabricCenterX - w / 2;\n const groupTop = fabricCenterY - h / 2;\n const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, movedGroupId, pageChildrenSec);\n useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });\n }\n commitHistory();\n unlockEditsSoon();\n return;\n }\n lastResizeScaleTargetRef.current = null;\n const node = findNodeById(pageChildrenSec, groupId) as GroupNode | null;\n if (node && isGroup(node)) {\n const objs = fabricSectionGroup.getObjects();\n const kids = node.children ?? [];\n const gap = isStackLayoutMode(node.layoutMode) ? (node.stackSpacing ?? 8) : 0;\n const groupScaleX = fabricSectionGroup.scaleX ?? 1;\n const groupScaleY = fabricSectionGroup.scaleY ?? 1;\n const w = (fabricSectionGroup.width ?? 0) * (fabricSectionGroup.scaleX ?? 1);\n const h = (fabricSectionGroup.height ?? 0) * (fabricSectionGroup.scaleY ?? 1);\n let prevBottom = 0;\n for (let i = 0; i < objs.length; i++) {\n const childObj = objs[i];\n const childId = getObjectId(childObj);\n const child = kids[i];\n if (!childId || !child || child.id !== childId) continue;\n const cW = (childObj.width ?? 0) * (childObj.scaleX ?? 1) * groupScaleX;\n const cH = (childObj.height ?? 0) * (childObj.scaleY ?? 1) * groupScaleY;\n const grLeft = (childObj.left ?? 0) + w / 2;\n const grTop = (childObj.top ?? 0) + h / 2;\n let storeTop: number;\n if (isStackLayoutMode(node.layoutMode) && i > 0) {\n storeTop = Math.max(0, grTop - prevBottom - gap);\n } else {\n storeTop = grTop;\n }\n prevBottom = grTop + cH;\n const updates: { left: number; top: number; width?: number; height?: number } = { left: grLeft, top: storeTop };\n const lockWidthForAutoShrinkText = isElement(child) && child.type === 'text' && child.overflowPolicy === 'auto-shrink';\n // Auto-shrink text: keep width fixed, but allow height changes so stack reflow can pull siblings up.\n if (!lockWidthForAutoShrinkText && cW > 0) updates.width = cW;\n if (lockWidthForAutoShrinkText) {\n const storedChildHeight = isElement(child) && typeof child.height === 'number' ? child.height : undefined;\n if (cH > 0) {\n // Auto-shrink must never persist growth, otherwise stack siblings get pushed downward incorrectly.\n updates.height = typeof storedChildHeight === 'number' ? Math.min(storedChildHeight, cH) : cH;\n }\n } else if (cH > 0) {\n updates.height = cH;\n }\n useEditorStore.getState().updateNode(childId, updates, { recordHistory: false, skipLayoutRecalc: false });\n if (childId !== '__background__') {\n modifiedIdsThisRound.add(childId);\n justModifiedIdsRef.current.add(childId);\n }\n }\n }\n if (node && isStackLayoutMode(node.layoutMode)) {\n useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);\n }\n const stateAfter = useEditorStore.getState();\n const pageAfter = stateAfter.canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const groupNodeAfter = findNodeById(pageAfter, groupId);\n if (groupNodeAfter) {\n const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);\n const centerX = abs.left + abs.width / 2;\n const centerY = abs.top + abs.height / 2;\n fabricSectionGroup.set({ left: centerX, top: centerY });\n fabricSectionGroup.setCoords();\n }\n setTimeout(() => modifiedIdsThisRound.forEach((id) => justModifiedIdsRef.current.delete(id)), 150);\n commitHistory();\n unlockEditsSoon();\n return;\n }\n }\n }\n\n // Handle crop groups - persist AFTER interaction completes (debounced)\n if (active && (active as any).__cropGroup) {\n const objId = getObjectId(active);\n if (objId) {\n // So the next sync (after unlockEditsSoon) skips overwriting position from store\n modifiedIdsThisRound.add(objId);\n justModifiedIdsRef.current.add(objId);\n const ct = (active as any).__cropData;\n if (ct) {\n // Clear any pending save\n if (cropGroupSaveTimer) {\n clearTimeout(cropGroupSaveTimer);\n }\n \n // Debounce save to avoid React rerenders during drag\n cropGroupSaveTimer = setTimeout(() => {\n const { updateElement } = useEditorStore.getState();\n const img = ct._img;\n const zoom = (img as any)?._ct?.zoom ?? 1;\n const panX = (img as any)?._ct?.panX ?? 0.5;\n const panY = (img as any)?._ct?.panY ?? 0.5;\n \n const stateCrop = useEditorStore.getState();\n const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenCrop = pageCrop?.children ?? [];\n // Crop group uses center in Fabric; store expects top-left\n const absLeft = (active.left ?? 0) - ct.frameW / 2;\n const absTop = (active.top ?? 0) - ct.frameH / 2;\n const storePosCrop = absoluteToStorePosition(absLeft, absTop, objId, pageChildrenCrop);\n \n updateElement(objId, {\n width: ct.frameW,\n height: ct.frameH,\n left: storePosCrop.left,\n top: storePosCrop.top,\n angle: active.angle ?? 0,\n cropPanX: panX,\n cropPanY: panY,\n cropZoom: zoom,\n }, { recordHistory: false });\n \n // Clear the flag\n (active as any).__isInternalCropUpdate = false;\n \n // Preserve selection\n fabricCanvas.setActiveObject(active);\n // Clear justModified after delay so future syncs can apply position from store\n setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);\n }, 0);\n \n unlockEditsSoon();\n return; // Skip normal processing for crop groups\n }\n }\n }\n\n // Section groups (fabric.Group with __docuforgeSectionGroup): persist group position and child positions/sizes.\n // When the user repositions the group, always update the group's left/top (not the children). When a child is resized, revert group and update children.\n if (active && active instanceof fabric.Group && (active as any).__docuforgeSectionGroup && getObjectId(active)) {\n const groupId = getObjectId(active)!;\n const pageChildrenSec = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const modifiedTarget = e?.target;\n const resizeScaleTarget = lastResizeScaleTargetRef.current;\n lastResizeScaleTargetRef.current = null;\n const children = active.getObjects();\n const groupNodeSec = findNodeById(pageChildrenSec, groupId);\n const storeBounds = groupNodeSec ? getAbsoluteBounds(groupNodeSec, pageChildrenSec) : null;\n const storeCenterX = storeBounds ? storeBounds.left + storeBounds.width / 2 : (active.left ?? 0);\n const storeCenterY = storeBounds ? storeBounds.top + storeBounds.height / 2 : (active.top ?? 0);\n const fabricCenterX = active.left ?? 0;\n const fabricCenterY = active.top ?? 0;\n const moveDx = Math.abs(fabricCenterX - storeCenterX);\n const moveDy = Math.abs(fabricCenterY - storeCenterY);\n const groupMoved = moveDx > 2 || moveDy > 2;\n // Child modified = user resized a child (target is child or we had a resize); only then revert group and don't update group position\n let isChildModified =\n (modifiedTarget && children.includes(modifiedTarget)) ||\n (resizeScaleTarget && children.includes(resizeScaleTarget));\n if (!isChildModified && !groupMoved && groupNodeSec && (active.scaleX ?? 1) === 1 && (active.scaleY ?? 1) === 1) {\n if (moveDx < 4 && moveDy < 4) isChildModified = true;\n }\n // Always update group position when the group was moved (repositioned); never skip this so group left/top stay correct\n if (groupMoved || !isChildModified) {\n const centerX = active.left ?? 0;\n const centerY = active.top ?? 0;\n const w = (active.width ?? 0) * (active.scaleX ?? 1);\n const h = (active.height ?? 0) * (active.scaleY ?? 1);\n const groupLeft = centerX - w / 2;\n const groupTop = centerY - h / 2;\n const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildrenSec);\n useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });\n }\n // Do not persist children's left/top: section group getObjects() are flattened leaf elements\n // while node.children are direct children (subgroups), so writing would corrupt positions.\n const node = findNodeById(pageChildrenSec, groupId) as GroupNode | null;\n if (isChildModified && node && !groupMoved) {\n const stateAfter = useEditorStore.getState();\n const pageAfter = stateAfter.canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const groupNodeAfter = findNodeById(pageAfter, groupId);\n if (groupNodeAfter) {\n const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);\n const centerX = abs.left + abs.width / 2;\n const centerY = abs.top + abs.height / 2;\n active.set({ left: centerX, top: centerY });\n active.setCoords();\n }\n }\n commitHistory();\n unlockEditsSoon();\n return;\n }\n\n // Single Fabric group (not section, not ActiveSelection): when repositioned, update only the group's left/top.\n // Skip ActiveSelection so multi-select falls through to the move-parent path (update parent group, not children).\n // Never update children here: prioritise group position so the moved group's position is what gets persisted.\n if (active && active instanceof fabric.Group && !(active instanceof fabric.ActiveSelection) && getObjectId(active)) {\n const groupId = getObjectId(active)!;\n const pageChildren = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const w = (active.width ?? 0) * (active.scaleX ?? 1);\n const h = (active.height ?? 0) * (active.scaleY ?? 1);\n const centerX = active.left ?? 0;\n const centerY = active.top ?? 0;\n const groupLeft = centerX - w / 2;\n const groupTop = centerY - h / 2;\n const storePos = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildren);\n useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });\n commitHistory();\n unlockEditsSoon();\n return;\n }\n\n const { updateElement } = useEditorStore.getState();\n \n // Unlock scaling for all objects after modification (in case it was locked)\n if (active instanceof fabric.FabricImage) {\n active.set({\n lockScalingX: false,\n lockScalingY: false,\n });\n }\n\n // Regular element(s) modified (single or ActiveSelection)\n const activeObj = fabricCanvas.getActiveObject();\n let activeObjects = fabricCanvas.getActiveObjects();\n // Fabric may return [activeSelection] when active is ActiveSelection (e.g. in minified build\n // activeObj.constructor.name !== 'ActiveSelection'), so the only item has no __docuforgeId.\n // Unwrap: if the single item has getObjects and no id, use its getObjects() as the real list.\n if (activeObjects.length === 1 && typeof (activeObjects[0] as any).getObjects === 'function' && !getObjectId(activeObjects[0])) {\n activeObjects = (activeObjects[0] as any).getObjects();\n }\n \n // Check if we're inside an ActiveSelection (multi-select; include unwrapped case when active has no id)\n const isActiveSelection = activeObj instanceof fabric.ActiveSelection || activeObjects.length > 1;\n \n // Check if all selected elements belong to the same group\n // If so, move the entire group together instead of individual elements\n const { getCurrentPage: getCurrentPageStore } = useEditorStore.getState();\n const currentPage = getCurrentPageStore();\n const selectedElementIds = activeObjects\n .map(obj => getObjectId(obj))\n .filter((id): id is string => !!id && id !== '__background__');\n \n // Never take \"group move only\" path when a crop group is selected - we must persist its dimensions (scale bake)\n const anyCropGroup = activeObjects.some((o: fabric.FabricObject) => (o as any).__cropGroup);\n\n if (selectedElementIds.length > 0 && !anyCropGroup) {\n const pageChildren = currentPage.children ?? [];\n const firstObj = activeObjects[0];\n const firstId = getObjectId(firstObj);\n const parentGroups = selectedElementIds\n .map((id) => findParentGroup(pageChildren, id))\n .filter((g): g is GroupNode => g !== null);\n const sameDirectParent = parentGroups.length > 0 && parentGroups.every((g) => g.id === parentGroups[0].id);\n const commonAncestor = findCommonAncestorGroup(selectedElementIds, pageChildren);\n // Only move the whole group when layout is stack-based; for absolute-layout groups,\n // let individual elements move freely (fall through to per-element persist below).\n const candidateGroup = sameDirectParent ? parentGroups[0] : commonAncestor;\n const candidateIsStack = candidateGroup && isStackLayoutMode(candidateGroup.layoutMode);\n const groupToMove = candidateIsStack ? candidateGroup : null;\n\n if (groupToMove && firstId) {\n const firstNode = findNodeById(pageChildren, firstId);\n if (firstNode) {\n const origAbs = getAbsoluteBounds(firstNode, pageChildren);\n let absoluteLeft: number;\n let absoluteTop: number;\n if (isActiveSelection && activeObj) {\n const selectionMatrix = activeObj.calcTransformMatrix();\n const relativePoint = { x: firstObj.left ?? 0, y: firstObj.top ?? 0 };\n const absolutePoint = fabric.util.transformPoint(relativePoint, selectionMatrix);\n absoluteLeft = absolutePoint.x;\n absoluteTop = absolutePoint.y;\n } else {\n absoluteLeft = firstObj.left ?? 0;\n absoluteTop = firstObj.top ?? 0;\n }\n const deltaX = absoluteLeft - origAbs.left;\n const deltaY = absoluteTop - origAbs.top;\n const hadScale = isActiveSelection && activeObj && (Math.abs((activeObj.scaleX ?? 1) - 1) > 0.01 || Math.abs((activeObj.scaleY ?? 1) - 1) > 0.01);\n\n if (!hadScale && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {\n if (typeof console !== 'undefined' && console.log) {\n console.log('[object:modified] plain-groups: moving group id=', groupToMove.id, 'newLeft=', (groupToMove.left ?? 0) + deltaX, 'newTop=', (groupToMove.top ?? 0) + deltaY);\n }\n const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();\n const newLeft = (groupToMove.left ?? 0) + deltaX;\n const newTop = (groupToMove.top ?? 0) + deltaY;\n updateNodeStore(groupToMove.id, { left: newLeft, top: newTop }, { recordHistory: false, skipLayoutRecalc: true });\n commitHistoryStore();\n\n if (isActiveSelection && activeObj instanceof fabric.ActiveSelection) {\n skipSelectionClearOnDiscardRef.current = true;\n fabricCanvas.discardActiveObject();\n }\n const stateAfter = useEditorStore.getState();\n const pageAfter = stateAfter.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenAfter = pageAfter?.children ?? [];\n for (const obj of activeObjects) {\n const objId = getObjectId(obj);\n if (!objId || objId === '__background__') continue;\n const node = findNodeById(pageChildrenAfter, objId);\n if (node) {\n const abs = getAbsoluteBounds(node, pageChildrenAfter);\n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n const w = (obj.width ?? 0) * (obj.scaleX ?? 1);\n const h = (obj.height ?? 0) * (obj.scaleY ?? 1);\n obj.set({ left: abs.left + w / 2, top: abs.top + h / 2 });\n } else {\n obj.set({ left: abs.left, top: abs.top });\n }\n obj.setCoords();\n }\n }\n if (isActiveSelection && activeObjects.length > 0) {\n const selection = new fabric.ActiveSelection([...activeObjects], { canvas: fabricCanvas });\n fabricCanvas.setActiveObject(selection);\n skipSelectionClearOnDiscardRef.current = false;\n }\n fabricCanvas.requestRenderAll();\n\n elementsRef.current = getCurrentElements();\n for (const obj of activeObjects) {\n const objId = getObjectId(obj);\n if (objId && objId !== '__background__') {\n justModifiedIdsRef.current.add(objId);\n modifiedIdsThisRound.add(objId);\n }\n }\n setTimeout(() => modifiedIdsThisRound.forEach((id) => justModifiedIdsRef.current.delete(id)), 150);\n unlockEditsSoon();\n return;\n }\n }\n }\n }\n\n for (const obj of activeObjects) {\n const objId = getObjectId(obj);\n if (!objId || objId === '__background__') continue;\n\n let intrinsicWidth: number;\n let intrinsicHeight: number;\n\n if (obj instanceof fabric.Circle) {\n const r = (obj as any).radius ?? 0;\n intrinsicWidth = r * 2;\n intrinsicHeight = r * 2;\n } else if (obj instanceof fabric.Ellipse) {\n intrinsicWidth = (obj.rx ?? 0) * 2;\n intrinsicHeight = (obj.ry ?? 0) * 2;\n } else if (obj instanceof fabric.Textbox) {\n intrinsicWidth = obj.width ?? 0;\n intrinsicHeight = obj.height ?? 0;\n } else if (obj instanceof fabric.Line) {\n // Line: length from coordinates; no height (avoid slant from scaleY/skew)\n const x1 = (obj as any).x1 ?? 0, y1 = (obj as any).y1 ?? 0, x2 = (obj as any).x2 ?? 0, y2 = (obj as any).y2 ?? 0;\n intrinsicWidth = Math.hypot(x2 - x1, y2 - y1) * (obj.scaleX ?? 1);\n intrinsicHeight = 0;\n } else {\n intrinsicWidth = obj.width ?? 0;\n intrinsicHeight = obj.height ?? 0;\n }\n\n // Get the absolute transform matrix directly from Fabric\n // This is the most reliable approach - no decomposition/recomposition errors\n let absoluteMatrix: [number, number, number, number, number, number];\n \n if (isActiveSelection && activeObj) {\n // For ActiveSelection, combine selection transform with object transform\n const selectionMatrix = activeObj.calcTransformMatrix();\n const objOwnMatrix = obj.calcOwnMatrix();\n const combined = fabric.util.multiplyTransformMatrices(selectionMatrix, objOwnMatrix);\n absoluteMatrix = combined as [number, number, number, number, number, number];\n } else {\n // Single object - get its full transform matrix\n const matrix = obj.calcTransformMatrix();\n absoluteMatrix = matrix as [number, number, number, number, number, number];\n }\n \n // Decompose for the individual properties (needed for backwards compatibility and some operations)\n const decomposed = fabric.util.qrDecompose(absoluteMatrix);\n\n justModifiedIdsRef.current.add(objId);\n modifiedIdsThisRound.add(objId);\n\n // Calculate absolute left/top position\n // When in ActiveSelection, obj.left/top are relative to selection center\n // We need to transform to absolute canvas coordinates\n let absoluteLeft: number;\n let absoluteTop: number;\n \n if (isActiveSelection && activeObj) {\n // Transform the object's origin point through the selection matrix\n // to get the absolute canvas position\n const selectionMatrix = activeObj.calcTransformMatrix();\n const relativePoint = { x: obj.left ?? 0, y: obj.top ?? 0 };\n const absolutePoint = fabric.util.transformPoint(relativePoint, selectionMatrix);\n absoluteLeft = absolutePoint.x;\n absoluteTop = absolutePoint.y;\n } else {\n // Single object - use direct values\n absoluteLeft = obj.left ?? 0;\n absoluteTop = obj.top ?? 0;\n }\n\n // For crop groups Fabric stores center (origin center); store expects top-left\n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n const ct = (obj as any).__cropData;\n const w = ct?.frameW ?? (obj.width ?? 0) * (obj.scaleX ?? 1);\n const h = ct?.frameH ?? (obj.height ?? 0) * (obj.scaleY ?? 1);\n absoluteLeft = (absoluteLeft ?? 0) - w / 2;\n absoluteTop = (absoluteTop ?? 0) - h / 2;\n } else if (obj instanceof fabric.FabricImage && (obj.originX === 'center' || obj.originY === 'center')) {\n // Plain images with center origin (imageFit fill): convert center to top-left for store\n const w = (obj.width ?? 0) * (obj.scaleX ?? 1);\n const h = (obj.height ?? 0) * (obj.scaleY ?? 1);\n absoluteLeft = (absoluteLeft ?? 0) - w / 2;\n absoluteTop = (absoluteTop ?? 0) - h / 2;\n }\n\n // For images with crop mode (imageFit !== 'fill'), handle differently based on handle type\n // Corner handles: scale the image (normal behavior)\n // Side handles: resize crop window, keep image scale fixed\n // CRITICAL: For Textbox, finalWidth/finalHeight are already set above to preserve stored width\n const sourceElement = elementsRef.current.find(el => el.id === objId);\n const preserveCornerGeometry =\n sourceElement?.type === 'shape' &&\n (\n sourceElement.shapeType === 'circle' ||\n sourceElement.shapeType === 'rounded-rect' ||\n sourceElement.shapeType === 'triangle'\n );\n\n let finalWidth = intrinsicWidth;\n let finalHeight = intrinsicHeight;\n let finalScaleX = decomposed.scaleX;\n let finalScaleY = decomposed.scaleY;\n let finalAbsoluteMatrix = absoluteMatrix;\n \n // Handle crop groups (Canva-style: frame + image)\n // Both crop handles and simple scale use custom controls that update ct.frameW/frameH; same persist path\n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n const ct = (obj as any).__cropData;\n if (ct) {\n finalWidth = ct.frameW;\n finalHeight = ct.frameH;\n finalScaleX = 1;\n finalScaleY = 1;\n obj.set({ scaleX: 1, scaleY: 1 });\n updateCoverLayout(obj);\n (obj as any).__lastResizeHandle = null;\n fabricCanvas.setActiveObject(obj);\n }\n } else if (obj instanceof fabric.FabricImage) {\n // Smart elements: bake scale into width/height and re-render SVG at new dimensions\n if (sourceElement?.smartElementType && sourceElement.smartProps) {\n const bakedW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));\n const bakedH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));\n finalWidth = bakedW;\n finalHeight = bakedH;\n finalScaleX = 1;\n finalScaleY = 1;\n obj.set({ scaleX: 1, scaleY: 1 });\n // Re-render SVG at new dimensions\n const newSrc = renderSmartElementToDataUri(sourceElement.smartElementType, sourceElement.smartProps, bakedW, bakedH);\n if (newSrc) {\n const imgEl = new Image();\n imgEl.src = newSrc;\n imgEl.onload = () => {\n (obj as fabric.FabricImage).setElement(imgEl);\n (obj as fabric.FabricImage).set({ width: bakedW, height: bakedH, scaleX: 1, scaleY: 1 });\n obj.setCoords();\n obj.dirty = true;\n fabricCanvas.requestRenderAll();\n };\n // Also update src in store\n useEditorStore.getState().updateElement(objId, { src: newSrc }, { recordHistory: false, skipLayoutRecalc: true });\n }\n } else {\n finalWidth = intrinsicWidth;\n finalHeight = intrinsicHeight;\n }\n } else if (obj instanceof fabric.Line) {\n // Line: persist length as width, no vertical scale/skew so it stays horizontal (no slant)\n finalWidth = intrinsicWidth;\n finalHeight = 0;\n finalScaleX = 1;\n finalScaleY = 1;\n } else if (preserveCornerGeometry) {\n // Keep core shape geometry stable by baking transform into width/height (circle, rounded-rect, triangle)\n const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));\n const scaledH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));\n\n if (sourceElement?.shapeType === 'circle') {\n const diameter = Math.max(1, Math.min(scaledW, scaledH));\n finalWidth = diameter;\n finalHeight = diameter;\n } else {\n finalWidth = scaledW;\n finalHeight = scaledH;\n }\n\n finalScaleX = 1;\n finalScaleY = 1;\n obj.set({ scaleX: 1, scaleY: 1 });\n\n if (isActiveSelection && activeObj) {\n const selectionMatrix = activeObj.calcTransformMatrix();\n const objOwnMatrix = obj.calcOwnMatrix();\n const combined = fabric.util.multiplyTransformMatrices(selectionMatrix, objOwnMatrix);\n finalAbsoluteMatrix = combined as [number, number, number, number, number, number];\n } else {\n finalAbsoluteMatrix = obj.calcTransformMatrix() as [number, number, number, number, number, number];\n }\n } else {\n finalWidth = intrinsicWidth;\n finalHeight = intrinsicHeight;\n }\n\n // Convert absolute canvas position to store position (relative when inside a group)\n const state = useEditorStore.getState();\n const page = state.canvas.pages.find((p) => p.id === pageId);\n const pageChildrenForSave = page?.children ?? [];\n const storePos = absoluteToStorePosition(absoluteLeft, absoluteTop, objId, pageChildrenForSave);\n \n const isLineObj = obj instanceof fabric.Line;\n const isAutoShrinkText =\n sourceElement?.type === 'text' && sourceElement.overflowPolicy === 'auto-shrink';\n const autoShrinkStoredWidth = isAutoShrinkText ? sourceElement.width : undefined;\n const autoShrinkStoredHeight = isAutoShrinkText ? sourceElement.height : undefined;\n const elementUpdate: Partial<CanvasElement> = {\n left: storePos.left,\n top: storePos.top,\n // Auto-shrink: lock width, but let height shrink to actual rendered height for proper stack reflow.\n width: isAutoShrinkText ? (autoShrinkStoredWidth ?? finalWidth) : finalWidth,\n height: isLineObj\n ? 0\n : (isAutoShrinkText\n ? (typeof autoShrinkStoredHeight === 'number' ? Math.min(autoShrinkStoredHeight, finalHeight) : finalHeight)\n : finalHeight),\n angle: decomposed.angle,\n skewX: isLineObj ? 0 : decomposed.skewX,\n skewY: isLineObj ? 0 : decomposed.skewY,\n scaleX: finalScaleX,\n scaleY: finalScaleY,\n transformMatrix: finalAbsoluteMatrix,\n };\n \n if (obj instanceof fabric.Textbox) {\n elementUpdate.text = obj.text || '';\n }\n \n if (sourceElement && sourceElement.opacity !== undefined) {\n elementUpdate.opacity = sourceElement.opacity;\n }\n \n updateElement(objId, elementUpdate, { recordHistory: false, skipLayoutRecalc: true });\n\n obj.setCoords();\n }\n\n // If any modified element belongs to a stack group (with or without backgroundColor), reflow so siblings shift\n const pageChildrenForReflow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children ?? [];\n const stackGroupsToReflow = new Set<string>();\n for (const id of modifiedIdsThisRound) {\n const parent = findParentGroup(pageChildrenForReflow, id);\n if (parent && isGroup(parent) && (parent.layoutMode ?? 'absolute') === 'stack') {\n stackGroupsToReflow.add(parent.id);\n }\n }\n for (const gid of stackGroupsToReflow) {\n useEditorStore.getState().reflowStackGroupInPage(pageId, gid);\n }\n\n // After persisting an ActiveSelection (group or multiselect), bake the selection transform\n // onto each object so Fabric state matches the store and sync won't overwrite or fight.\n if (isActiveSelection && activeObj && activeObjects.length > 0) {\n const selectionMatrix = activeObj.calcTransformMatrix();\n for (const obj of activeObjects) {\n const objId = getObjectId(obj);\n if (!objId || objId === '__background__') continue;\n const objOwnMatrix = obj.calcOwnMatrix();\n const absoluteMatrix = fabric.util.multiplyTransformMatrices(selectionMatrix, objOwnMatrix);\n fabric.util.applyTransformToObject(obj, absoluteMatrix);\n obj.setCoords();\n }\n activeObj.set({ scaleX: 1, scaleY: 1 });\n activeObj.setCoords();\n fabricCanvas.requestRenderAll();\n }\n \n setTimeout(() => modifiedIdsThisRound.forEach(id => justModifiedIdsRef.current.delete(id)), 150);\n commitHistory();\n unlockEditsSoon();\n } catch (e) {\n unlockEditsSoon();\n }\n });\n\n // When selection is cleared, bake transforms into Fabric objects\n // The store was already updated during object:modified, so we only update Fabric objects here\n // BUT: Only do this if we actually had a transform, not just a click\n fabricCanvas.on('before:selection:cleared', (e) => {\n if (!isActiveRef.current) return;\n \n // Only apply transforms if we actually transformed (not just clicked)\n if (!didTransformRef.current) {\n return; // Skip transform baking if it was just a click\n }\n \n const deselected = e.deselected;\n if (!deselected || deselected.length === 0) return;\n \n // Get the selection that's being cleared\n const activeObj = fabricCanvas.getActiveObject();\n if (!(activeObj instanceof fabric.ActiveSelection)) return;\n \n const selectionMatrix = activeObj.calcTransformMatrix();\n \n for (const obj of deselected) {\n const objId = getObjectId(obj);\n if (!objId || objId === '__background__') continue;\n \n // Get the absolute transform matrix (same as object:modified)\n const objOwnMatrix = obj.calcOwnMatrix();\n const absoluteMatrix = fabric.util.multiplyTransformMatrices(selectionMatrix, objOwnMatrix);\n \n // Use Fabric's built-in utility to apply the matrix\n // This is the most reliable way as it handles all edge cases\n fabric.util.applyTransformToObject(obj, absoluteMatrix);\n obj.setCoords();\n }\n });\n \n // Double-click to edit text (or enter group for editing) - only in editor mode\n fabricCanvas.on('mouse:dblclick', (e) => {\n if (!isActiveRef.current || !allowEditing) return;\n let target: fabric.FabricObject | null = e.target;\n // If click wasn't on a Textbox (e.g. selection border), use active object when it's a single Textbox\n if (!target || !(target instanceof fabric.Textbox)) {\n const active = fabricCanvas.getActiveObject();\n if (active instanceof fabric.Textbox) target = active;\n else if (active instanceof fabric.ActiveSelection && active.getObjects().length === 1 && active.getObjects()[0] instanceof fabric.Textbox) target = active.getObjects()[0];\n }\n if (target && target instanceof fabric.Textbox) {\n const elementId = getObjectId(target);\n editingTextIdRef.current = elementId || null;\n target.enterEditing();\n target.selectAll();\n }\n });\n\n // Track when text editing starts (can also happen via keyboard). Bypass reflow debounce so layout is immediate.\n fabricCanvas.on('text:editing:entered', (e) => {\n const target = e.target;\n if (target && target instanceof fabric.Textbox) {\n const elementId = getObjectId(target);\n editingTextIdRef.current = elementId || null;\n setEditingText(true);\n }\n });\n\n // Save text content when editing is finished - only in editor mode\n // Note: We don't check isActiveRef here because text should always be saved\n // even if the user clicks to another page to finish editing\n fabricCanvas.on('text:editing:exited', (e) => {\n const target = e.target;\n if (!target || !(target instanceof fabric.Textbox)) return;\n \n const elementId = getObjectId(target);\n if (!elementId) return;\n \n // Clear the editing flag so store can use debounced reflow again\n editingTextIdRef.current = null;\n setEditingText(false);\n\n // Only update store in editor mode\n if (!allowEditing) return;\n \n // Re-initialize dimensions so Fabric has correct bounds\n target.initDimensions();\n target.setCoords();\n \n const newText = target.text || '';\n const state = useEditorStore.getState();\n const currentElement = state.getCurrentElements().find(el => el.id === elementId);\n const overflowPolicy = currentElement?.overflowPolicy || 'grow-and-push';\n \n // Use Fabric textbox bounds only - no manual measurement (store uses unscaled width/height like persistTextDimensionsAfterFontLoad)\n const fabricHeight = target.height ?? 0;\n const fabricWidth = target.width ?? 0;\n \n const { updateElement, commitHistory } = state;\n \n if (overflowPolicy === 'auto-shrink' && currentElement) {\n // Auto-shrink: keep width fixed and persist the post-shrink rendered height\n // so vertical stack siblings reflow upward correctly.\n let nextHeight: number | undefined;\n try {\n const measuredObj = createText({ ...currentElement, text: newText } as CanvasElement);\n if (measuredObj instanceof fabric.Textbox) {\n const measuredHeight = measuredObj.height ?? 0;\n if (measuredHeight > 0) {\n if (typeof currentElement.height === 'number') {\n // Never grow height for auto-shrink; only keep or shrink.\n nextHeight = Math.min(currentElement.height, measuredHeight);\n } else {\n nextHeight = measuredHeight;\n }\n }\n }\n } catch {\n // Fallback to current Fabric target height if measurement fails.\n if (\n fabricHeight > 0 &&\n typeof currentElement.height === 'number' &&\n fabricHeight < currentElement.height - 0.5\n ) {\n nextHeight = fabricHeight;\n }\n }\n updateElement(elementId, {\n text: newText,\n ...(typeof nextHeight === 'number' && nextHeight > 0 && { height: nextHeight }),\n });\n } else {\n updateElement(elementId, {\n text: newText,\n ...(fabricHeight > 0 && { height: fabricHeight }),\n ...(fabricWidth > 0 && { width: fabricWidth }),\n });\n }\n \n commitHistory();\n });\n\n return () => {\n setReady(false);\n unregisterFabricCanvas(pageId);\n // Clean up font loading listener\n if ((fabricCanvas as any).__fontCleanup) {\n (fabricCanvas as any).__fontCleanup();\n }\n fabricCanvas.dispose();\n fabricRef.current = null;\n };\n }, [pageId]);\n\n // === SYNC CANVAS SIZE + BACKGROUND ===\n useEffect(() => {\n const fc = fabricRef.current;\n if (!fc) return;\n\n fc.setDimensions({ width: canvasWidth, height: canvasHeight });\n\n fc.clipPath = new fabric.Rect({\n left: 0,\n top: 0,\n width: canvasWidth,\n height: canvasHeight,\n absolutePositioned: true,\n });\n\n fc.requestRenderAll();\n }, [canvasWidth, canvasHeight]);\n\n // === APPLY VIEWPORT ZOOM FOR CRISP RENDERING ===\n // This handles zoom changes AFTER initialization (init already applies initial zoom)\n useEffect(() => {\n const fc = fabricRef.current;\n if (!fc) return;\n \n const zoom = workspaceZoom || 1;\n const scaledWidth = canvasWidth * zoom;\n const scaledHeight = canvasHeight * zoom;\n \n // Update canvas dimensions to match zoomed size\n fc.setDimensions({ width: scaledWidth, height: scaledHeight });\n \n // Apply zoom via viewport transform - this gives true vector zoom\n fc.setViewportTransform([zoom, 0, 0, zoom, 0, 0]);\n \n // Keep control/handle size consistent at any zoom: inverse scale so handles stay same on-screen size\n fc.getObjects().forEach((obj) => applyControlSizeForZoom(fc, obj));\n const active = fc.getActiveObject();\n if (active && active instanceof fabric.ActiveSelection) applyControlSizeForZoom(fc, active);\n fc.requestRenderAll();\n }, [workspaceZoom, canvasWidth, canvasHeight]);\n\n // === SYNC ELEMENTS TO FABRIC (keeping hidden elements on canvas but invisible) ===\n // In preview mode: run sync immediately (don't wait for fonts) and use elements prop, not store\n useEffect(() => {\n const fc = fabricRef.current;\n if (!fc) return;\n if (!ready && !isPreviewMode) {\n hasClearedCachesBeforeFirstSyncRef.current = false;\n return;\n }\n \n // Check if this is a visibility-only update batch BEFORE creating sync function\n // This helps detect group visibility toggles\n const currentFabricObjectsMap = new Map<string, fabric.FabricObject>();\n fc.getObjects().forEach((obj: fabric.FabricObject) => {\n const id = getObjId(obj);\n if (id) currentFabricObjectsMap.set(id, obj);\n });\n \n let visibilityOnlyCount = 0;\n let totalVisibilityChanges = 0;\n let hasAnyVisibilityChange = false;\n \n for (const el of elements) {\n const prevVisible = previousVisibilityRef.current.get(el.id) ?? true;\n const currVisible = el.visible !== false;\n const visibilityChanged = prevVisible !== currVisible;\n \n if (visibilityChanged) {\n hasAnyVisibilityChange = true;\n totalVisibilityChanges++;\n const existingObj = currentFabricObjectsMap.get(el.id);\n if (existingObj) {\n const positionChanged = \n Math.abs((existingObj.left ?? 0) - (el.left ?? 0)) > 0.1 ||\n Math.abs((existingObj.top ?? 0) - (el.top ?? 0)) > 0.1;\n \n if (!positionChanged) {\n visibilityOnlyCount++;\n }\n } else {\n // If object doesn't exist yet, treat as visibility-only (new elements shouldn't cause position issues)\n visibilityOnlyCount++;\n }\n }\n }\n \n // If we have visibility changes and most/all are position-only, it's likely a group visibility toggle\n // Set flag to skip updateFabricObject for all elements to prevent position recalculation\n // This is especially important when groups with subgroups are selected\n // Use a threshold: if 80%+ of visibility changes are position-only, treat as visibility batch\n // OR if we have many visibility changes (5+) and most are position-only, also treat as batch\n const visibilityBatchThreshold = hasAnyVisibilityChange && (\n (totalVisibilityChanges > 0 && visibilityOnlyCount / totalVisibilityChanges >= 0.8) ||\n (totalVisibilityChanges >= 5 && visibilityOnlyCount / totalVisibilityChanges >= 0.7)\n );\n \n if (visibilityBatchThreshold) {\n visibilityUpdateInProgressRef.current = true;\n // Reset flag after sync completes (use longer timeout for groups with many children)\n setTimeout(() => {\n visibilityUpdateInProgressRef.current = false;\n }, 200);\n } else if (!hasAnyVisibilityChange) {\n // Only reset flag if there are no visibility changes at all\n // This prevents resetting during a visibility batch\n visibilityUpdateInProgressRef.current = false;\n }\n \n // CRITICAL: Always store the sync function FIRST (before any early returns)\n // When run deferred (after unlock), use latest store so we don't overwrite with stale closure\n doSyncRef.current = () => {\n // Recalculate shouldSkipUpdates inside the function (refs may have changed)\n const shouldSkipUpdates = syncLockedRef.current || editLockRef.current;\n const state = useEditorStore.getState();\n // Use this page's elements (prop): in multi-page editor each PageCanvas gets its own elements from FabricCanvas; getCurrentElements() is the selected page only and would clear other pages when adding a new page\n const elementsToSync = elements;\n elementsRef.current = elementsToSync;\n // In editor use store for this pageId so group backgrounds and z-order see latest; in preview use prop\n const pageTree = (isPreviewMode && pageChildren?.length) ? (pageChildren ?? []) : (state.canvas.pages.find((p) => p.id === pageId)?.children ?? []);\n // selectedIds from store: skip applying position to selected elements even when SYNC runs before SELECTION_SYNC (no Fabric active object yet)\n const selectedIdsFromStore = new Set(state.canvas?.selectedIds ?? []);\n \n isRebuildingRef.current = true;\n \n // Keep ALL elements on canvas (both visible and hidden)\n const allElementIds = new Set(elementsToSync.map(el => el.id));\n\n // Section groups (top-level groups with backgroundColor) are rendered as fabric.Group so bg moves with the group\n const sectionGroups = (pageTree as CanvasNode[]).filter(\n (n): n is GroupNode => isGroup(n) && !!(n as GroupNode).backgroundColor && (n as GroupNode).backgroundColor !== 'transparent'\n );\n const sectionDescendantIds = new Set<string>();\n const sectionGroupIds = new Set<string>();\n for (const g of sectionGroups) {\n sectionGroupIds.add(g.id);\n getAllElementIds(g.children ?? []).forEach((id) => sectionDescendantIds.add(id));\n }\n const validTopLevelIds = new Set<string>(sectionGroupIds);\n allElementIds.forEach((id) => {\n if (!sectionDescendantIds.has(id)) validTopLevelIds.add(id);\n });\n\n // Helper: find object by id in canvas (top-level only; nested search done during group build)\n const getObjectById = (id: string): fabric.FabricObject | null =>\n fc.getObjects().find((o) => getObjectId(o) === id) ?? null;\n \n // CRITICAL: Preserve active object during sync - don't discard unless absolutely necessary\n // This prevents deselection during drag and after transforms\n const activeBeforeSync = fc.getActiveObject();\n const isTransforming = !!(fc as any)._currentTransform;\n \n // When sync was triggered by a panel update (layout/display/property change), discard selection\n // so position updates apply to loose objects (absolute coords). Otherwise setting left/top on\n // objects inside ActiveSelection uses group-relative coords and causes shift. SELECTION_SYNC\n // will re-select after this effect.\n // CRITICAL: Never discard when the active object is the Textbox being edited - that would exit editing.\n if (syncTriggeredByPanelRef.current && !shouldSkipUpdates && !isTransforming) {\n const activeObj = fc.getActiveObject();\n const activeObjId = activeObj ? getObjectId(activeObj) : null;\n const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;\n if (activeObj && !((activeObj as any)._ct?.isCropGroup || (activeObj as any).__cropGroup) && !isTextBeingEdited) {\n fc.discardActiveObject();\n }\n }\n\n // Get current fabric objects\n const currentFabricObjects = new Map<string, fabric.FabricObject>();\n \n fc.getObjects().forEach((obj) => {\n const id = getObjectId(obj);\n if (id && id !== '__background__') {\n currentFabricObjects.set(id, obj);\n }\n });\n \n // Restore active object after sync if it still exists (skip when panel-driven; SELECTION_SYNC will re-select)\n // CRITICAL: Never call setActiveObject when the active object is the Textbox currently being edited - it steals focus from the hidden textarea and exits editing.\n const skipRestoreSelection = syncTriggeredByPanelRef.current;\n const activeStillOnCanvas = activeBeforeSync && fc.getObjects().includes(activeBeforeSync);\n const activeId = activeBeforeSync ? getObjectId(activeBeforeSync) : null;\n const isActiveTextBeingEdited = activeId && editingTextIdRef.current === activeId && activeBeforeSync instanceof fabric.Textbox;\n if (!skipRestoreSelection && activeBeforeSync && activeStillOnCanvas && !isActiveTextBeingEdited) {\n const isCropGroup = (activeBeforeSync as any)._ct?.isCropGroup || (activeBeforeSync as any).__cropGroup;\n const isSectionGroup = activeId && sectionGroupIds.has(activeId);\n if ((isCropGroup || !shouldSkipUpdates) && !isSectionGroup) {\n fc.setActiveObject(activeBeforeSync);\n }\n }\n\n // Remove section groups (we recreate them below so bg is part of the group)\n for (const id of sectionGroupIds) {\n const obj = fc.getObjects().find((o) => getObjectId(o) === id);\n if (obj) fc.remove(obj);\n }\n // Remove top-level objects that are no longer valid (not in validTopLevelIds)\n [...fc.getObjects()].forEach((obj) => {\n const id = getObjectId(obj);\n if (id && id !== '__background__' && !validTopLevelIds.has(id)) {\n fc.remove(obj);\n }\n });\n\n // Create section groups (fabric.Group with native backgroundColor). Use center origin so Fabric's\n // _renderBackground (fillRect -dim/2, -dim/2, dim, dim) and child layout match; we convert to/from store (top-left) on persist.\n for (const G of sectionGroups) {\n const descElements = flattenChildren(G.children ?? []);\n // Compute group visual bounds from children's absolute positions and scaled dimensions (so scaled children don't shift)\n let gw: number;\n let gh: number;\n let centerX: number;\n let centerY: number;\n let Gabs: { left: number; top: number; width: number; height: number };\n const childAbsAndBounds: Array<{ el: CanvasElement; elAbs: { left: number; top: number }; bounds: { width: number; height: number }; scaleX: number; scaleY: number }> = [];\n if (descElements.length === 0) {\n const fallback = getAbsoluteBounds(G, pageTree);\n gw = Math.max(1, fallback.width);\n gh = Math.max(1, fallback.height);\n centerX = fallback.left + gw / 2;\n centerY = fallback.top + gh / 2;\n Gabs = { left: fallback.left, top: fallback.top, width: gw, height: gh };\n } else {\n let gMinX = Infinity, gMinY = Infinity, gMaxX = -Infinity, gMaxY = -Infinity;\n for (const el of descElements) {\n const node = findNodeById(pageTree, el.id);\n const bounds = node && pageTree.length ? getNodeBounds(node, pageTree, pageBoundsOptions) : { width: el.width ?? 100, height: el.height ?? 50 };\n const w = Math.max(1, Number(bounds.width) || 100);\n const h = Math.max(1, Number(bounds.height) || 50);\n const sx = el.scaleX ?? 1;\n const sy = el.scaleY ?? 1;\n const elAbs = node ? getAbsoluteBounds(node, pageTree) : { left: el.left ?? 0, top: el.top ?? 0, width: w, height: h };\n childAbsAndBounds.push({ el, elAbs, bounds: { width: w, height: h }, scaleX: sx, scaleY: sy });\n const visW = w * sx;\n const visH = h * sy;\n gMinX = Math.min(gMinX, elAbs.left);\n gMinY = Math.min(gMinY, elAbs.top);\n gMaxX = Math.max(gMaxX, elAbs.left + visW);\n gMaxY = Math.max(gMaxY, elAbs.top + visH);\n }\n gw = Math.max(1, gMaxX - gMinX);\n gh = Math.max(1, gMaxY - gMinY);\n centerX = gMinX + gw / 2;\n centerY = gMinY + gh / 2;\n Gabs = { left: gMinX, top: gMinY, width: gw, height: gh };\n }\n const children: fabric.FabricObject[] = [];\n for (const { el, elAbs, bounds, scaleX, scaleY } of childAbsAndBounds) {\n const elForCreate = { ...el, width: bounds.width, height: bounds.height };\n const childObj = createFabricObjectForGroupMember(elForCreate);\n if (childObj) {\n const relLeft = elAbs.left - Gabs.left;\n const relTop = elAbs.top - Gabs.top;\n childObj.set({\n left: relLeft - gw / 2,\n top: relTop - gh / 2,\n visible: el.visible !== false,\n scaleX,\n scaleY,\n });\n setObjectData(childObj, el.id);\n children.push(childObj);\n }\n }\n const groupOpts: Partial<fabric.GroupProps> & { backgroundColor?: string } = {\n left: centerX,\n top: centerY,\n originX: 'center',\n originY: 'center',\n width: gw,\n height: gh,\n backgroundColor: G.backgroundColor || '#f0f0f0',\n subTargetCheck: true,\n selectable: allowSelection,\n evented: allowSelection,\n hasControls: allowEditing,\n hasBorders: allowEditing,\n };\n const sectionGroup = new fabric.Group(children, groupOpts as any);\n setObjectData(sectionGroup, G.id);\n (sectionGroup as any).__docuforgeSectionGroup = true;\n // Custom render: draw section bg with Fabric renderer (no separate rect). Center origin so fillRect matches group bounds (same as Object._renderBackground).\n const bgColor = G.backgroundColor || '#f0f0f0';\n sectionGroup._renderBackground = function (this: fabric.Group, ctx: CanvasRenderingContext2D) {\n if (!bgColor || bgColor === 'transparent') return;\n const dim = (this as any)._getNonTransformedDimensions?.() ?? { x: this.width ?? gw, y: this.height ?? gh };\n const w = dim.x ?? gw;\n const h = dim.y ?? gh;\n ctx.save();\n ctx.fillStyle = bgColor;\n ctx.fillRect(-w / 2, -h / 2, w, h);\n ctx.restore();\n };\n fc.add(sectionGroup);\n }\n\n // Add or update ALL elements (visible and hidden) — skip elements that live inside section groups\n for (const element of elementsToSync) {\n if (sectionDescendantIds.has(element.id)) continue;\n let existingObj = currentFabricObjects.get(element.id);\n \n // Determine if this element should be visible on canvas\n const isHidden = !element.visible;\n \n if (existingObj) {\n const isBeingTransformed = transformingIdsRef.current.has(element.id);\n const wasJustModified = justModifiedIdsRef.current.has(element.id);\n const isBeingTextEdited = editingTextIdRef.current === element.id;\n \n // For images, check if URL changed and reload if needed\n if (element.type === 'image') {\n const currentImageUrl = element.src || element.imageUrl || '';\n const storedImageUrl = (existingObj as any).__imageSrc;\n \n // Normalize empty values\n const currentUrlNormalized = currentImageUrl.trim();\n const storedUrlNormalized = storedImageUrl ? String(storedImageUrl).trim() : '';\n \n // If URL or SVG recolor map changed, reload the image\n const svgColorMapStr = element.svgColorMap ? JSON.stringify(element.svgColorMap) : '';\n const storedColorMapStr = (existingObj as any).__svgColorMap || '';\n const colorMapChanged = svgColorMapStr !== storedColorMapStr;\n const sourceUrlChanged = currentUrlNormalized !== storedUrlNormalized;\n const needsReload = sourceUrlChanged || colorMapChanged;\n const hasUrl = currentUrlNormalized !== '';\n const isCropGroup = existingObj instanceof fabric.Group && (existingObj as any).__cropGroup;\n const isPlaceholder = (existingObj instanceof fabric.Group && !isCropGroup) || !(existingObj instanceof fabric.FabricImage);\n \n // If URL was removed (empty string), replace with placeholder\n // Check if we had a URL before (storedImageUrl exists and is not empty) and now we don't\n const hadUrlBefore = storedImageUrl && String(storedImageUrl).trim() !== '';\n if (!hasUrl && hadUrlBefore) {\n // For crop groups, we need to replace the entire group with a placeholder\n // For regular images, replace the image object with a placeholder\n const placeholder = isCropGroup \n ? createImagePlaceholderForGroup(element)\n : createImagePlaceholder(element);\n setObjectData(placeholder, element.id);\n (placeholder as any).__imageSrc = '';\n \n const idx = fc.getObjects().indexOf(existingObj);\n fc.remove(existingObj);\n if (idx >= 0) {\n fc.insertAt(idx, placeholder);\n } else {\n fc.add(placeholder);\n }\n fc.requestRenderAll();\n continue;\n }\n \n // In preview, we may need a crop group (clipped) but currently have a plain image — replace it\n const imageFitForReplace = element.imageFit || (element as any).style?.imageFit || 'cover';\n const clipShapeForReplace = element.clipShape ?? (element as any).style?.imageFrameShape ?? (isPreviewMode ? 'rectangle' : 'none');\n const needCropGroupForElement = imageFitForReplace !== 'fill' || (clipShapeForReplace && clipShapeForReplace !== 'none');\n const plainImageNeedsCropGroup = hasUrl && !isCropGroup && existingObj instanceof fabric.FabricImage && needCropGroupForElement;\n\n if (hasUrl && (needsReload || isPlaceholder || plainImageNeedsCropGroup)) {\n // CRITICAL: Only reload when not transforming (but always reload for source URL changes like background removal)\n if (needsReload && !isBeingTransformed && (!wasJustModified || sourceUrlChanged)) {\n // DO NOT update __imageSrc or __svgColorMap before loadImageAsync.\n // Crop groups use those markers to detect whether a reload is needed,\n // so mutating them here makes source changes look like no-ops.\n loadImageAsync(element, existingObj, fc);\n } else if (plainImageNeedsCropGroup) {\n // Preview (or element now has clip): replace plain image with crop group so image is clipped\n loadImageAsync(element, existingObj, fc);\n } else if (!needsReload && isCropGroup) {\n // URL didn't change - this is just a crop/resize/zoom/clipShape update\n // Update crop group properties in place (don't reload image)\n const ct = (existingObj as any).__cropData;\n if (ct) {\n // Store is source of truth: use element width/height (with scale) so save/reload shows same size (especially when crop handles are off)\n const resolvedCrop = pageTree.length > 0 ? getNodeBounds(element, pageTree, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 200, height: typeof element.height === 'number' ? element.height : 50 };\n const hasExplicitCropSize =\n typeof element.width === 'number' && Number.isFinite(element.width) && element.width > 0 &&\n typeof element.height === 'number' && Number.isFinite(element.height) && element.height > 0;\n const minCropVisible = hasExplicitCropSize ? 1 : 20;\n const fallbackCropW = hasExplicitCropSize ? Number(element.width) : 200;\n const fallbackCropH = hasExplicitCropSize ? Number(element.height) : 50;\n const elementWidth = Math.max(minCropVisible, Number(resolvedCrop.width) || fallbackCropW) * (element.scaleX ?? 1);\n const elementHeight = Math.max(minCropVisible, Number(resolvedCrop.height) || fallbackCropH) * (element.scaleY ?? 1);\n // Always push store dimensions into crop frame so saved state matches after reload\n ct.frameW = elementWidth;\n ct.frameH = elementHeight;\n // Check if clipShape changed - if so, we need to update clipPath\n const clipShape = element.clipShape || 'none';\n const newShape = clipShape === 'circle' ? 'circle' : clipShape === 'rounded' ? 'roundRect' : 'rect';\n const shapeChanged = ct.shape !== newShape;\n \n // Update shape and rx if changed\n if (shapeChanged || element.clipShape !== undefined) {\n ct.shape = newShape;\n // CRITICAL: Only use rx if clipShape is 'rounded', otherwise set to 0\n // rx is stored as ratio (0-0.5), convert old pixel values if needed\n let rxRatio = 0;\n if (clipShape === 'rounded') {\n const elementRx = element.rx || 0.1; // Default to 10% if not set\n // If rx > 0.5, it's likely an old pixel value, convert to ratio\n if (elementRx > 0.5) {\n const minDim = Math.min(elementWidth, elementHeight);\n rxRatio = Math.min(elementRx / minDim, 0.5); // Cap at 50%\n } else {\n rxRatio = elementRx;\n }\n }\n ct.rx = rxRatio;\n }\n \n // Update group position/angle/opacity if changed (use absolute when pageChildren provided)\n const cropPos = pageChildren ? (() => {\n const node = findNodeById(pageChildren, element.id);\n return node ? getAbsoluteBounds(node, pageChildren) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n // Crop groups use center origin — convert top-left to center\n const cropCenterX = cropPos.left + (ct.frameW ?? 0) / 2;\n const cropCenterY = cropPos.top + (ct.frameH ?? 0) / 2;\n if (element.left !== undefined) existingObj.set({ left: cropCenterX });\n if (element.top !== undefined) existingObj.set({ top: cropCenterY });\n if (element.angle !== undefined) existingObj.set({ angle: element.angle });\n // CRITICAL: Apply flips on crop-group itself (this is the rendered object on canvas)\n existingObj.set({\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n // CRITICAL: Always update opacity (even if 1.0) to ensure it's applied correctly\n // Clamp opacity to valid range [0, 1]\n const clampedOpacity = element.opacity !== undefined \n ? Math.max(0, Math.min(1, element.opacity)) \n : 1;\n existingObj.set({ opacity: clampedOpacity });\n \n // Update group dimensions to match frame\n existingObj.set({ width: ct.frameW, height: ct.frameH });\n \n // Use transparent so PNG/SVG transparent pixels show through (no white fill)\n existingObj.set({ backgroundColor: 'transparent' });\n \n // CRITICAL: If clipShape changed, recreate clipPath\n if (shapeChanged) {\n const needsNewClipPath = \n !existingObj.clipPath ||\n (newShape === 'circle' && !(existingObj.clipPath instanceof fabric.Ellipse)) ||\n (newShape !== 'circle' && !(existingObj.clipPath instanceof fabric.Rect));\n \n if (needsNewClipPath) {\n const rx = ct.rx || 0;\n const newClip = newShape === 'circle'\n ? new fabric.Ellipse({\n rx: ct.frameW / 2,\n ry: ct.frameH / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n })\n : new fabric.Rect({\n width: ct.frameW,\n height: ct.frameH,\n rx: rx,\n ry: rx,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n (newClip as any).absolutePositioned = false;\n (newClip as any).excludeFromExport = true;\n existingObj.clipPath = newClip;\n }\n }\n \n // Reapply layout (this handles zoom/pan and updates clipPath geometry)\n updateCoverLayout(existingObj);\n \n // CRITICAL: In preview mode, disable controls and borders for crop groups\n // In editor mode use element.useCropHandles to decide crop vs simple-scale handles\n if (allowEditing) {\n if (element.useCropHandles) {\n installCanvaMaskControls(existingObj);\n } else {\n setSimpleScaleControls(existingObj);\n }\n } else {\n // Preview mode - disable all controls and borders\n existingObj.set({\n hasControls: false,\n hasBorders: false,\n selectable: false,\n evented: isPreviewMode && dynamicFieldIds.includes(element.id), // Only allow click if dynamic field\n });\n }\n \n // CRITICAL: Force immediate render to show clip shape and opacity changes\n existingObj.setCoords();\n (existingObj as any).dirty = true;\n if (existingObj.clipPath) {\n (existingObj.clipPath as any).dirty = true;\n }\n fc.requestRenderAll();\n }\n continue; // Skip to next element, don't exit the entire sync function\n }\n } else if (!hasUrl && !isPlaceholder) {\n // If URL was removed, replace image with placeholder\n // Keep explicit tiny dimensions (e.g. PDF bullets/borders) instead of forcing 20px.\n const resolvedSizeImg = pageChildren?.length ? getNodeBounds(element, pageChildren, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 200, height: typeof element.height === 'number' ? element.height : 50 };\n const hasExplicitSize =\n typeof element.width === 'number' && Number.isFinite(element.width) && element.width > 0 &&\n typeof element.height === 'number' && Number.isFinite(element.height) && element.height > 0;\n const minVisiblePlaceholder = hasExplicitSize ? 1 : 20;\n const elementForPlaceholder = {\n ...element,\n width: Math.max(minVisiblePlaceholder, Number(resolvedSizeImg.width) || 200),\n height: Math.max(minVisiblePlaceholder, Number(resolvedSizeImg.height) || 50),\n };\n const placeholder = createImagePlaceholder(elementForPlaceholder);\n setObjectData(placeholder, element.id);\n \n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n placeholder.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n const idx = fc.getObjects().indexOf(existingObj);\n fc.remove(existingObj);\n if (idx >= 0) {\n fc.insertAt(idx, placeholder);\n } else {\n fc.add(placeholder);\n }\n (placeholder as any).__imageSrc = undefined;\n } else if (!isBeingTransformed && !isBeingTextEdited && !shouldSkipUpdates) {\n // CRITICAL: Skip updateFabricObject for crop groups - they handle their own updates\n if (existingObj instanceof fabric.Group && (existingObj as any).__cropGroup) {\n // Crop groups are updated in place above - don't call updateFabricObject\n // But still apply flip and opacity which are safe to set on the group\n existingObj.set({\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n });\n existingObj.setCoords();\n fc.requestRenderAll();\n continue; // Skip to next element, don't exit the loop\n }\n \n // CRITICAL: For Textbox, skip updateFabricObject if it was just modified on canvas — unless sync was triggered by panel (width/height from properties panel must apply)\n if (existingObj instanceof fabric.Textbox && wasJustModified && !syncTriggeredByPanelRef.current) {\n justModifiedIdsRef.current.delete(element.id);\n continue;\n }\n \n // Also check if currently transforming (double safety)\n if (existingObj instanceof fabric.Textbox) {\n const fc = fabricRef.current;\n const isCurrentlyTransforming = fc && (fc as any)._currentTransform && \n (fc as any)._currentTransform.target === existingObj;\n \n if (isCurrentlyTransforming) {\n // Textbox is being resized - skip update completely\n continue;\n }\n }\n \n // Check if only visibility changed for images (preserve position)\n const previousVisible = previousVisibilityRef.current.get(element.id) ?? true;\n const currentVisible = element.visible !== false;\n const visibilityChanged = previousVisible !== currentVisible;\n \n // Check if position properties changed (compare with store's absolute when pageChildren provided)\n const storePosForImg = pageChildren ? (() => {\n const node = findNodeById(pageChildren, element.id);\n return node ? getAbsoluteBounds(node, pageChildren) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n const positionChanged = \n Math.abs((existingObj.left ?? 0) - storePosForImg.left) > 0.1 ||\n Math.abs((existingObj.top ?? 0) - storePosForImg.top) > 0.1;\n \n // If visibility changed and position didn't, OR if we're in a visibility batch update,\n // skip updateFabricObject to preserve position\n if ((visibilityChanged && !positionChanged) || visibilityUpdateInProgressRef.current) {\n // Only visibility changed - skip updateFabricObject to preserve position\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n // Don't call setCoords() to avoid position recalculation\n previousVisibilityRef.current.set(element.id, currentVisible);\n } else {\n // Normal update for images (position, scale, etc.)\n // BUT: if visibility batch is in progress, still skip updateFabricObject to preserve position\n if (!visibilityUpdateInProgressRef.current) {\n updateFabricObject(existingObj, element, wasJustModified);\n }\n \n // Set visibility and interaction properties based on mode\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n // Only call setCoords if not in visibility batch (to preserve position)\n if (!visibilityUpdateInProgressRef.current) {\n existingObj.setCoords();\n }\n previousVisibilityRef.current.set(element.id, currentVisible);\n }\n }\n } else {\n // Skip updating if being actively transformed, was just modified, or is being text-edited\n // (to prevent sync from interrupting user interaction) - only in editor mode\n // Also skip if sync is locked (but still allow adding new elements)\n if (!isBeingTransformed && !isBeingTextEdited && !shouldSkipUpdates) {\n // Check if only visibility changed (preserve position)\n const previousVisible = previousVisibilityRef.current.get(element.id) ?? true;\n const currentVisible = element.visible !== false;\n const visibilityChanged = previousVisible !== currentVisible;\n \n // Check if position properties changed (compare Fabric absolute with store's absolute when pageChildren provided)\n const fabricLeft = existingObj.left ?? 0;\n const fabricTop = existingObj.top ?? 0;\n const storePos = pageChildren ? (() => {\n const node = findNodeById(pageChildren, element.id);\n return node ? getAbsoluteBounds(node, pageChildren) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n const storeLeft = storePos.left;\n const storeTop = storePos.top;\n const deltaX = Math.abs(fabricLeft - storeLeft);\n const deltaY = Math.abs(fabricTop - storeTop);\n let positionChanged = deltaX > 0.1 || deltaY > 0.1;\n \n // CRITICAL: Don't overwrite Fabric position from store when this object is selected (Fabric active or store selectedIds).\n // SYNC can run before SELECTION_SYNC, so fc.getActiveObject() may be null; use store selectedIds so we still skip (flat group shift fix).\n const activeObj = fc.getActiveObject();\n const isInActiveSelection = activeObj &&\n (activeObj instanceof fabric.ActiveSelection\n ? activeObj.getObjects().includes(existingObj)\n : activeObj === existingObj);\n const isInSelectedIds = selectedIdsFromStore.has(element.id);\n const isSelected = isInActiveSelection || isInSelectedIds;\n // Only trust Fabric position for selected objects when user just dragged/transformed\n // (so layout recalc from properties panel still applies in real time)\n if (positionChanged && isSelected && (wasJustModified || isBeingTransformed)) {\n positionChanged = false; // Trust Fabric position during/after user drag\n }\n \n // Check if other properties changed (excluding position) - use resolved size when element has auto/inherit\n const resolvedSizeForCompare = pageChildren?.length ? getNodeBounds(element, pageChildren, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 0, height: typeof element.height === 'number' ? element.height : 0 };\n const fabricText = (existingObj as any).text ?? '';\n const storeText = (element as any).text ?? '';\n const otherPropsChanged =\n Math.abs((existingObj.width ?? 0) - resolvedSizeForCompare.width) > 0.1 ||\n Math.abs((existingObj.height ?? 0) - resolvedSizeForCompare.height) > 0.1 ||\n Math.abs((existingObj.angle ?? 0) - (element.angle ?? 0)) > 0.1 ||\n Math.abs((existingObj.scaleX ?? 1) - (element.scaleX ?? 1)) > 0.01 ||\n Math.abs((existingObj.scaleY ?? 1) - (element.scaleY ?? 1)) > 0.01 ||\n (existingObj.flipX ?? false) !== (element.flipX ?? false) ||\n (existingObj.flipY ?? false) !== (element.flipY ?? false) ||\n fabricText !== storeText ||\n (existingObj.fill as string) !== (element.fill ?? '') ||\n (existingObj.stroke as string) !== (element.stroke ?? '') ||\n Math.abs((existingObj.strokeWidth ?? 0) - (element.strokeWidth ?? 0)) > 0.01 ||\n Math.abs((existingObj.opacity ?? 1) - (element.opacity ?? 1)) > 0.01 ||\n ((existingObj as any).fontSize ?? 0) !== (element.fontSize ?? 0) ||\n ((existingObj as any).fontFamily ?? '') !== (element.fontFamily ?? '') ||\n // CRITICAL: Detect gradient fill/stroke changes — serialise to JSON for deep comparison\n JSON.stringify((element as any).fillGradient || null) !== ((existingObj as any).__lastFillGradientJson ?? 'null') ||\n JSON.stringify((element as any).strokeGradient || null) !== ((existingObj as any).__lastStrokeGradientJson ?? 'null');\n \n // When sync was triggered by panel, always apply store → Fabric so every element (incl. 2nd text) syncs\n const forceApplyFromPanel = syncTriggeredByPanelRef.current;\n \n // CRITICAL: If position didn't change AND no other significant properties changed,\n // preserve current Fabric object position completely (unless panel-driven)\n const noPropsOrPositionChanged = !positionChanged && !otherPropsChanged;\n if ((noPropsOrPositionChanged && !forceApplyFromPanel) || visibilityUpdateInProgressRef.current) {\n // Only visibility changed, not position - skip updateFabricObject to preserve position\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n // Don't call setCoords() to avoid position recalculation\n // Update visibility tracking\n previousVisibilityRef.current.set(element.id, currentVisible);\n } else {\n // Normal update for other property changes\n // When isSelected and store position differs and user just dragged, apply all props but preserve Fabric position\n const skipPositionBecauseSelection = isSelected && (deltaX > 0.1 || deltaY > 0.1) && (wasJustModified || isBeingTransformed);\n const anyChange = positionChanged || otherPropsChanged || forceApplyFromPanel;\n if (!visibilityUpdateInProgressRef.current) {\n if (anyChange && !skipPositionBecauseSelection) {\n updateFabricObject(existingObj, element, wasJustModified);\n if (wasJustModified) justModifiedIdsRef.current.delete(element.id);\n } else if (skipPositionBecauseSelection && anyChange) {\n // In selection and store position differs: apply all panel props but preserve Fabric position (user's drag)\n const savedLeft = existingObj.left;\n const savedTop = existingObj.top;\n updateFabricObject(existingObj, element, true);\n existingObj.set({ left: savedLeft, top: savedTop });\n existingObj.setCoords();\n if (wasJustModified) justModifiedIdsRef.current.delete(element.id);\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n previousVisibilityRef.current.set(element.id, currentVisible);\n } else {\n // Position and other props didn't change - just update non-position properties\n // This prevents shift when only selection or UI state changes\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n // Preserve current position - don't update left/top\n });\n // Don't call setCoords() to preserve position\n }\n }\n \n // Set visibility and interaction properties based on mode\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n existingObj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n // Only call setCoords if position actually changed (to avoid unnecessary recalculation)\n if (!visibilityUpdateInProgressRef.current && positionChanged) {\n existingObj.setCoords();\n }\n // Update visibility tracking\n previousVisibilityRef.current.set(element.id, currentVisible);\n }\n }\n }\n // Only clear just-modified when we actually applied an update (above); never clear when we skipped (preserves skip on next sync)\n } else {\n // Add new element\n if (element.type === 'image') {\n // Resolve width/height for auto/inherit so placeholder and loadImageAsync have correct dimensions.\n // Preserve explicit tiny sizes for precise imported vectors (borders, bullets, dividers).\n const resolvedSizeImg = pageTree.length > 0 ? getNodeBounds(element, pageTree, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 200, height: typeof element.height === 'number' ? element.height : 50 };\n const hasExplicitSize =\n typeof element.width === 'number' && Number.isFinite(element.width) && element.width > 0 &&\n typeof element.height === 'number' && Number.isFinite(element.height) && element.height > 0;\n const minVisibleImage = hasExplicitSize ? 1 : 20;\n const elementForImage = {\n ...element,\n width: Math.max(minVisibleImage, Number(resolvedSizeImg.width) || 200),\n height: Math.max(minVisibleImage, Number(resolvedSizeImg.height) || 50),\n };\n const placeholder = createImagePlaceholder(elementForImage);\n setObjectData(placeholder, element.id);\n \n // CRITICAL: Apply absolute position from page tree (same as non-image elements)\n // createImagePlaceholder uses element.left/top which may be relative; resolve to absolute\n const absPosImg = pageTree.length > 0 ? (() => {\n const node = findNodeById(pageTree, element.id);\n return node ? getAbsoluteBounds(node, pageTree) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n placeholder.set({ left: absPosImg.left, top: absPosImg.top });\n placeholder.setCoords();\n \n // Store the image URL on the placeholder\n const imageUrl = element.src || element.imageUrl;\n (placeholder as any).__imageSrc = imageUrl;\n \n // Set visibility and interaction properties based on mode\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n placeholder.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n visible: true, // CRITICAL: Always visible when added (isHidden only affects opacity)\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n \n // CRITICAL: Bring new elements to front so they're visible\n fc.add(placeholder);\n fc.bringObjectToFront(placeholder);\n \n // CRITICAL: Don't auto-select new elements if a crop group is active\n // This prevents selection jumping to image element\n const activeObj = fc.getActiveObject();\n if (activeObj && ((activeObj as any)._ct?.isCropGroup || (activeObj as any).__cropGroup)) {\n // Keep crop group selected, don't switch to new element\n fc.setActiveObject(activeObj);\n }\n \n // Force render to ensure element is visible\n (placeholder as any).dirty = true;\n fc.requestRenderAll();\n \n if (imageUrl) {\n loadImageAsync(elementForImage, placeholder, fc);\n }\n } else {\n // Resolve width/height for auto/inherit so created Fabric object has correct dimensions.\n // Preserve explicit tiny sizes from imports instead of forcing 20px minimum.\n const resolvedSizeCreate = pageTree.length > 0 ? getNodeBounds(element, pageTree, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 200, height: typeof element.height === 'number' ? element.height : 50 };\n const hasExplicitSize =\n typeof element.width === 'number' && Number.isFinite(element.width) && element.width > 0 &&\n typeof element.height === 'number' && Number.isFinite(element.height) && element.height > 0;\n const minVisibleCreate = hasExplicitSize ? 1 : 20;\n const elementForCreate = {\n ...element,\n width: Math.max(minVisibleCreate, Number(resolvedSizeCreate.width) || 200),\n height: Math.max(minVisibleCreate, Number(resolvedSizeCreate.height) || 50),\n };\n const obj = createFabricObject(elementForCreate);\n if (obj) {\n // Position: use absolute canvas coords from page tree (group children have relative in store; preview uses pageChildren prop)\n const absPos = pageTree.length > 0 ? (() => {\n const node = findNodeById(pageTree, element.id);\n return node ? getAbsoluteBounds(node, pageTree) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n obj.set({ left: absPos.left, top: absPos.top });\n obj.setCoords();\n // In preview/export mode, elements are not selectable/editable\n // But in preview mode, dynamic field elements can be clicked\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n // Set visibility and interaction properties\n // CRITICAL: Ensure visible is true and opacity is correct\n obj.set({\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n visible: true, // CRITICAL: Always visible when added (isHidden only affects opacity)\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n hasControls: allowEditing && !isHidden,\n hasBorders: allowEditing && !isHidden,\n hoverCursor: isDynamicField && isPreviewMode ? 'pointer' : undefined,\n });\n\n // Triangles always use caching with custom cache padding to avoid\n // low-opacity overlap artifacts while keeping sharp miter tips un-clipped.\n \n // CRITICAL: Bring new elements to front so they're visible\n fc.add(obj);\n fc.bringObjectToFront(obj);\n \n // CRITICAL: Don't auto-select new elements if a crop group is active\n // This prevents selection jumping to image element\n const activeObj = fc.getActiveObject();\n if (activeObj && ((activeObj as any)._ct?.isCropGroup || (activeObj as any).__cropGroup)) {\n // Keep crop group selected, don't switch to new element\n fc.setActiveObject(activeObj);\n }\n \n // Force render to ensure element is visible\n obj.dirty = true;\n fc.requestRenderAll();\n }\n }\n }\n }\n\n // Z-order: depth-first tree order. Section groups (fabric.Group) get their id in the list; inside groups we visit children in reverse.\n if (!shouldSkipUpdates) {\n const dfsIds: string[] = [];\n const visit = (nodes: CanvasNode[], reverseGroupChildren = false) => {\n const list = reverseGroupChildren ? [...nodes].reverse() : nodes;\n for (const n of list) {\n if (isElement(n)) dfsIds.push(n.id);\n else if (isGroup(n)) {\n const g = n as GroupNode;\n if (sectionGroupIds.has(g.id)) dfsIds.push(g.id);\n visit(g.children ?? [], true);\n }\n }\n };\n visit(pageTree, false);\n const allFabricObjects = fc.getObjects();\n allFabricObjects.sort((a, b) => {\n const aIndex = dfsIds.indexOf(getObjectId(a) || '');\n const bIndex = dfsIds.indexOf(getObjectId(b) || '');\n return aIndex - bIndex;\n });\n allFabricObjects.forEach((obj) => fc.bringObjectToFront(obj));\n }\n\n isRebuildingRef.current = false;\n \n // CRITICAL: Force render after adding new elements to ensure they're visible\n fc.requestRenderAll();\n \n // CRITICAL: If a crop group was active before sync, restore it\n // This prevents selection from jumping to newly added elements\n if (activeBeforeSync && fc.getObjects().includes(activeBeforeSync)) {\n const isCropGroup = (activeBeforeSync as any)._ct?.isCropGroup || (activeBeforeSync as any).__cropGroup;\n if (isCropGroup) {\n // Always restore crop group selection\n fc.setActiveObject(activeBeforeSync);\n fc.requestRenderAll();\n }\n }\n\n // Fabric -> store for text: use Fabric textbox bounds so layout/reflow sees correct height/width.\n // Include textboxes inside section groups (nested) so stack auto-shift and section height work.\n // Skip the textbox currently being edited - the live reflow rAF handles it.\n const stateAfterSync = useEditorStore.getState();\n const elementsAfterSync = stateAfterSync.getCurrentElements();\n const editingId = editingTextIdRef.current;\n const visitAllObjects = (fn: (obj: fabric.FabricObject) => void) => {\n fc.getObjects().forEach((obj) => {\n if (obj instanceof fabric.Group && (obj as any).__docuforgeSectionGroup) {\n obj.getObjects().forEach(fn);\n } else {\n fn(obj);\n }\n });\n };\n visitAllObjects((obj) => {\n if (obj instanceof fabric.Textbox) {\n const id = getObjectId(obj);\n if (!id || id === editingId) return;\n const w = obj.width ?? 0;\n const h = obj.height ?? 0;\n const el = elementsAfterSync.find((e) => e.id === id);\n if (!el || el.type !== 'text') return;\n const storeW = (el as CanvasElement).width;\n const storeH = (el as CanvasElement).height;\n const shouldKeepFixedHeight = el.overflowPolicy === 'auto-shrink';\n const needW = !shouldKeepFixedHeight && w > 0 && (typeof storeW !== 'number' || Math.abs(storeW - w) > 0.1);\n // Auto-shrink: allow height to shrink for proper stack reflow, but never grow\n const needH = shouldKeepFixedHeight\n ? (h > 0 && typeof storeH === 'number' && h < (storeH as number) - 0.5)\n : (h > 0 && (typeof storeH !== 'number' || Math.abs((storeH as number) - h) > 0.1));\n if (needW || needH) {\n const updates: Partial<CanvasElement> = {};\n if (needW) updates.width = w;\n if (needH) updates.height = h;\n // When sync was triggered by panel (font/size/weight change), don't run stack reflow so stored top is not reset\n stateAfterSync.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: false, skipStackReflow: syncTriggeredByPanelRef.current });\n }\n }\n });\n\n syncTriggeredByPanelRef.current = false;\n }; // End of doSyncRef.current function\n \n // When sync was triggered by a panel update (canvasUpdateVersion bumped), clear \"just modified\"\n // and mark so we discard selection before applying updates (positions apply to loose objects, no shift)\n if (canvasUpdateVersion !== prevCanvasUpdateVersionRef.current) {\n justModifiedIdsRef.current.clear();\n prevCanvasUpdateVersionRef.current = canvasUpdateVersion;\n syncTriggeredByPanelRef.current = true;\n } else {\n syncTriggeredByPanelRef.current = false;\n }\n\n // CRITICAL: Check if we should skip/defer sync\n // When locked, defer sync instead of skipping (so it runs when unlocked)\n const shouldSkipUpdates = syncLockedRef.current || editLockRef.current;\n \n // If locked, defer sync instead of skipping\n if (shouldSkipUpdates) {\n pendingSyncRef.current = true;\n return; // Defer sync, don't run it now\n }\n \n pendingSyncRef.current = false;\n if (ready && !hasClearedCachesBeforeFirstSyncRef.current) {\n hasClearedCachesBeforeFirstSyncRef.current = true;\n clearFabricCharCache();\n clearMeasurementCache();\n }\n doSyncRef.current();\n \n // In preview mode, force a repaint when canvas gets non-zero size (e.g. after container measures)\n if (isPreviewMode && fc && (fc.width ?? 0) > 0 && (fc.height ?? 0) > 0) {\n fc.requestRenderAll();\n }\n \n // CRITICAL: After running sync, check if there was a pending sync that we just fulfilled\n // This handles the case where elements changed while locked, then we unlocked\n if (pendingSyncRef.current) {\n pendingSyncRef.current = false;\n }\n // In preview mode re-run sync when scale becomes non-zero (container measured), so canvas paints after resize\n }, [elements, ready, canvasUpdateVersion, ...(isPreviewMode ? [workspaceZoom] : [])]);\n\n // Run deferred sync when unlock requested (from object:modified). Keeps doSync out of setTimeout to avoid \"Maximum update depth\".\n useEffect(() => {\n if (unlockRequestId === 0) return;\n if (doSyncRef.current) doSyncRef.current();\n }, [unlockRequestId]);\n\n // Live reflow during text edit: sync Fabric textbox dimensions to store every frame so siblings move down (document-like, no overlap).\n useEffect(() => {\n if (!allowEditing || !isActive) return;\n let rafId: number;\n const tick = () => {\n const id = editingTextIdRef.current;\n if (!id) {\n lastTextEditDimensionsRef.current = null;\n rafId = requestAnimationFrame(tick);\n return;\n }\n const fc = fabricRef.current;\n if (!fc) {\n rafId = requestAnimationFrame(tick);\n return;\n }\n let obj: fabric.FabricObject | null = fc.getObjects().find((o) => getObjectId(o) === id) ?? null;\n if (!obj) {\n for (const o of fc.getObjects()) {\n if (o instanceof fabric.Group && (o as any).__docuforgeSectionGroup) {\n const found = o.getObjects().find((c) => getObjectId(c) === id);\n if (found) {\n obj = found;\n break;\n }\n }\n }\n }\n if (!(obj instanceof fabric.Textbox)) {\n rafId = requestAnimationFrame(tick);\n return;\n }\n const w = obj.width ?? 0;\n const h = obj.height ?? 0;\n const last = lastTextEditDimensionsRef.current;\n if (last?.id === id && last.w === w && last.h === h) {\n rafId = requestAnimationFrame(tick);\n return;\n }\n lastTextEditDimensionsRef.current = { id, w, h };\n if (w > 0 && h > 0) {\n const state = useEditorStore.getState();\n const currentElement = state.getCurrentElements().find((el) => el.id === id);\n const isAutoShrinkText =\n currentElement?.type === 'text' && currentElement.overflowPolicy === 'auto-shrink';\n\n if (isAutoShrinkText) {\n const storeH = currentElement.height;\n // During live editing, auto-shrink elements may momentarily report larger textbox heights.\n // Persist only shrink so vertical stack siblings never move downward by mistake.\n if (h > 0 && (typeof storeH !== 'number' || h < storeH - 0.5)) {\n state.updateElement(id, { height: h }, { recordHistory: false, skipLayoutRecalc: false });\n }\n } else {\n state.updateElement(id, { width: w, height: h }, { recordHistory: false, skipLayoutRecalc: false });\n }\n }\n rafId = requestAnimationFrame(tick);\n };\n rafId = requestAnimationFrame(tick);\n return () => cancelAnimationFrame(rafId);\n }, [allowEditing, isActive, pageId]);\n\n // === POST-READY REFLOW: run twice (2 rAFs, reflow+persist, then 2 rAFs again) to settle text metrics after fonts load ===\n useEffect(() => {\n if (!ready || hasRunPostReadyReflowForPageRef.current === pageId) return;\n hasRunPostReadyReflowForPageRef.current = pageId;\n let cancelled = false;\n const runReflowAndPersist = () => {\n const fc = fabricRef.current;\n if (!fc || cancelled) return;\n clearFontCacheAndRerender(fc);\n const state = useEditorStore.getState();\n const page = state.canvas.pages.find((p) => p.id === pageId);\n if (page) {\n const elements = flattenChildren(page.children);\n fc.getObjects().forEach((obj) => {\n if (obj instanceof fabric.Textbox) {\n const id = getObjectId(obj);\n if (!id) return;\n const w = obj.width ?? 0;\n const h = obj.height ?? 0;\n const el = elements.find((e) => e.id === id);\n if (!el) return;\n const storeW = (el as CanvasElement).width ?? 0;\n const storeH = (el as CanvasElement).height ?? 0;\n const updates: Partial<CanvasElement> = {};\n const shouldKeepFixedSize = (el as CanvasElement).overflowPolicy === 'auto-shrink';\n // Persist width only when store has numeric width and it differs (don't overwrite 'inherit'/'auto')\n if (!shouldKeepFixedSize && w > 0 && typeof storeW === 'number' && Math.abs(w - storeW) > 0.1) updates.width = w;\n if (shouldKeepFixedSize) {\n if (h > 0 && typeof storeH === 'number' && h < storeH - 0.5) updates.height = h;\n } else {\n if (h > 0 && (typeof storeH !== 'number' || Math.abs(h - storeH) > 0.1)) updates.height = h;\n }\n if (Object.keys(updates).length > 0) {\n state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });\n }\n }\n });\n }\n };\n const raf1 = requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (cancelled) return;\n runReflowAndPersist();\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (cancelled) return;\n runReflowAndPersist();\n });\n });\n });\n });\n return () => {\n cancelled = true;\n cancelAnimationFrame(raf1);\n };\n }, [ready, pageId]);\n\n // === SYNC SELECTION (Store -> Fabric) ===\n useEffect(() => {\n const fc = fabricRef.current;\n // CRITICAL: Do not rebuild while transforming (prevents deselection and recreation)\n if (!fc || isRebuildingRef.current || !isActive) return;\n if (isTransforming(fc)) return; // <-- critical guard\n \n // CRITICAL: Never touch Fabric selection when user is editing text on canvas (double-click then type).\n // Otherwise we call setActiveObject/discardActiveObject and the textbox loses focus / exits editing.\n if (editingTextIdRef.current) return;\n \n // CRITICAL: Never touch Fabric selection when editLocked (e.g. during/after group move).\n // Running SELECTION_SYNC while locked causes a second ActiveSelection + setCoords() which\n // can move objects and cause visual shift (selectedIds often has group id so hasUnselectedNewElements\n // is true and we used to allow sync - that was wrong).\n if (editLockRef.current) return;\n\n // Set flag to prevent feedback loop (Fabric selection events -> store -> Fabric)\n isSyncingSelectionToFabricRef.current = true;\n\n // Build a set of selected IDs for faster lookup.\n // When a group is selected we pass the group id only; Fabric selects the group-bounds Rect (or empty-group Rect) for native handles.\n const selectedSet = new Set(selectedIds);\n\n // Find objects to select - top-level elements, fabric.Groups, and objects inside section groups\n const toSelect: fabric.FabricObject[] = [];\n \n for (const obj of fc.getObjects()) {\n const id = getObjectId(obj);\n if (id === '__background__') continue;\n if (id && selectedSet.has(id)) {\n toSelect.push(obj);\n continue;\n }\n // Section groups: selected ids may refer to elements inside the group\n if (obj instanceof fabric.Group && (obj as any).__docuforgeSectionGroup) {\n for (const child of obj.getObjects()) {\n const childId = getObjectId(child);\n if (childId && selectedSet.has(childId)) toSelect.push(child);\n }\n }\n }\n\n // CRITICAL: Don't discard selection if sync is locked (during transform) OR if edits are locked\n if (toSelect.length === 0) {\n // CRITICAL: Don't discard if edits are locked (during handle drag)\n // Also check if clicked on an object (Fabric's findTarget)\n // BUT: Allow deselection if it's a clean click (not during transform)\n if (!syncLockedRef.current && !editLockRef.current) {\n fc.discardActiveObject();\n }\n } else if (toSelect.length === 1) {\n const objId = getObjectId(toSelect[0]);\n const obj = toSelect[0];\n \n if (objId) {\n justModifiedIdsRef.current.add(objId);\n }\n fc.setActiveObject(obj);\n obj.setCoords();\n } else {\n // Flat group = multiple elements, no fabric.Group in selection.\n // setCoords() on ActiveSelection of multiple loose objects can recalc and shift positions (Fabric quirk).\n // Groups with subgroup often have a single fabric.Group, so they take the single-object path above and don't shift.\n const isFlatGroupSelection = toSelect.length > 1 && toSelect.every(o => !(o instanceof fabric.Group));\n \n // Reuse existing ActiveSelection when same set of objects — avoids recreate + setCoords() that causes shift\n const active = fc.getActiveObject();\n const sameSelection =\n active instanceof fabric.ActiveSelection &&\n active.getObjects().length === toSelect.length &&\n toSelect.every(obj => active.getObjects().includes(obj));\n \n if (sameSelection && isFlatGroupSelection) {\n // Don't recreate or call setCoords(); positions are already correct\n fc.requestRenderAll();\n } else {\n toSelect.forEach(obj => {\n const objId = getObjectId(obj);\n if (objId) justModifiedIdsRef.current.add(objId);\n });\n \n const selection = new fabric.ActiveSelection(toSelect, { canvas: fc });\n fc.setActiveObject(selection);\n // Flat group: setCoords() on ActiveSelection of loose objects can recalc and shift positions (Fabric quirk).\n // Skip it; control box may update on next interaction; avoids visual jump.\n if (!isFlatGroupSelection) {\n selection.setCoords();\n }\n }\n }\n\n fc.requestRenderAll();\n \n // Clear flag after a short delay to allow Fabric events to fire and be ignored\n requestAnimationFrame(() => {\n isSyncingSelectionToFabricRef.current = false;\n });\n }, [selectedIds, isActive, ready, elements]);\n\n // === HELPER FUNCTIONS ===\n // Note: createFabricObject and createFabricObjectForGroupMember are imported from fabricObjectCreators\n\n const updateFabricObject = (obj: fabric.FabricObject, element: CanvasElement, skipPositionUpdate = false) => {\n // CRITICAL: Do not update while transforming (prevents recreation during drag)\n const fc = fabricRef.current;\n if (fc && isTransforming(fc)) {\n return; // Skip updates during drag - prevents deselection\n }\n \n // Position for Fabric: always use absolute canvas coords. In preview use pageChildren prop; in editor use store.\n // When skipPositionUpdate (e.g. object just modified by user), don't overwrite left/top to prevent shift-after-drag.\n const currentPageTree = (pageChildren?.length ? pageChildren : useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children) ?? [];\n const fabricPos = currentPageTree.length > 0\n ? (() => {\n const node = findNodeById(currentPageTree, element.id);\n return node ? getAbsoluteBounds(node, currentPageTree) : { left: element.left ?? 0, top: element.top ?? 0 };\n })()\n : { left: element.left ?? 0, top: element.top ?? 0 };\n\n // Resolved width/height for Fabric (element may have auto/inherit — resolve from parent when in group)\n const resolvedSize = currentPageTree.length > 0 ? getNodeBounds(element, currentPageTree, pageBoundsOptions) : { width: typeof element.width === 'number' ? element.width : 200, height: typeof element.height === 'number' ? element.height : 50 };\n // Keep explicit small dimensions (e.g. imported bullets/borders) while still guarding unresolved auto/inherit sizes.\n const shouldPreserveSmallSize =\n typeof element.width === 'number' && Number.isFinite(element.width) && element.width > 0 &&\n typeof element.height === 'number' && Number.isFinite(element.height) && element.height > 0;\n const minVisible = shouldPreserveSmallSize ? 1 : 20;\n const rW = Math.max(minVisible, Number(resolvedSize.width) || 200);\n const rH = Math.max(minVisible, Number(resolvedSize.height) || 50);\n const preserveCornerGeometry =\n element.type === 'shape' &&\n (element.shapeType === 'circle' || element.shapeType === 'rounded-rect' || element.shapeType === 'triangle');\n const cornerSafeW = preserveCornerGeometry ? Math.max(1, rW * Math.abs(element.scaleX ?? 1)) : rW;\n const cornerSafeH = preserveCornerGeometry ? Math.max(1, rH * Math.abs(element.scaleY ?? 1)) : rH;\n // Textbox-like container semantics: children of flex/grid must not scale; use width/height from layout so text wraps\n const parentGroup = currentPageTree.length > 0 ? findParentGroup(currentPageTree, element.id) : null;\n const isInFlexOrGridGroup = parentGroup && (getEffectiveGroupDisplay(parentGroup) === 'flex' || getEffectiveGroupDisplay(parentGroup) === 'grid');\n const effectiveScaleX = preserveCornerGeometry ? 1 : (isInFlexOrGridGroup ? 1 : (element.scaleX ?? 1));\n const effectiveScaleY = preserveCornerGeometry ? 1 : (isInFlexOrGridGroup ? 1 : (element.scaleY ?? 1));\n \n // IMPORTANT:\n // Images need special handling based on imageFit mode\n const isImage = obj instanceof fabric.FabricImage;\n const isTextbox = obj instanceof fabric.Textbox;\n \n // For images in crop groups (not \"fill\"), update group and image inside IN PLACE\n if (obj instanceof fabric.Group && (obj as any).__cropGroup) {\n const ct = (obj as any).__cropData;\n if (ct) {\n const elementWidth = rW * (element.scaleX ?? 1);\n const elementHeight = rH * (element.scaleY ?? 1);\n const clipShape = element.clipShape || 'none';\n // CRITICAL: Only use rx if clipShape is 'rounded', otherwise set to 0\n // rx is stored as a ratio (0-0.5), convert old pixel values if needed\n let rxRatio = 0;\n if (clipShape === 'rounded') {\n const elementRx = element.rx || 0.1; // Default to 10% if not set\n // If rx > 0.5, it's likely an old pixel value, convert to ratio\n if (elementRx > 0.5) {\n const minDim = Math.min(elementWidth, elementHeight);\n rxRatio = Math.min(elementRx / minDim, 0.5); // Cap at 50%\n } else {\n rxRatio = elementRx;\n }\n }\n \n // Update frame dimensions IN PLACE (no recreation)\n ct.frameW = elementWidth;\n ct.frameH = elementHeight;\n ct.shape = clipShape === 'circle' ? 'circle' : clipShape === 'rounded' ? 'roundRect' : 'rect';\n ct.rx = rxRatio; // Store as ratio\n \n // Update maintainResolution flag (default to true)\n (obj as any).__maintainResolution = element.maintainResolution !== false;\n \n // Update group position and properties IN PLACE\n // Crop groups use originX/Y 'center' in Fabric — convert top-left (getAbsoluteBounds) to center\n const centerX = fabricPos.left + elementWidth / 2;\n const centerY = fabricPos.top + elementHeight / 2;\n const cropSetProps: Record<string, unknown> = {\n width: elementWidth,\n height: elementHeight,\n scaleX: 1,\n scaleY: 1,\n angle: element.angle ?? 0,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n opacity: element.opacity ?? 1,\n evented: true,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n subTargetCheck: false, // don't look inside group\n interactive: true, // CRITICAL: Keep interactive for proper control detection\n perPixelTargetFind: false,\n lockRotation: true, // Disable rotation for crop groups (simplifies resize math)\n hasRotatingPoint: false, // Hide rotation handle\n // Scale with zoom so handles stay same visual size (controls drawn in canvas pixel space)\n cornerSize: Math.max(6, Math.round(10 * (fc.getZoom() || 1))),\n borderScaleFactor: fc.getZoom() || 1,\n transparentCorners: false,\n cornerStyle: 'rect',\n cornerColor: '#ffffff',\n cornerStrokeColor: '#4f46e5',\n borderColor: '#4f46e5',\n padding: 0,\n };\n if (!skipPositionUpdate) {\n cropSetProps.left = centerX;\n cropSetProps.top = centerY;\n }\n obj.set(cropSetProps);\n \n // Ensure children are absolutely non-targetable\n const img = ct._img;\n if (img) {\n img.set({ \n evented: false, \n selectable: false, \n hasControls: false, \n hasBorders: false \n });\n }\n if (ct._border) {\n ct._border.set({ evented: false, selectable: false });\n }\n \n // Mark as crop group for promotion logic\n (obj as any)._ct = (obj as any)._ct || {};\n (obj as any)._ct.isCropGroup = true;\n \n // CRITICAL: Handle clipShape 'none' - remove clipPath\n if (clipShape === 'none') {\n obj.clipPath = undefined;\n } else {\n // Update clipPath with group-local positioning\n // CRITICAL: If clipShape changed, we may need to recreate the clipPath (Rect vs Ellipse)\n const needsNewClipPath = \n !obj.clipPath ||\n (clipShape === 'circle' && !(obj.clipPath instanceof fabric.Ellipse)) ||\n (clipShape !== 'circle' && !(obj.clipPath instanceof fabric.Rect));\n \n if (needsNewClipPath) {\n // Recreate clipPath with correct type\n const newClip = clipShape === 'circle'\n ? new fabric.Ellipse({\n rx: elementWidth / 2,\n ry: elementHeight / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n })\n : new fabric.Rect({\n width: elementWidth,\n height: elementHeight,\n rx: element.rx || 0,\n ry: element.rx || 0,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n (newClip as any).absolutePositioned = false;\n (newClip as any).excludeFromExport = true;\n // CRITICAL: Set coordinates and mark as dirty to ensure proper rendering\n newClip.setCoords();\n (newClip as any).dirty = true;\n obj.clipPath = newClip;\n } else if (obj.clipPath && typeof (obj.clipPath as any).set === 'function') {\n // Update existing clipPath properties\n // CRITICAL: Force refresh to ensure rx/ry changes are rendered\n if (clipShape === 'circle') {\n const clipPathObj = obj.clipPath as fabric.FabricObject;\n clipPathObj.set({\n rx: elementWidth / 2,\n ry: elementHeight / 2,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n // CRITICAL: Make clipPath non-interactive\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n // CRITICAL: Force update coordinates and mark as dirty to ensure re-render\n clipPathObj.setCoords();\n (clipPathObj as any).dirty = true;\n } else {\n // CRITICAL: Always update rx/ry, even if it's 0 (to remove rounded corners)\n const clipPathObj = obj.clipPath as fabric.FabricObject;\n clipPathObj.set({\n width: elementWidth,\n height: elementHeight,\n rx: element.rx || 0,\n ry: element.rx || 0,\n left: 0,\n top: 0,\n originX: 'center',\n originY: 'center',\n // CRITICAL: Make clipPath non-interactive\n selectable: false,\n evented: false,\n hasControls: false,\n hasBorders: false,\n });\n // CRITICAL: Force update coordinates and mark as dirty to ensure re-render\n clipPathObj.setCoords();\n (clipPathObj as any).dirty = true;\n }\n // Ensure clipPath is group-local for live updates\n (obj.clipPath as any).absolutePositioned = false;\n (obj.clipPath as any).excludeFromExport = true;\n }\n }\n \n // CRITICAL: Update layout to apply new clipPath shape\n updateCoverLayout(obj);\n \n // CRITICAL: Force immediate render to show clip shape change\n obj.setCoords();\n (obj as any).dirty = true;\n if (obj.clipPath) {\n (obj.clipPath as any).dirty = true;\n (obj.clipPath as any).setCoords();\n }\n \n // After update, apply controls based on element.useCropHandles\n if (element?.useCropHandles) {\n installCanvaMaskControls(obj);\n } else {\n setSimpleScaleControls(obj);\n }\n \n // CRITICAL: Ensure crop group is always selectable and evented after updates\n obj.set({\n selectable: true,\n evented: true,\n hasControls: true,\n hasBorders: true,\n interactive: true,\n subTargetCheck: false,\n });\n obj.setCoords();\n \n // CRITICAL: Force immediate render to show clip shape change in live preview\n fc.requestRenderAll();\n }\n } else if (isImage && element.imageFit && element.imageFit !== 'fill') {\n // Legacy: direct image with clipPath (shouldn't happen with new group approach)\n // Keep for backwards compatibility\n const baseScaleX = (obj as any).__cropBaseScaleX;\n const baseScaleY = (obj as any).__cropBaseScaleY;\n \n if (baseScaleX && baseScaleY) {\n const imgNaturalWidth = obj.width || 1;\n const imgNaturalHeight = obj.height || 1;\n const elementWidth = rW * (element.scaleX ?? 1);\n const elementHeight = rH * (element.scaleY ?? 1);\n \n // Keep image at fixed scale\n const scaledImageWidth = imgNaturalWidth * baseScaleX;\n const scaledImageHeight = imgNaturalHeight * baseScaleY;\n const imageLeft = fabricPos.left + (elementWidth - scaledImageWidth) / 2;\n const imageTop = fabricPos.top + (elementHeight - scaledImageHeight) / 2;\n \n obj.set({\n left: imageLeft,\n top: imageTop,\n scaleX: baseScaleX,\n scaleY: baseScaleY,\n });\n \n // Update clipPath\n const clipShape = element.clipShape || 'none';\n const cropWidth = elementWidth / baseScaleX;\n const cropHeight = elementHeight / baseScaleY;\n const clipLeft = (scaledImageWidth - elementWidth) / 2;\n const clipTop = (scaledImageHeight - elementHeight) / 2;\n \n let cropClip: fabric.FabricObject;\n if (clipShape === 'circle') {\n const radius = Math.min(cropWidth, cropHeight) / 2;\n cropClip = new fabric.Ellipse({\n left: -clipLeft / baseScaleX,\n top: -clipTop / baseScaleY,\n rx: radius,\n ry: radius,\n originX: 'left',\n originY: 'top',\n });\n } else if (clipShape === 'rounded') {\n const radius = Math.min(element.rx || 16, cropWidth / 2, cropHeight / 2);\n cropClip = new fabric.Rect({\n left: -clipLeft / baseScaleX,\n top: -clipTop / baseScaleY,\n width: cropWidth,\n height: cropHeight,\n rx: radius,\n ry: radius,\n originX: 'left',\n originY: 'top',\n });\n } else {\n cropClip = new fabric.Rect({\n left: -clipLeft / baseScaleX,\n top: -clipTop / baseScaleY,\n width: cropWidth,\n height: cropHeight,\n originX: 'left',\n originY: 'top',\n });\n }\n obj.clipPath = cropClip;\n }\n }\n \n const baseScaleX = isImage && element.imageFit === 'fill' ? (rW / ((obj.width ?? 1) || 1)) : 1;\n const baseScaleY = isImage && element.imageFit === 'fill' ? (rH / ((obj.height ?? 1) || 1)) : 1;\n\n // Line: set length from width, no scaleY/skew so it stays horizontal (no slant)\n const isLine = obj instanceof fabric.Line;\n if (isLine) {\n const lineLen = Math.max(1, Number(element.width) || rW || 100);\n const lineSet: Record<string, unknown> = {\n x1: 0,\n y1: 0,\n x2: lineLen,\n y2: 0,\n angle: element.angle ?? 0,\n scaleX: 1,\n scaleY: 1,\n skewX: 0,\n skewY: 0,\n };\n if (!skipPositionUpdate) {\n lineSet.left = fabricPos.left;\n lineSet.top = fabricPos.top;\n }\n obj.set(lineSet);\n obj.setCoords();\n }\n\n // If we have a raw transform matrix, use the stored decomposed values\n // For Line we already set position/angle/scale above; skip to avoid overwriting with slant\n if (!isLine) {\n let posIfNotSkipped: { left?: number; top?: number } = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };\n if (!skipPositionUpdate && obj instanceof fabric.FabricImage && obj.originX === 'center') {\n const vW = rW * effectiveScaleX;\n const vH = rH * effectiveScaleY;\n posIfNotSkipped = { left: fabricPos.left + vW / 2, top: fabricPos.top + vH / 2 };\n }\n if (element.transformMatrix && element.transformMatrix.length === 6) {\n if (isTextbox) {\n obj.set({\n ...posIfNotSkipped,\n width: rW,\n scaleX: effectiveScaleX,\n scaleY: effectiveScaleY,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n });\n } else {\n obj.set({\n ...posIfNotSkipped,\n scaleX: effectiveScaleX,\n scaleY: effectiveScaleY,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n });\n }\n obj.setCoords();\n } else {\n if (isTextbox) {\n obj.set({\n ...posIfNotSkipped,\n width: rW,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n scaleX: effectiveScaleX * baseScaleX,\n scaleY: effectiveScaleY * baseScaleY,\n });\n } else {\n obj.set({\n ...posIfNotSkipped,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n scaleX: effectiveScaleX * baseScaleX,\n scaleY: effectiveScaleY * baseScaleY,\n });\n }\n }\n }\n \n // Always set these properties regardless of matrix usage\n obj.set({\n opacity: element.opacity ?? 1,\n selectable: element.selectable,\n evented: element.evented,\n hasControls: element.selectable,\n hasBorders: element.selectable,\n lockMovementX: !element.selectable,\n lockMovementY: !element.selectable,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n\n if (obj instanceof fabric.Circle) {\n obj.set({\n radius: Math.min(cornerSafeW, cornerSafeH) / 2,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'round',\n strokeLineCap: 'round',\n lockUniScaling: true,\n objectCaching: true,\n });\n } else if (obj instanceof fabric.Ellipse) {\n obj.set({ rx: rW / 2, ry: rH / 2 });\n } else if (obj instanceof fabric.Textbox) {\n const overflowPolicy = element.overflowPolicy || 'grow-and-push';\n let text = element.text || 'Text';\n let fontSize = element.fontSize || 16;\n const minFontSize = element.minFontSize || 8;\n const maxLines = element.maxLines || 3;\n const storedWidth = rW > 0 ? rW : 200;\n const fixedWidth = Math.max(storedWidth, 1);\n const splitByGrapheme = overflowPolicy === 'auto-shrink'\n ? false\n : (element.splitByGrapheme ?? (element.wordWrap === 'break-word'));\n \n // For auto-shrink: keep width fixed, avoid implicit wrapping, and shrink font until text fits.\n if (overflowPolicy === 'auto-shrink') {\n const explicitLineCount = Math.max(1, text.split('\\n').length);\n while (fontSize > 1) {\n const testTextbox = new fabric.Textbox(text, {\n width: fixedWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fontWeight: element.fontWeight as number || 400,\n fontStyle: element.fontStyle || 'normal',\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n splitByGrapheme: false,\n });\n testTextbox.initDimensions();\n \n const textHeight = testTextbox.height || 0;\n const renderedLineCount = testTextbox.textLines?.length || 1;\n const hasNoImplicitWrap = renderedLineCount <= explicitLineCount;\n const fitsHeight = rH <= 0 || textHeight <= rH;\n // Also check that no line overflows the fixed width (single long word case)\n const actualTextboxWidth = testTextbox.width ?? fixedWidth;\n const widthDidGrow = actualTextboxWidth > fixedWidth + 0.5;\n const lineWidths = (testTextbox as any).__lineWidths as number[] | undefined;\n const maxLineWidth = lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : 0;\n const fitsWidth = !widthDidGrow && maxLineWidth <= fixedWidth + 1;\n\n if (hasNoImplicitWrap && fitsHeight && fitsWidth) {\n break;\n }\n fontSize--;\n }\n }\n \n // For max-lines-ellipsis: truncate text to fit max lines\n if (overflowPolicy === 'max-lines-ellipsis') {\n const testTextbox = new fabric.Textbox(element.text || 'Text', {\n width: rW,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n lineHeight: element.lineHeight || 1.2,\n splitByGrapheme,\n });\n testTextbox.initDimensions();\n \n const lines = testTextbox.textLines || [];\n if (lines.length > maxLines) {\n const originalText = element.text || 'Text';\n \n // Helper to count lines for a given text\n const countLines = (testText: string): number => {\n const tb = new fabric.Textbox(testText, {\n width: rW,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n lineHeight: element.lineHeight || 1.2,\n splitByGrapheme,\n });\n tb.initDimensions();\n return tb.textLines?.length || 1;\n };\n \n // Binary search for the right cutoff point\n let low = 0;\n let high = originalText.length;\n let result = originalText;\n \n while (low < high) {\n const mid = Math.floor((low + high + 1) / 2);\n const testText = originalText.slice(0, mid) + '...';\n const lineCount = countLines(testText);\n \n if (lineCount <= maxLines) {\n low = mid;\n result = testText;\n } else {\n high = mid - 1;\n }\n }\n \n // Trim trailing spaces before ellipsis\n if (result.endsWith('...') && result.length > 3) {\n const beforeEllipsis = result.slice(0, -3).trimEnd();\n result = beforeEllipsis + '...';\n }\n \n text = result;\n }\n }\n \n // For auto-shrink width must remain fixed; other policies can expand to avoid horizontal clipping.\n const minWidth = getMinTextWidth(element);\n const textboxWidth = overflowPolicy === 'auto-shrink'\n ? fixedWidth\n : Math.max(storedWidth, minWidth);\n obj.set({\n width: textboxWidth,\n fontSize,\n fontFamily: element.fontFamily || 'Open Sans',\n fill: element.fill || '#1a1a1a',\n fontWeight: (element.fontWeight as number) || 400,\n textAlign: (element.textAlign as any) || 'left',\n fontStyle: element.fontStyle || 'normal',\n underline: element.underline ?? false,\n linethrough: element.linethrough ?? false,\n lineHeight: element.lineHeight || 1.2,\n charSpacing: element.charSpacing || 0,\n objectCaching: false,\n noScaleCache: true,\n splitByGrapheme,\n text,\n });\n \n // Re-initialize dimensions so Textbox height matches content (keeps text inside bounds)\n obj.initDimensions();\n // CRITICAL: Force-set width back after initDimensions — Fabric may shrink it,\n // causing underline/linethrough to end short of the last few characters.\n if (Math.abs((obj.width ?? 0) - textboxWidth) > 0.01) {\n (obj as any).width = textboxWidth;\n }\n obj.setCoords();\n obj.dirty = true;\n } else if (!isImage && !isLine) {\n // For shapes: use resolved width/height. Circle + rounded-rect + triangle bake scale into dimensions to preserve geometry and stroke behavior.\n if (obj instanceof fabric.Circle) {\n obj.set({\n radius: Math.min(cornerSafeW, cornerSafeH) / 2,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n objectCaching: true,\n });\n } else if (obj instanceof fabric.Rect && element.shapeType === 'rounded-rect') {\n const toRadius = (value: number | undefined, fallback: number) =>\n Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;\n const baseRx = Math.max(0, Number(element.rx ?? 0));\n const baseRy = Math.max(0, Number(element.ry ?? element.rx ?? 0));\n const hasIndividualCornerValues =\n element.rxTL != null || element.rxTR != null || element.rxBR != null || element.rxBL != null;\n const tl = toRadius(element.rxTL, baseRx);\n const tr = toRadius(element.rxTR, baseRx);\n const br = toRadius(element.rxBR, baseRx);\n const bl = toRadius(element.rxBL, baseRx);\n const hasNonUniformCorners = hasIndividualCornerValues && (tl !== tr || tr !== br || br !== bl);\n const shouldUsePath = hasNonUniformCorners;\n const rectRx = hasIndividualCornerValues ? tl : baseRx;\n const rectRy = hasIndividualCornerValues ? tl : baseRy;\n const hasRoundedCorners = shouldUsePath\n ? (tl > 0 || tr > 0 || br > 0 || bl > 0)\n : (rectRx > 0 || rectRy > 0);\n\n if (shouldUsePath) {\n const pathStr = buildRoundedRectPath(cornerSafeW, cornerSafeH, tl, tr, br, bl);\n const pathObj = new fabric.Path(pathStr, {\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n left: obj.left,\n top: obj.top,\n angle: obj.angle,\n scaleX: 1,\n scaleY: 1,\n opacity: obj.opacity,\n originX: 'left',\n originY: 'top',\n selectable: obj.selectable,\n evented: obj.evented,\n objectCaching: true,\n });\n setObjectData(pathObj, element.id);\n const fc2 = fabricRef.current;\n if (fc2) {\n const idx = fc2.getObjects().indexOf(obj);\n fc2.remove(obj);\n if (idx >= 0) {\n fc2.insertAt(idx, pathObj);\n } else {\n fc2.add(pathObj);\n }\n }\n } else {\n obj.set({\n width: cornerSafeW,\n height: cornerSafeH,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: hasRoundedCorners ? 'round' : 'miter',\n strokeLineCap: hasRoundedCorners ? 'round' : 'butt',\n objectCaching: true,\n rx: rectRx,\n ry: rectRy,\n });\n }\n } else if (obj instanceof fabric.Path && element.shapeType === 'rounded-rect') {\n const toRadius = (value: number | undefined, fallback: number) =>\n Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;\n const baseRx = Math.max(0, Number(element.rx ?? 0));\n const hasIndividualCornerValues =\n element.rxTL != null || element.rxTR != null || element.rxBR != null || element.rxBL != null;\n const tl = toRadius(element.rxTL, baseRx);\n const tr = toRadius(element.rxTR, baseRx);\n const br = toRadius(element.rxBR, baseRx);\n const bl = toRadius(element.rxBL, baseRx);\n const hasNonUniformCorners = hasIndividualCornerValues && (tl !== tr || tr !== br || br !== bl);\n const shouldUsePath = hasNonUniformCorners;\n // For path-based shapes, always use miter — Bezier curves handle the rounding\n\n if (!shouldUsePath) {\n const rectRadius = hasIndividualCornerValues ? tl : baseRx;\n const rectObj = new fabric.Rect({\n width: cornerSafeW,\n height: cornerSafeH,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n rx: rectRadius,\n ry: rectRadius,\n strokeUniform: true,\n strokeLineJoin: rectRadius > 0 ? 'round' : 'miter',\n strokeLineCap: rectRadius > 0 ? 'round' : 'butt',\n left: obj.left,\n top: obj.top,\n angle: obj.angle,\n scaleX: 1,\n scaleY: 1,\n opacity: obj.opacity,\n originX: 'left',\n originY: 'top',\n selectable: obj.selectable,\n evented: obj.evented,\n objectCaching: true,\n });\n setObjectData(rectObj, element.id);\n const fc2 = fabricRef.current;\n if (fc2) {\n const idx = fc2.getObjects().indexOf(obj);\n fc2.remove(obj);\n if (idx >= 0) {\n fc2.insertAt(idx, rectObj);\n } else {\n fc2.add(rectObj);\n }\n }\n } else {\n const pathStr = buildRoundedRectPath(cornerSafeW, cornerSafeH, tl, tr, br, bl);\n const pathObj = new fabric.Path(pathStr, {\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n left: obj.left,\n top: obj.top,\n angle: obj.angle,\n scaleX: 1,\n scaleY: 1,\n opacity: obj.opacity,\n originX: 'left',\n originY: 'top',\n selectable: obj.selectable,\n evented: obj.evented,\n objectCaching: true,\n });\n setObjectData(pathObj, element.id);\n const fc2 = fabricRef.current;\n if (fc2) {\n const idx = fc2.getObjects().indexOf(obj);\n fc2.remove(obj);\n if (idx >= 0) {\n fc2.insertAt(idx, pathObj);\n } else {\n fc2.add(pathObj);\n }\n }\n }\n } else if (element.shapeType === 'triangle') {\n // Render triangles as Path to keep geometry consistent. In preview, disable caching to preserve sharp mitered tips.\n const rTop = Math.max(0, Number(element.triRTop ?? 0));\n const rBR = Math.max(0, Number(element.triRBR ?? 0));\n const rBL = Math.max(0, Number(element.triRBL ?? 0));\n const pathStr = buildRoundedTrianglePath(cornerSafeW, cornerSafeH, rTop, rBR, rBL);\n\n if (obj instanceof fabric.Path) {\n const newPath = new fabric.Path(pathStr);\n const newPathOffset = (newPath as any).pathOffset ?? new fabric.Point(cornerSafeW / 2, cornerSafeH / 2);\n (obj as fabric.Path).set({\n path: (newPath as any).path,\n pathOffset: newPathOffset,\n width: cornerSafeW,\n height: cornerSafeH,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n scaleX: 1,\n scaleY: 1,\n objectCaching: false,\n });\n } else {\n const pathObj = new fabric.Path(pathStr, {\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n strokeMiterLimit: TRIANGLE_STROKE_MITER_LIMIT,\n left: obj.left,\n top: obj.top,\n angle: obj.angle,\n scaleX: 1,\n scaleY: 1,\n opacity: obj.opacity,\n originX: 'left',\n originY: 'top',\n selectable: obj.selectable,\n evented: obj.evented,\n objectCaching: false,\n });\n setObjectData(pathObj, element.id);\n const fc2 = fabricRef.current;\n if (fc2) {\n const idx = fc2.getObjects().indexOf(obj);\n fc2.remove(obj);\n if (idx >= 0) fc2.insertAt(idx, pathObj);\n else fc2.add(pathObj);\n }\n }\n } else {\n // Generic shapes (backwards-compatible legacy shapes)\n obj.set({\n width: rW,\n height: rH,\n fill: element.fill || 'transparent',\n stroke: element.stroke || 'transparent',\n strokeWidth: element.strokeWidth || 0,\n strokeUniform: true,\n strokeLineJoin: 'miter',\n strokeLineCap: 'butt',\n objectCaching: true,\n });\n }\n\n // CRITICAL: Mark dirty and force render so Fabric re-renders the shape\n obj.dirty = true;\n obj.setCoords();\n const fcShape = fabricRef.current;\n if (fcShape) fcShape.renderAll();\n }\n\n // Line: always sync stroke (color) and strokeWidth (thickness) — Line uses stroke, not fill, so the 'fill' in obj block above is skipped\n if (isLine) {\n obj.set({\n stroke: element.stroke || '#1a1a1a',\n strokeWidth: typeof element.strokeWidth === 'number' ? element.strokeWidth : 2,\n strokeUniform: true,\n strokeLineCap: 'round',\n strokeLineJoin: 'round',\n ...(element.strokeDashArray && { strokeDashArray: element.strokeDashArray }),\n });\n }\n\n // === GRADIENT FILL/STROKE APPLICATION ===\n // Apply Fabric.js gradient objects when fillGradient or strokeGradient is set\n // CRITICAL: Disable objectCaching for gradient fills — Fabric's cache doesn't\n // reliably invalidate when gradient stops/colors change, causing stale renders.\n if (element.fillGradient && isGradientConfig(element.fillGradient)) {\n const objWidth = typeof obj.width === 'number' ? obj.width : rW;\n const objHeight = typeof obj.height === 'number' ? obj.height : rH;\n if (objWidth > 0 && objHeight > 0) {\n obj.set({\n fill: gradientToFabric(element.fillGradient, objWidth, objHeight),\n objectCaching: false,\n });\n (obj as any).__lastFillGradientJson = JSON.stringify(element.fillGradient);\n obj.dirty = true;\n const fcGrad = fabricRef.current;\n if (fcGrad) fcGrad.requestRenderAll();\n }\n } else {\n (obj as any).__lastFillGradientJson = 'null';\n }\n if (element.strokeGradient && isGradientConfig(element.strokeGradient)) {\n const objWidth = typeof obj.width === 'number' ? obj.width : rW;\n const objHeight = typeof obj.height === 'number' ? obj.height : rH;\n if (objWidth > 0 && objHeight > 0) {\n obj.set({\n stroke: gradientToFabric(element.strokeGradient, objWidth, objHeight),\n objectCaching: false,\n });\n (obj as any).__lastStrokeGradientJson = JSON.stringify(element.strokeGradient);\n obj.dirty = true;\n const fcGrad = fabricRef.current;\n if (fcGrad) fcGrad.requestRenderAll();\n }\n } else {\n (obj as any).__lastStrokeGradientJson = 'null';\n }\n };\n\n // Note: createShape, createText, createLine, createImagePlaceholder, etc. are imported from modules\n // Keeping loadImageAsync local as it needs component refs (fabricRef, syncLockedRef, isTransforming)\n\n // Helper function to clamp a value between min and max\n const clamp = (v: number, min: number, max: number): number => {\n return Math.max(min, Math.min(max, v));\n };\n\n const loadImageAsync = async (\n element: CanvasElement,\n placeholder: fabric.FabricObject,\n fc: fabric.Canvas\n ) => {\n const imageUrl = element.src || element.imageUrl;\n if (!imageUrl) return;\n const elementId = element.id;\n const nextSvgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : '';\n\n // Monotonic request id per element to avoid race conditions creating duplicate objects\n const nextRequestSeq = (imageReloadRequestSeqRef.current.get(elementId) ?? 0) + 1;\n imageReloadRequestSeqRef.current.set(elementId, nextRequestSeq);\n const isLatestRequest = () => imageReloadRequestSeqRef.current.get(elementId) === nextRequestSeq;\n\n // CRITICAL: Early return for crop groups - don't remove/recreate if only crop/resize changed\n // This prevents deselection during drag\n if (placeholder instanceof fabric.Group && (placeholder as any).__cropGroup) {\n const ct = (placeholder as any).__cropData;\n const existingImg = ct?._img;\n const existingSrc = (placeholder as any).__imageSrc;\n const existingSvgColorMap = (placeholder as any).__svgColorMap || '';\n\n // If image URL and SVG color map haven't changed and image exists, don't remove/recreate\n if (existingImg && existingSrc === imageUrl && existingSvgColorMap === nextSvgColorMap) {\n // Crop/resize changed; image source did not.\n // Do not remove/recreate the object - just update in place if needed\n return placeholder;\n }\n }\n\n try {\n // Preserve imported SVG fidelity: avoid normalization/re-encoding for inline SVG data URLs\n // unless user explicitly applied SVG color overrides.\n let url = getProxiedImageUrl(imageUrl);\n const hasColorOverrides = Boolean((element as any).svgColorMap && Object.keys((element as any).svgColorMap).length > 0);\n const isInlineSvgDataUrl = imageUrl.startsWith('data:image/svg+xml');\n\n if (isSvgImage(imageUrl, (element as any).sourceFormat) && (!isInlineSvgDataUrl || hasColorOverrides)) {\n const normalized = await getNormalizedSvgUrl(imageUrl, (element as any).svgColorMap, (element as any).sourceFormat);\n if (!isLatestRequest()) return;\n if (normalized) url = normalized;\n }\n const img = await fabric.FabricImage.fromURL(url, { crossOrigin: 'anonymous' });\n\n if (!fabricRef.current || !isLatestRequest()) return;\n\n // Fallback: ensure Fabric has correct dimensions if browser still reported wrong\n await normalizeSvgImageDimensions(img, imageUrl, (element as any).sourceFormat);\n if (!isLatestRequest()) return;\n\n // Set basic properties first\n // Account for visibility state - hidden elements should have opacity 0\n const isHidden = !element.visible;\n // NOTE: opacity is set on the OUTER object only (crop group or raw image in fill mode).\n // Setting it on the inner img AND the crop group causes double-multiplication.\n img.set({\n originX: 'left',\n originY: 'top',\n visible: !isHidden,\n opacity: 1, // opacity applied on final wrapper, not here\n selectable: !isHidden && element.selectable,\n evented: !isHidden && element.evented,\n hasControls: !isHidden && element.selectable,\n hasBorders: !isHidden && element.selectable,\n lockMovementX: !element.selectable,\n lockMovementY: !element.selectable,\n // NOTE: flipX/flipY are set on the OUTER object only (crop group or raw image in fill mode).\n // Setting it on the inner img AND the crop group causes double-flip (cancels out).\n // Flip will be applied below: on cropGroup for crop mode, on img for fill mode.\n // Disable caching to ensure proper transparency rendering\n objectCaching: false,\n noScaleCache: true,\n });\n\n // If we have a stored transformMatrix, use the stored element values\n // The element.left/top/scale/angle were saved from the actual Fabric object properties\n if (element.transformMatrix && element.transformMatrix.length === 6) {\n img.set({\n left: element.left,\n top: element.top,\n scaleX: element.scaleX ?? 1,\n scaleY: element.scaleY ?? 1,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n });\n img.setCoords();\n } else {\n // Calculate image scale based on imageFit mode\n const imageFit = element.imageFit || (element as any).style?.imageFit || 'cover';\n // In preview mode use same fallback as form thumbnail/export: clipShape ?? style.imageFrameShape ?? 'rectangle'\n const clipShape = element.clipShape ?? (element as any).style?.imageFrameShape ?? (isPreviewMode ? 'rectangle' : 'none');\n const needCropGroup = imageFit !== 'fill' || (clipShape && clipShape !== 'none');\n const imgNaturalWidth = img.width || 1;\n const imgNaturalHeight = img.height || 1;\n const elementWidth = Number(element.width);\n const elementHeight = Number(element.height);\n \n let baseScaleX: number;\n let baseScaleY: number;\n \n if (imageFit === 'fill' && !needCropGroup) {\n // Fill (no clip shape): stretch to fit element dimensions\n baseScaleX = elementWidth / imgNaturalWidth;\n baseScaleY = elementHeight / imgNaturalHeight;\n } else if (imageFit === 'contain') {\n const scaleX = elementWidth / imgNaturalWidth;\n const scaleY = elementHeight / imgNaturalHeight;\n const scale = Math.min(scaleX, scaleY);\n baseScaleX = scale;\n baseScaleY = scale;\n } else {\n // Cover (or fill + clipShape: use cover so frame is filled and clipped)\n const scaleX = elementWidth / imgNaturalWidth;\n const scaleY = elementHeight / imgNaturalHeight;\n const scale = Math.max(scaleX, scaleY);\n baseScaleX = scale;\n baseScaleY = scale;\n }\n \n // When needCropGroup we wrap in crop group; otherwise use image directly (fill, no clip)\n if (imageFit === 'fill' && !needCropGroup) {\n // Fill mode: apply element scale on top\n const finalScaleX = baseScaleX * (element.scaleX ?? 1);\n const finalScaleY = baseScaleY * (element.scaleY ?? 1);\n \n const pageTreeForCreate = (pageChildren?.length ? pageChildren : useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children) ?? [];\n const createPos = pageTreeForCreate.length > 0 ? (() => {\n const node = findNodeById(pageTreeForCreate, element.id);\n return node ? getAbsoluteBounds(node, pageTreeForCreate) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n img.set({\n left: createPos.left,\n top: createPos.top,\n scaleX: finalScaleX,\n scaleY: finalScaleY,\n angle: element.angle ?? 0,\n skewX: element.skewX ?? 0,\n skewY: element.skewY ?? 0,\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n \n // Apply clipShape if specified\n const clipPath = createImageClipPath(element, img.width || Number(element.width), img.height || Number(element.height));\n if (clipPath) {\n img.clipPath = clipPath;\n }\n } else {\n // Crop mode: image stays at base scale, position it centered\n // The image will be wrapped in a group that acts as the crop window\n const elementWidth = Number(element.width) * (element.scaleX ?? 1);\n const elementHeight = Number(element.height) * (element.scaleY ?? 1);\n \n // Calculate scaled image dimensions\n const scaledImageWidth = imgNaturalWidth * baseScaleX;\n const scaledImageHeight = imgNaturalHeight * baseScaleY;\n \n // Center the image within the group (group will be created below)\n // Position relative to group's top-left (0,0)\n const imageLeft = (elementWidth - scaledImageWidth) / 2;\n const imageTop = (elementHeight - scaledImageHeight) / 2;\n \n // Set image properties with correct origin\n img.set({\n originX: 'left',\n originY: 'top',\n left: imageLeft,\n top: imageTop,\n scaleX: baseScaleX,\n scaleY: baseScaleY,\n angle: 0, // Image rotation handled by group\n skewX: 0,\n skewY: 0,\n });\n \n // Store base scale for later use\n (img as any).__cropBaseScaleX = baseScaleX;\n (img as any).__cropBaseScaleY = baseScaleY;\n }\n }\n\n // Store the image URL and color map on the loaded image\n (img as any).__imageSrc = imageUrl;\n (img as any).__svgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : '';\n\n // Use crop group when imageFit is cover/contain, or when any clip shape is set (so preview clips like editor)\n const imageFitFinal = element.imageFit || (element as any).style?.imageFit || 'cover';\n const clipShapeFinal = element.clipShape ?? (element as any).style?.imageFrameShape ?? (isPreviewMode ? 'rectangle' : 'none');\n const needCropGroup = imageFitFinal !== 'fill' || (clipShapeFinal && clipShapeFinal !== 'none');\n let finalObject: fabric.FabricObject = img;\n \n if (needCropGroup) {\n // Resolve frame size from store so reload uses saved dimensions (avoid stale closure)\n const pageTreeForCropResolve = (pageChildren?.length ? pageChildren : useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children) ?? [];\n const nodeForSize = pageTreeForCropResolve.length ? findNodeById(pageTreeForCropResolve, element.id) : null;\n const w = nodeForSize && isElement(nodeForSize) ? (nodeForSize as CanvasElement).width : element.width;\n const h = nodeForSize && isElement(nodeForSize) ? (nodeForSize as CanvasElement).height : element.height;\n const sx = nodeForSize && isElement(nodeForSize) ? ((nodeForSize as CanvasElement).scaleX ?? 1) : (element.scaleX ?? 1);\n const sy = nodeForSize && isElement(nodeForSize) ? ((nodeForSize as CanvasElement).scaleY ?? 1) : (element.scaleY ?? 1);\n const elementWidth = Math.max(1, Number(w) || 200) * sx;\n const elementHeight = Math.max(1, Number(h) || 50) * sy;\n const clipShape = clipShapeFinal;\n // rx is stored as ratio (0-0.5), convert old pixel values if needed\n let rxRatio = 0;\n if (clipShape === 'rounded') {\n const elementRx = element.rx || 0.1; // Default to 10% if not set\n // If rx > 0.5, it's likely an old pixel value, convert to ratio\n if (elementRx > 0.5) {\n const minDim = Math.min(elementWidth, elementHeight);\n rxRatio = Math.min(elementRx / minDim, 0.5); // Cap at 50%\n } else {\n rxRatio = elementRx;\n }\n }\n \n // Map clipShape to createMaskedImageElement shape format\n let shape: 'circle' | 'roundRect' | 'rect' = 'rect';\n if (clipShape === 'circle') {\n shape = 'circle';\n } else if (clipShape === 'rounded' || clipShape === 'rectangle') {\n shape = 'roundRect';\n }\n \n // Check if there's an existing crop group to preserve pan values\n const existingCropGroup = fc.getObjects().find(\n (obj) => obj instanceof fabric.Group && \n (obj as any).__cropGroup && \n getObjectId(obj) === element.id\n ) as fabric.Group | undefined;\n \n let panX = (element as any).cropPanX ?? 0.5;\n let panY = (element as any).cropPanY ?? 0.5;\n let zoom = (element as any).cropZoom ?? 1;\n if (existingCropGroup) {\n const existingImg = (existingCropGroup as any).__cropData?._img;\n if (existingImg) {\n // Prefer live _ct; fallback to stored element values\n panX = (existingImg as any)._ct?.panX ?? (existingImg as any).__panX ?? panX;\n panY = (existingImg as any)._ct?.panY ?? (existingImg as any).__panY ?? panY;\n zoom = (existingImg as any)._ct?.zoom ?? zoom;\n }\n }\n \n // Create the masked image element using the new utility\n // Use the already-loaded image instead of loading again\n const isDynamicField = dynamicFieldIds.includes(element.id);\n const canBeEvented = isEditorMode || (isPreviewMode && isDynamicField);\n \n const pageTreeForCrop = (pageChildren?.length ? pageChildren : useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)?.children) ?? [];\n const createPosForCrop = pageTreeForCrop.length > 0 ? (() => {\n const node = findNodeById(pageTreeForCrop, element.id);\n return node ? getAbsoluteBounds(node, pageTreeForCrop) : { left: element.left ?? 0, top: element.top ?? 0 };\n })() : { left: element.left ?? 0, top: element.top ?? 0 };\n // Re-resolve frame size from same tree so create uses exact saved dimensions (fixes reload scale)\n const nodeForCreate = pageTreeForCrop.length ? findNodeById(pageTreeForCrop, element.id) : null;\n const createW = nodeForCreate && isElement(nodeForCreate) ? (nodeForCreate as CanvasElement).width : elementWidth;\n const createH = nodeForCreate && isElement(nodeForCreate) ? (nodeForCreate as CanvasElement).height : elementHeight;\n const createSx = nodeForCreate && isElement(nodeForCreate) ? ((nodeForCreate as CanvasElement).scaleX ?? 1) : (element.scaleX ?? 1);\n const createSy = nodeForCreate && isElement(nodeForCreate) ? ((nodeForCreate as CanvasElement).scaleY ?? 1) : (element.scaleY ?? 1);\n const frameW = Math.max(1, Number(createW) || 200) * createSx;\n const frameH = Math.max(1, Number(createH) || 50) * createSy;\n // Crop groups use center origin in Fabric — convert top-left (getAbsoluteBounds) to center\n const createCenterX = createPosForCrop.left + frameW / 2;\n const createCenterY = createPosForCrop.top + frameH / 2;\n const cropGroup = await createMaskedImageElement({\n image: img,\n frameW,\n frameH,\n shape,\n rx: rxRatio, // Pass ratio, not pixel value\n stroke: null, // No border by default\n strokeWidth: 0,\n left: createCenterX,\n top: createCenterY,\n angle: element.angle ?? 0,\n opacity: isHidden ? 0 : (element.opacity ?? 1),\n selectable: allowSelection && !isHidden,\n evented: canBeEvented && !isHidden,\n visible: !isHidden,\n panX,\n panY,\n zoom,\n });\n \n // Store maintainResolution flag on the crop group (default to true)\n (cropGroup as any).__maintainResolution = element.maintainResolution !== false;\n \n // Use transparent so PNG/SVG transparent pixels show through (no white fill)\n cropGroup.set({\n backgroundColor: 'transparent',\n flipX: element.flipX ?? false,\n flipY: element.flipY ?? false,\n });\n \n // CRITICAL: In preview mode, disable controls and borders for crop groups\n // DO NOT install controls in preview mode\n if (!allowEditing) {\n cropGroup.set({\n hasControls: false,\n hasBorders: false,\n selectable: false,\n evented: canBeEvented && !isHidden,\n });\n } else {\n // Editor mode - use element.useCropHandles (checkbox in Image Settings)\n if (element.useCropHandles) {\n installCanvaMaskControls(cropGroup);\n } else {\n setSimpleScaleControls(cropGroup);\n }\n }\n \n // Restore pan/zoom on image\n const cropImg = (cropGroup as any).__cropData?._img;\n if (cropImg) {\n (cropImg as any)._ct = { panX, panY, zoom };\n updateCoverLayout(cropGroup);\n }\n \n // Store metadata\n setObjectData(cropGroup, element.id);\n (cropGroup as any).__imageElement = cropImg;\n (cropGroup as any).__imageSrc = imageUrl;\n (cropGroup as any).__svgColorMap = nextSvgColorMap;\n \n // Persist natural dimensions to store so export never relies on Fabric baked state after move\n if (cropImg && (element as any).imageNaturalWidth == null) {\n const el = (cropImg as any).getElement?.() ?? (cropImg as any)._element;\n const orig = typeof (cropImg as any).getOriginalSize === 'function' ? (cropImg as any).getOriginalSize() : null;\n const nw = orig?.width ?? el?.naturalWidth ?? (cropImg as any).width;\n const nh = orig?.height ?? el?.naturalHeight ?? (cropImg as any).height;\n if (typeof nw === 'number' && typeof nh === 'number' && nw > 0 && nh > 0) {\n useEditorStore.getState().updateElement(element.id, { imageNaturalWidth: nw, imageNaturalHeight: nh }, { recordHistory: false });\n }\n }\n \n finalObject = cropGroup;\n } else {\n // Fill mode: use image directly — apply opacity here since there's no crop group wrapper\n const isHiddenFill = !element.visible;\n img.set({ opacity: isHiddenFill ? 0 : (element.opacity ?? 1) });\n setObjectData(img, element.id);\n }\n\n // CRITICAL: Only remove placeholder if we're not transforming\n // This prevents deselection during drag\n if (!syncLockedRef.current && !isTransforming(fc)) {\n const activeBeforeReplace = fc.getActiveObject();\n const activeIdsBeforeReplace = new Set(\n fc.getActiveObjects()\n .map((o) => getObjectId(o))\n .filter((id): id is string => !!id)\n );\n const wasPlaceholderSelected = activeIdsBeforeReplace.has(elementId);\n\n // Drop stale async reloads (older recolor/image loads) so they can't override newer state\n if (!isLatestRequest()) return;\n\n const canvasObjects = fc.getObjects();\n const idx = canvasObjects.indexOf(placeholder);\n\n if (idx >= 0) {\n // Prevent store selection clear only when we are actually removing selected placeholder\n if (wasPlaceholderSelected) {\n skipSelectionClearOnDiscardRef.current = true;\n }\n fc.remove(placeholder);\n fc.insertAt(idx, finalObject);\n } else {\n const existingForElement = canvasObjects.find((obj) => getObjectId(obj) === elementId);\n if (existingForElement) {\n // Placeholder already replaced by a newer request; avoid duplicate overlay\n return;\n }\n fc.add(finalObject);\n }\n\n // Restore active selection immediately for the replaced element\n if (wasPlaceholderSelected && allowSelection) {\n // Keep behavior predictable: restore the recolored object as active\n fc.setActiveObject(finalObject);\n finalObject.setCoords();\n // Safety reset in case no selection:cleared event fired\n requestAnimationFrame(() => {\n skipSelectionClearOnDiscardRef.current = false;\n });\n } else if (activeBeforeReplace && fc.getObjects().includes(activeBeforeReplace)) {\n // Preserve previously active object when it still exists\n fc.setActiveObject(activeBeforeReplace);\n }\n } else {\n // During transform, just update in place if possible\n // For crop groups, we should never need to remove/recreate\n if (finalObject instanceof fabric.Group && (finalObject as any).__cropGroup) {\n // Don't remove - just update the existing object\n // The placeholder will be replaced on next non-transform sync\n return;\n }\n }\n\n fc.requestRenderAll();\n } catch (error) {\n // Failed to load image - non-critical\n }\n };\n\n // Note: loadImageIntoGroup is imported from canvasImageLoader\n\n // Handle canvas click for adding elements\n const handleCanvasClick = useCallback(\n (e: React.MouseEvent<HTMLCanvasElement>) => {\n if (!isActive || !fabricRef.current) return;\n const fc = fabricRef.current;\n const rect = canvasElRef.current?.getBoundingClientRect();\n if (!rect) return;\n\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n if (activeTool === 'text') {\n const newElement = createDefaultElement({\n id: `text-${Date.now()}`,\n type: 'text',\n name: 'Text',\n left: x,\n top: y,\n width: 200,\n height: 32,\n fill: '#1a1a1a',\n text: 'Type something...',\n fontSize: 16,\n fontFamily: 'Open Sans',\n fontWeight: 400,\n textAlign: 'left',\n });\n addElement(newElement);\n setActiveTool('select');\n }\n },\n [isActive, activeTool, addElement, setActiveTool]\n );\n\n // Handle click/mousedown on wrapper to activate page\n // Using mousedown for more reliable activation before Fabric.js processes the event\n const handleWrapperMouseDown = useCallback((e: React.MouseEvent) => {\n if (!isActive && onActivate) {\n e.stopPropagation();\n onActivate();\n }\n }, [isActive, onActivate]);\n\n // Fabric.js creates a wrapper div with upper-canvas and lower-canvas\n // We need to control pointer events on the wrapper level\n useEffect(() => {\n const fc = fabricRef.current;\n if (!fc) return;\n \n const wrapperEl = fc.wrapperEl;\n if (wrapperEl) {\n wrapperEl.style.pointerEvents = isActive ? 'auto' : 'none';\n // In preview mode, allow vertical scrolling to pass through to parent\n if (isPreviewMode) {\n wrapperEl.style.touchAction = 'pan-y';\n }\n }\n // Also set touch-action on the upper canvas so Fabric doesn't block scrolling in preview\n const upperCanvas = wrapperEl?.querySelector('.upper-canvas') as HTMLElement;\n if (upperCanvas && isPreviewMode) {\n upperCanvas.style.touchAction = 'pan-y';\n }\n }, [isActive, isPreviewMode]);\n\n const zoom = workspaceZoom || 1;\n const scaledWidth = canvasWidth * zoom;\n const scaledHeight = canvasHeight * zoom;\n\n // Page background only (group bgs are drawn as part of fabric.Group sections)\n const pageTreeForUnderlay = (isPreviewMode && pageChildren?.length) ? (pageChildren ?? []) : (currentPage?.children ?? []);\n\n // Groups are logical only (multiselect); no sections overlay\n const sectionsOverlay = useMemo(() => null, []);\n\n const groupBoundsOverlay = useMemo(() => null, []);\n\n return (\n <div \n ref={groupOverlayWrapperRef}\n className=\"relative\" \n style={{ width: scaledWidth, height: scaledHeight }}\n onMouseDown={handleWrapperMouseDown}\n >\n {/* Underlay: page bg only; section bgs are part of fabric.Group on canvas */}\n <div\n className=\"absolute inset-0 pointer-events-none\"\n style={{\n width: canvasWidth,\n height: canvasHeight,\n transform: `scale(${zoom})`,\n transformOrigin: '0 0',\n }}\n >\n <div\n className=\"absolute inset-0\"\n style={{ \n left: 0, top: 0, width: canvasWidth, height: canvasHeight, \n backgroundColor: pageSettings.backgroundColor || '#ffffff',\n ...(pageSettings.backgroundGradient && isGradientConfig(pageSettings.backgroundGradient) ? {\n background: (() => {\n const g = pageSettings.backgroundGradient;\n const stopsStr = g.stops.map(s => `${s.color} ${Math.round(s.offset * 100)}%`).join(', ');\n if (g.type === 'linear') return `linear-gradient(${g.angle ?? 90}deg, ${stopsStr})`;\n if (g.type === 'radial') return `radial-gradient(circle at ${(g.cx ?? 0.5) * 100}% ${(g.cy ?? 0.5) * 100}%, ${stopsStr})`;\n if (g.type === 'conic') return `conic-gradient(from ${g.angle ?? 0}deg at ${(g.cx ?? 0.5) * 100}% ${(g.cy ?? 0.5) * 100}%, ${stopsStr})`;\n return undefined;\n })(),\n } : {}),\n }}\n />\n </div>\n <canvas\n ref={canvasElRef}\n onClick={handleCanvasClick}\n />\n \n {/* Sections highlight overlay */}\n {sectionsOverlay}\n\n {/* Group bounds + resize overlay (when a group is selected; independent of show sections) */}\n {groupBoundsOverlay}\n \n {/* Snap guides overlay - scaled to match canvas zoom */}\n {guides.length > 0 && (\n <svg\n className=\"absolute inset-0 pointer-events-none\"\n style={{ width: scaledWidth, height: scaledHeight }}\n viewBox={`0 0 ${canvasWidth} ${canvasHeight}`}\n >\n {/* Deduplicate guides by position and type */}\n {(() => {\n const seen = new Set<string>();\n return guides.filter((guide) => {\n const key = `${guide.type}-${guide.position.toFixed(1)}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n }).map((guide, i) => {\n // Use element-relative extents if available, otherwise full canvas\n const isElementRelative = guide.start !== undefined && guide.end !== undefined;\n \n const isActive = guide.active === true;\n const strokeColor = isActive ? '#22c55e' : (isElementRelative ? '#f43f5e' : '#3b82f6');\n const strokeWidth = isActive ? 2.5 : 1;\n const strokeDasharray = isActive ? 'none' : (isElementRelative ? '3,3' : '4,4');\n\n const showDistance = typeof guide.distance === 'number';\n const labelText = showDistance ? String(Math.round(guide.distance)) : '';\n const labelW = Math.max(24, labelText.length * 7);\n\n if (guide.type === 'vertical') {\n const padding = isElementRelative || isActive ? 20 : 0;\n const y1 = (guide.start != null && guide.end != null) ? Math.max(0, guide.start - padding) : 0;\n const y2 = (guide.start != null && guide.end != null) ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;\n const labelY = (guide.start != null && guide.end != null) ? (guide.start + guide.end) / 2 : canvasHeight / 2;\n\n return (\n <g key={i}>\n <line x1={guide.position} y1={y1} x2={guide.position} y2={y2} stroke={strokeColor} strokeWidth={strokeWidth} strokeDasharray={strokeDasharray} />\n {isActive && <circle cx={guide.position} cy={y2} r={4} fill=\"#22c55e\" opacity={0.9} />}\n {isElementRelative && !isActive && (\n <>\n <circle cx={guide.position} cy={y1} r={2} fill=\"#f43f5e\" />\n <circle cx={guide.position} cy={y2} r={2} fill=\"#f43f5e\" />\n </>\n )}\n {showDistance && (\n <g transform={`translate(${guide.position + 6}, ${labelY})`}>\n <rect x={-2} y={-9} width={labelW} height={16} rx={4} fill=\"rgba(0,0,0,0.75)\" />\n <text x={labelW / 2 - 2} y={4} textAnchor=\"middle\" fill=\"#fff\" fontSize={10} fontFamily=\"system-ui, sans-serif\" fontWeight={500}>{labelText}</text>\n </g>\n )}\n </g>\n );\n } else {\n const padding = isElementRelative || isActive ? 20 : 0;\n const x1 = (guide.start != null && guide.end != null) ? Math.max(0, guide.start - padding) : 0;\n const x2 = (guide.start != null && guide.end != null) ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;\n const labelX = (guide.start != null && guide.end != null) ? (guide.start + guide.end) / 2 : canvasWidth / 2;\n\n return (\n <g key={i}>\n <line x1={x1} y1={guide.position} x2={x2} y2={guide.position} stroke={strokeColor} strokeWidth={strokeWidth} strokeDasharray={strokeDasharray} />\n {isActive && <circle cx={x2} cy={guide.position} r={4} fill=\"#22c55e\" opacity={0.9} />}\n {isElementRelative && !isActive && (\n <>\n <circle cx={x1} cy={guide.position} r={2} fill=\"#f43f5e\" />\n <circle cx={x2} cy={guide.position} r={2} fill=\"#f43f5e\" />\n </>\n )}\n {showDistance && (\n <g transform={`translate(${labelX}, ${guide.position - 10})`}>\n <rect x={-2} y={-9} width={labelW} height={16} rx={4} fill=\"rgba(0,0,0,0.75)\" />\n <text x={labelW / 2 - 2} y={4} textAnchor=\"middle\" fill=\"#fff\" fontSize={10} fontFamily=\"system-ui, sans-serif\" fontWeight={500}>{labelText}</text>\n </g>\n )}\n </g>\n );\n }\n });\n })()}\n </svg>\n )}\n\n {/* Grid resize label: cols × rows while dragging grid group handle */}\n {gridResizeLabel && (\n <svg\n className=\"absolute inset-0 pointer-events-none\"\n style={{ width: scaledWidth, height: scaledHeight }}\n viewBox={`0 0 ${canvasWidth} ${canvasHeight}`}\n >\n <g transform={`translate(${gridResizeLabel.x}, ${gridResizeLabel.y})`}>\n <defs>\n <filter id=\"grid-label-shadow\" x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">\n <feDropShadow dx={0} dy={1} stdDeviation={2} floodColor=\"#000\" floodOpacity={0.25} />\n </filter>\n </defs>\n <rect\n x={-36}\n y={-14}\n width={72}\n height={28}\n rx={14}\n ry={14}\n fill=\"rgba(34, 197, 94, 0.95)\"\n stroke=\"rgba(255,255,255,0.9)\"\n strokeWidth={1.5}\n filter=\"url(#grid-label-shadow)\"\n />\n <text\n textAnchor=\"middle\"\n dominantBaseline=\"central\"\n fill=\"white\"\n fontSize={13}\n fontWeight={600}\n fontFamily=\"system-ui, -apple-system, sans-serif\"\n >\n {gridResizeLabel.cols} × {gridResizeLabel.rows}\n </text>\n </g>\n </svg>\n )}\n </div>\n );\n }\n);\n\nPageCanvas.displayName = 'PageCanvas';\n","/**\n * PreviewCanvas - Unified preview component using PageCanvas\n * \n * Uses the same PageCanvas as the builder but in preview mode,\n * ensuring consistent rendering and behavior.\n */\n\nimport { useRef, useMemo, useCallback, useEffect, useState } from 'react';\nimport { PageCanvas, PageCanvasRef, ElementBounds } from '@/components/editor/PageCanvas';\nimport { \n TemplateConfig, \n CanvasElement, \n CanvasNode,\n flattenChildren,\n updateNodeInTree,\n PageSettings,\n ProjectSettings,\n DynamicField,\n} from '@/types/editor';\nimport { reflowPageFromRoot } from '@/lib/layoutEngine';\nimport { preloadImages, getProxiedImageUrl } from '@/lib/canvasImageLoader';\n\ninterface PreviewCanvasProps {\n config: TemplateConfig;\n pageIndex?: number;\n zoom?: number;\n /** When true, zoom is used as-is without auto-fit calculation */\n absoluteZoom?: boolean;\n className?: string;\n onDynamicFieldClick?: (elementId: string, fieldId: string) => void;\n /** Called when the canvas has fully loaded fonts and is ready to display */\n onReady?: () => void;\n}\n\ninterface FieldInfo {\n fieldId: string;\n label: string;\n}\n\n/**\n * Build a map of elementId -> field info for quick lookup\n * Handles both old format (elementIds) and new format (mappings)\n */\nfunction buildElementToFieldMap(dynamicFields: DynamicField[] | undefined): Map<string, FieldInfo> {\n const map = new Map<string, FieldInfo>();\n if (!dynamicFields) return map;\n \n for (const field of dynamicFields) {\n // New format: field.mappings array\n if (field.mappings && Array.isArray(field.mappings)) {\n for (const mapping of field.mappings) {\n if (mapping.elementId) {\n map.set(mapping.elementId, {\n fieldId: field.id,\n label: field.label,\n });\n }\n }\n }\n // Old format: field.elementIds array (backward compatibility)\n else if ((field as any).elementIds && Array.isArray((field as any).elementIds)) {\n for (const elementId of (field as any).elementIds) {\n if (elementId) {\n map.set(elementId, {\n fieldId: field.id,\n label: field.label,\n });\n }\n }\n }\n }\n return map;\n}\n\nexport function PreviewCanvas({\n config,\n pageIndex = 0,\n zoom = 1,\n absoluteZoom = false,\n className,\n onDynamicFieldClick,\n onReady,\n}: PreviewCanvasProps) {\n const canvasRef = useRef<PageCanvasRef>(null);\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerWidth, setContainerWidth] = useState(0);\n const [hoveredFieldId, setHoveredFieldId] = useState<string | null>(null);\n \n // Get page data\n const page = config?.pages?.[pageIndex];\n const canvasWidth = config?.canvas?.width || 612;\n const canvasHeight = config?.canvas?.height || 792;\n\n // Build element to field mapping\n const elementToFieldMap = useMemo(() => \n buildElementToFieldMap(config?.dynamicFields),\n [config?.dynamicFields]\n );\n \n // Get all dynamic field element IDs\n const dynamicFieldIds = useMemo(() => \n Array.from(elementToFieldMap.keys()),\n [elementToFieldMap]\n );\n\n // Run layout (reflow) on page tree so auto/inherit dimensions and flex/grid positions match the builder.\n // Always reflow when children change so that when form data updates text, text is measured and elements below move (auto layout).\n const laidOutPageChildren = useMemo((): CanvasNode[] => {\n if (!page?.children?.length) return [];\n const children = JSON.parse(JSON.stringify(page.children)) as CanvasNode[];\n const allUpdates = reflowPageFromRoot(children);\n if (allUpdates.size === 0) return children;\n let updated = children;\n allUpdates.forEach((u, nodeId) => {\n const dims: Partial<CanvasElement> & Record<string, unknown> = {};\n if (u.width !== undefined) dims.width = u.width;\n if (u.height !== undefined) dims.height = u.height;\n if (Object.keys(dims).length > 0) updated = updateNodeInTree(updated, nodeId, dims);\n });\n allUpdates.forEach((u, nodeId) => {\n const pos: Partial<CanvasElement> & Record<string, unknown> = {};\n if (u.left !== undefined) pos.left = u.left;\n if (u.top !== undefined) pos.top = u.top;\n if (Object.keys(pos).length > 0) updated = updateNodeInTree(updated, nodeId, pos);\n });\n return updated;\n }, [page?.children, page?.id, config?.layoutEngineDriven]);\n\n // Flatten elements from laid-out page (so positions/sizes match builder)\n const elements = useMemo(() => {\n if (!laidOutPageChildren.length) {\n return [];\n }\n return flattenChildren(laidOutPageChildren).filter(el => el.visible !== false);\n }, [laidOutPageChildren]);\n\n // Preload all image URLs so they're in browser memory before Fabric requests them\n useEffect(() => {\n const urls: string[] = [];\n for (const el of elements) {\n const src = el.src || el.imageUrl;\n if (src && !src.startsWith('data:')) {\n urls.push(getProxiedImageUrl(src));\n }\n }\n if (urls.length > 0) {\n preloadImages(urls);\n }\n }, [elements]);\n \n // Page settings\n const pageSettings: PageSettings = useMemo(() => ({\n backgroundColor: page?.settings?.backgroundColor || '#ffffff',\n backgroundGradient: page?.settings?.backgroundGradient,\n }), [page?.settings?.backgroundColor, page?.settings?.backgroundGradient]);\n \n // Project settings (minimal for preview)\n const projectSettings: ProjectSettings = useMemo(() => ({\n showGrid: false,\n snapToGrid: false,\n gridSize: 10,\n snapToGuides: false,\n snapThreshold: 5,\n }), []);\n \n // Handle dynamic field click\n const handleDynamicFieldClick = useCallback((elementId: string) => {\n const fieldInfo = elementToFieldMap.get(elementId);\n if (fieldInfo && onDynamicFieldClick) {\n onDynamicFieldClick(elementId, fieldInfo.fieldId);\n }\n }, [elementToFieldMap, onDynamicFieldClick]);\n \n // Get dynamic field elements with their positions for overlay\n // CRITICAL: Filter to avoid duplicates - only show overlay for elements that are actually dynamic fields\n const dynamicFieldElements = useMemo(() => {\n const result: Array<{ element: CanvasElement; label: string }> = [];\n const seenIds = new Set<string>();\n for (const element of elements) {\n // Skip if we've already added this element (prevent duplicates)\n if (seenIds.has(element.id)) continue;\n const fieldInfo = elementToFieldMap.get(element.id);\n if (fieldInfo) {\n seenIds.add(element.id);\n result.push({ element, label: fieldInfo.label });\n }\n }\n return result;\n }, [elements, elementToFieldMap]);\n \n // Track actual element bounds from the Fabric canvas (for accurate overlay positioning)\n const [actualBounds, setActualBounds] = useState<Map<string, ElementBounds>>(new Map());\n \n // Update actual bounds after canvas renders\n const updateBounds = useCallback(() => {\n if (canvasRef.current?.getAllElementBounds) {\n const bounds = canvasRef.current.getAllElementBounds();\n setActualBounds(bounds);\n }\n }, []);\n \n // Update bounds when elements change (after a short delay to let canvas render)\n useEffect(() => {\n const timer = setTimeout(updateBounds, 100);\n return () => clearTimeout(timer);\n }, [elements, updateBounds]);\n \n // Calculate scale to fit container\n const fitScale = useMemo(() => {\n if (!containerWidth || !canvasWidth) return 1;\n return Math.min(containerWidth / canvasWidth, 1);\n }, [containerWidth, canvasWidth]);\n \n // When absoluteZoom is true, use zoom directly without fitScale multiplication\n const scale = absoluteZoom ? zoom : fitScale * zoom;\n \n // Observe container size\n useEffect(() => {\n if (!containerRef.current) return;\n const w = containerRef.current.clientWidth;\n const resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n setContainerWidth(entry.contentRect.width);\n }\n });\n resizeObserver.observe(containerRef.current);\n setContainerWidth(w);\n return () => resizeObserver.disconnect();\n }, [canvasWidth]);\n \n if (!page) return null;\n \n return (\n <div \n ref={containerRef} \n className={className}\n style={{ width: '100%', overflow: 'hidden' }}\n >\n {/* Mount when width is known, or immediately in absoluteZoom mode (where scale doesn't depend on container width). */}\n {(containerWidth > 0 || absoluteZoom) && (\n <div\n style={{\n width: canvasWidth * scale,\n height: canvasHeight * scale,\n boxShadow: '0 2px 8px rgba(0,0,0,0.1)',\n position: 'relative',\n }}\n >\n {/* PageCanvas now uses viewport transform for crisp zoom */}\n <PageCanvas\n ref={canvasRef}\n pageId={page.id || `page-${pageIndex}`}\n elements={elements}\n pageChildren={laidOutPageChildren}\n pageSettings={pageSettings}\n projectSettings={projectSettings}\n canvasWidth={canvasWidth}\n canvasHeight={canvasHeight}\n isActive={true}\n workspaceZoom={scale}\n selectedIds={[]}\n activeTool=\"select\"\n mode=\"preview\"\n dynamicFieldIds={dynamicFieldIds}\n onDynamicFieldClick={handleDynamicFieldClick}\n onReady={onReady}\n />\n \n {/* Dynamic field click overlays - use actual bounds from canvas for accurate positioning */}\n {onDynamicFieldClick && dynamicFieldElements.map(({ element, label }) => {\n // Use actual bounds from Fabric canvas if available, otherwise fall back to element data\n const bounds = actualBounds.get(element.id);\n const isHovered = hoveredFieldId === element.id;\n \n // Bounds are in canvas coordinate space, so we need to apply scale\n let left: number, top: number, width: number, height: number;\n \n if (bounds) {\n // Use actual rendered bounds from canvas (e.g., text that grew in height)\n left = bounds.left * scale;\n top = bounds.top * scale;\n width = bounds.width * scale;\n height = bounds.height * scale;\n } else {\n // Fallback to element data\n left = element.left * scale;\n top = element.top * scale;\n width = Number(element.width) * (element.scaleX || 1) * scale;\n height = Number(element.height) * (element.scaleY || 1) * scale;\n }\n \n return (\n <div\n key={`overlay-${element.id}`}\n className=\"absolute cursor-pointer transition-all duration-150\"\n style={{\n left,\n top,\n width,\n height,\n border: isHovered ? '2px solid rgb(59 130 246)' : 'none',\n backgroundColor: isHovered ? 'rgba(59, 130, 246, 0.1)' : 'transparent',\n borderRadius: 4,\n pointerEvents: 'auto',\n }}\n onMouseEnter={() => setHoveredFieldId(element.id)}\n onMouseLeave={() => setHoveredFieldId(null)}\n onClick={() => {\n const fieldInfo = elementToFieldMap.get(element.id);\n if (fieldInfo && onDynamicFieldClick) {\n onDynamicFieldClick(element.id, fieldInfo.fieldId);\n }\n }}\n >\n {/* Label badge on hover */}\n {isHovered && (\n <div\n className=\"absolute -top-6 left-0 bg-primary text-primary-foreground text-xs px-2 py-0.5 rounded shadow-sm whitespace-nowrap\"\n style={{ fontSize: 10 }}\n >\n {label}\n </div>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n","/**\n * Infer a generic form schema from template.config.dynamicFields + fieldGroups.\n * Works for any template (resume, invoice, certificate, etc.): sections, repeatable entries,\n * min entries (can't delete below that), add/delete driven by template field IDs.\n *\n * Repeatable sections can come from:\n * 1. Tree: a group or element that is a direct child of a vertical stack and has repeatableSection set.\n * 2. Field ID convention: field_<prefix>_<index>_<suffix> or field_<prefix>_<index>.\n */\n\nimport type { DynamicField, FieldGroup } from '@/types/editor';\nimport type { CanvasNode, TemplateConfigPage } from '@/types/editor';\nimport { isGroup, isElement, flattenChildren, isVerticalStackLayoutMode } from '@/types/editor';\n\nexport type InferredFieldType = 'text' | 'email' | 'tel' | 'textarea' | 'date' | 'url' | 'number' | 'toggle' | 'list' | 'color' | 'image';\n\nexport interface InferredFieldDef {\n key: string;\n label: string;\n type: InferredFieldType;\n order: number;\n templateKey: string; // full template key (field id) for single; suffix for repeatable\n placeholder?: string;\n rows?: number;\n}\n\nexport interface InferredSectionSingle {\n id: string;\n label: string;\n order: number;\n type: 'single';\n fields: InferredFieldDef[];\n}\n\nexport interface InferredSectionRepeatable {\n id: string;\n label: string;\n order: number;\n type: 'repeatable';\n templateKeyPrefix: string; // e.g. \"field_exp\"\n minEntries: number;\n maxEntries?: number; // optional cap on entries (form disables \"Add\" when at max)\n entryFields: InferredFieldDef[];\n initialEntryCount: number;\n /** When set, this repeatable is nested inside another repeatable section (render inside parent entries). */\n parentId?: string;\n}\n\nexport type InferredSection = InferredSectionSingle | InferredSectionRepeatable;\n\n// ── Convert FormDefSection[] (from form_schemas table) → InferredSection[] ──\n// Preserves original section IDs so saved form values (keyed by section ID) match.\n\nimport type { FormDefSection, FormDefField } from '@/lib/formSchemasApi';\n\nfunction mapFormDefFieldType(t: FormDefField['type']): InferredFieldType {\n if (['text', 'email', 'tel', 'textarea', 'date', 'url', 'number', 'toggle', 'color', 'image'].includes(t)) return t as InferredFieldType;\n if (t === 'currency') return 'number';\n return 'text';\n}\n\n/**\n * Convert schema sections (from form_schemas table) into InferredSection[].\n * This ensures section IDs match those used when the form was configured/saved.\n *\n * @param schemaSections — sections from FormDefSchema.sections\n * @param repeatableNodeMap — optional map from section label → tree nodeId (from template's form_schema.repeatableSections)\n * Used to populate `treeNodeId` so the preview can find the right template nodes.\n */\nexport function formDefSectionsToInferred(\n schemaSections: FormDefSection[],\n repeatableNodeMap?: Map<string, string>,\n): InferredSection[] {\n const sections: InferredSection[] = [];\n\n function convert(defs: FormDefSection[], parentId?: string): void {\n for (const def of defs) {\n if (def.repeatable) {\n const rawPrefix = def.templateKeyPrefix || def.label.toLowerCase().replace(/\\s+/g, '_');\n const labelPrefix = rawPrefix.startsWith('field_') ? rawPrefix : `field_${rawPrefix}`;\n // Try to find the tree nodeId by label match\n const treeNodeId = repeatableNodeMap?.get(def.label.trim().toLowerCase());\n // Use treeNodeId-based prefix so flattenSectionStateToFormData produces keys\n // that match what applyFormDataToConfig expects (field_<nodeId>_N_<fieldId>)\n const prefix = treeNodeId ? `field_${treeNodeId}` : labelPrefix;\n\n const section: InferredSectionRepeatable = {\n id: def.id,\n label: def.label,\n order: def.order ?? 0,\n type: 'repeatable',\n templateKeyPrefix: prefix,\n minEntries: def.minEntries ?? 1,\n maxEntries: def.maxEntries,\n entryFields: def.fields.map((f, i) => ({\n key: f.key,\n label: f.label,\n type: mapFormDefFieldType(f.type),\n order: f.order ?? i,\n templateKey: f.key,\n placeholder: f.placeholder,\n })),\n initialEntryCount: def.minEntries ?? 1,\n parentId,\n };\n // Store tree nodeId for preview mapping\n if (treeNodeId) (section as any).treeNodeId = treeNodeId;\n\n sections.push(section);\n\n if (def.children?.length) {\n convert(def.children, def.id);\n }\n } else {\n sections.push({\n id: def.id,\n label: def.label,\n order: def.order ?? 0,\n type: 'single',\n fields: def.fields.map((f, i) => ({\n key: f.key,\n label: f.label,\n type: mapFormDefFieldType(f.type),\n order: f.order ?? i,\n templateKey: `field_${f.key}`,\n placeholder: f.placeholder,\n })),\n });\n // Process children of non-repeatable sections (e.g. a single \"Skills\" section with a repeatable \"Skill\" child)\n if (def.children?.length) {\n convert(def.children, def.id);\n }\n }\n }\n }\n\n convert(schemaSections);\n return sections.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n}\n\nconst REPEATABLE_PATTERN = /^field_(.+)_(\\d+)_(.+)$/; // field_exp_1_title\nconst REPEATABLE_LIST_PATTERN = /^field_(.+)_(\\d+)$/; // field_skills_1\n/** Form definition canonical key pattern: field_{prefix}_N_{suffix} (supports deep nesting). */\nconst FORMDEF_REPEATABLE_PATTERN = /^field_(.+)_N_(.+)$/;\n\nfunction parseFormDefCanonicalFieldId(fieldId: string): { path: string[]; suffix: string } | null {\n if (!FORMDEF_REPEATABLE_PATTERN.test(fieldId)) return null;\n const raw = fieldId.replace(/^field_/, '');\n const segments = raw\n .split('_N_')\n .map((s) => s.trim())\n .filter(Boolean);\n if (segments.length < 2) return null;\n return {\n path: segments.slice(0, -1),\n suffix: segments[segments.length - 1],\n };\n}\n\nfunction humanizeToken(token: string): string {\n return token\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction mapDynamicTypeToInferred(t: string): InferredFieldType {\n if (['text', 'email', 'tel', 'textarea', 'date', 'url', 'number', 'toggle', 'color', 'image'].includes(t)) return t as InferredFieldType;\n if (t === 'currency') return 'number';\n return 'text';\n}\n\n/** Repeatable section node info from the tree. */\ninterface RepeatableNodeInfo {\n nodeId: string;\n label: string;\n minEntries?: number;\n maxEntries?: number;\n parentNodeId?: string;\n}\n\n/**\n * Walk the tree and build:\n * 1. List of repeatable section nodes (direct children of a stack with repeatableSection set).\n * 2. Map: elementId -> nodeId of the innermost repeatable section that contains that element (direct or nested).\n */\nfunction findRepeatableNodesAndElementContainment(pages: TemplateConfigPage[]): {\n repeatableNodes: RepeatableNodeInfo[];\n elementIdToRepeatableNodeId: Map<string, string>;\n} {\n const repeatableNodes: RepeatableNodeInfo[] = [];\n const repeatableNodeIds = new Set<string>();\n const elementIdToRepeatableNodeId = new Map<string, string>();\n let order = 0;\n\n function walk(\n children: CanvasNode[],\n repeatableStack: string[],\n parentRepeatableNodeId?: string\n ): void {\n if (!Array.isArray(children)) return;\n for (const node of children) {\n if (isElement(node)) {\n if (repeatableStack.length > 0) {\n elementIdToRepeatableNodeId.set(node.id, repeatableStack[repeatableStack.length - 1]);\n }\n continue;\n }\n if (!isGroup(node)) continue;\n const nodeChildren = node.children;\n if (!Array.isArray(nodeChildren) || nodeChildren.length === 0) continue;\n\n const selfRep = (node as CanvasNode & { repeatableSection?: { label: string; minEntries?: number; maxEntries?: number } }).repeatableSection;\n if (selfRep?.label && !repeatableNodeIds.has(node.id)) {\n repeatableNodeIds.add(node.id);\n repeatableNodes.push({\n nodeId: node.id,\n label: selfRep.label,\n minEntries: selfRep.minEntries,\n maxEntries: selfRep.maxEntries,\n parentNodeId: parentRepeatableNodeId,\n });\n repeatableStack.push(node.id);\n elementIdToRepeatableNodeId.set(node.id, node.id);\n walk(nodeChildren, repeatableStack, node.id);\n repeatableStack.pop();\n continue;\n }\n\n if (isVerticalStackLayoutMode(node.layoutMode)) {\n for (const child of nodeChildren) {\n const rep = (child as CanvasNode & { repeatableSection?: { label: string; minEntries?: number; maxEntries?: number } }).repeatableSection;\n const childChildren = isGroup(child) ? (child as CanvasNode & { children?: unknown }).children : undefined;\n if (rep?.label) {\n const dedupeKey = parentRepeatableNodeId != null ? `${parentRepeatableNodeId}_${child.id}` : child.id;\n if (!repeatableNodeIds.has(dedupeKey)) {\n repeatableNodeIds.add(dedupeKey);\n repeatableNodes.push({\n nodeId: child.id,\n label: rep.label,\n minEntries: rep.minEntries,\n maxEntries: rep.maxEntries,\n parentNodeId: parentRepeatableNodeId,\n });\n }\n repeatableStack.push(child.id);\n elementIdToRepeatableNodeId.set(child.id, child.id);\n if (Array.isArray(childChildren)) walk(childChildren as CanvasNode[], repeatableStack, child.id);\n repeatableStack.pop();\n } else {\n if (Array.isArray(childChildren)) walk(childChildren as CanvasNode[], repeatableStack, parentRepeatableNodeId);\n }\n }\n continue;\n }\n\n walk(nodeChildren, repeatableStack, parentRepeatableNodeId);\n }\n }\n\n for (const page of pages) {\n if (Array.isArray(page.children) && page.children.length > 0) walk(page.children, []);\n }\n\n return { repeatableNodes, elementIdToRepeatableNodeId };\n}\n\nexport function inferFormSchemaFromTemplate(\n dynamicFields: DynamicField[],\n fieldGroups: FieldGroup[],\n options?: { pages?: TemplateConfigPage[] }\n): InferredSection[] {\n const sections: InferredSection[] = [];\n const sortedGroups = [...(fieldGroups || [])].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // Tree-based repeatable sections: use actual structure – which elements sit inside which repeatable node (direct or nested).\n // Order them by field group order (match by label) so Use page section order matches Fields tab.\n const fieldIdsInTreeRepeatable = new Set<string>();\n if (options?.pages?.length) {\n const { repeatableNodes, elementIdToRepeatableNodeId } = findRepeatableNodesAndElementContainment(options.pages);\n const labelNorm = (s: string) => String(s).trim().toLowerCase();\n\n for (let idx = 0; idx < repeatableNodes.length; idx++) {\n const { nodeId, label, minEntries: minFromNode, maxEntries: maxFromNode, parentNodeId } = repeatableNodes[idx];\n const entryFields: InferredFieldDef[] = [];\n\n for (const field of dynamicFields) {\n const mappings = field.mappings ?? [];\n const mapsIntoThisNode = mappings.some((m) => {\n if (!m.elementId) return false;\n return elementIdToRepeatableNodeId.get(m.elementId) === nodeId;\n });\n if (mapsIntoThisNode) {\n fieldIdsInTreeRepeatable.add(field.id);\n entryFields.push({\n key: field.id.replace(/^field_/, '') || field.id,\n label: field.label,\n type: mapDynamicTypeToInferred(field.type),\n order: field.order ?? 0,\n templateKey: field.id,\n placeholder: field.placeholder,\n rows: (field as { rows?: number }).rows,\n });\n }\n }\n\n entryFields.sort((a, b) => a.order - b.order);\n if (entryFields.length > 0) {\n const matchingGroup = sortedGroups.find((g) => labelNorm(g.label) === labelNorm(label));\n const sectionOrder = matchingGroup !== undefined ? (matchingGroup.order ?? 0) : 500 + idx;\n sections.push({\n id: nodeId,\n label,\n order: sectionOrder,\n type: 'repeatable',\n templateKeyPrefix: `field_${nodeId}`,\n minEntries: minFromNode !== undefined && minFromNode !== null ? Math.max(0, minFromNode) : 1,\n maxEntries: maxFromNode !== undefined && maxFromNode !== null ? Math.max(1, maxFromNode) : undefined,\n entryFields,\n initialEntryCount: 1,\n parentId: parentNodeId,\n });\n }\n }\n }\n\n for (const group of sortedGroups) {\n const groupFields = dynamicFields\n .filter((f) => f.group === group.id && !fieldIdsInTreeRepeatable.has(f.id))\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n if (groupFields.length === 0) continue;\n\n const withIndexSuffix = groupFields.filter((f) => REPEATABLE_PATTERN.test(f.id));\n const withIndexOnly = groupFields.filter((f) => REPEATABLE_LIST_PATTERN.test(f.id) && !REPEATABLE_PATTERN.test(f.id));\n\n // ── Form definition canonical repeatables (supports deep nesting): field_a_N_b_N_c_N_field ──\n const parsedFormDef = groupFields\n .map((field) => {\n const parsed = parseFormDefCanonicalFieldId(field.id);\n return parsed ? { field, parsed } : null;\n })\n .filter((x): x is { field: DynamicField; parsed: { path: string[]; suffix: string } } => x !== null);\n\n if (parsedFormDef.length > 0) {\n type RepeatableDraft = InferredSectionRepeatable & { depth: number };\n const sectionByPath = new Map<string, RepeatableDraft>();\n\n for (const { field, parsed } of parsedFormDef) {\n for (let depth = 0; depth < parsed.path.length; depth++) {\n const pathParts = parsed.path.slice(0, depth + 1);\n const pathKey = pathParts.join('::');\n if (!sectionByPath.has(pathKey)) {\n const sectionId = `formdef_${pathParts.join('__')}`;\n const parentId = depth > 0 ? `formdef_${pathParts.slice(0, -1).join('__')}` : undefined;\n sectionByPath.set(pathKey, {\n id: sectionId,\n label: depth === 0 ? group.label : humanizeToken(pathParts[depth]),\n order: (group.order ?? 0) + depth * 0.01,\n type: 'repeatable',\n templateKeyPrefix: `field_${pathParts[depth]}`,\n minEntries: 1,\n entryFields: [],\n initialEntryCount: 1,\n parentId,\n depth,\n });\n }\n\n // Attach field to the deepest section of the parsed path.\n if (depth === parsed.path.length - 1) {\n const draft = sectionByPath.get(pathKey)!;\n const fieldKey = parsed.suffix;\n if (!draft.entryFields.some((f) => f.key === fieldKey && f.templateKey === fieldKey)) {\n draft.entryFields.push({\n key: fieldKey,\n label: field.label,\n type: mapDynamicTypeToInferred(field.type),\n order: field.order ?? 0,\n templateKey: fieldKey,\n placeholder: field.placeholder,\n rows: field.rows,\n });\n }\n }\n }\n }\n\n const drafts = [...sectionByPath.values()]\n .map((d) => ({ ...d, entryFields: [...d.entryFields].sort((a, b) => a.order - b.order) }))\n .sort((a, b) => (a.order !== b.order ? a.order - b.order : a.depth - b.depth));\n\n for (const draft of drafts) {\n const { depth: _depth, ...section } = draft;\n sections.push(section);\n }\n\n const parsedFieldIds = new Set(parsedFormDef.map(({ field }) => field.id));\n const nonFormDefFields = groupFields.filter((f) => !parsedFieldIds.has(f.id));\n if (nonFormDefFields.length > 0) {\n sections.push({\n id: `${group.id}_single`,\n label: group.label,\n order: group.order - 0.1,\n type: 'single',\n fields: nonFormDefFields.map((f) => ({\n key: f.id.replace(/^field_/, '') || f.id,\n label: f.label,\n type: mapDynamicTypeToInferred(f.type),\n order: f.order ?? 0,\n templateKey: f.id,\n placeholder: f.placeholder,\n rows: f.rows,\n })),\n });\n }\n\n continue;\n }\n\n\n if (withIndexSuffix.length > 0) {\n // Repeatable: field_exp_1_title, field_exp_1_company, field_exp_2_title, ...\n const byPrefix = new Map<string, { index: number; suffix: string; field: DynamicField }[]>();\n for (const field of groupFields) {\n const m = field.id.match(REPEATABLE_PATTERN);\n if (m) {\n const [, prefix, indexStr, suffix] = m;\n const index = parseInt(indexStr, 10);\n const key = `field_${prefix}`;\n if (!byPrefix.has(key)) byPrefix.set(key, []);\n byPrefix.get(key)!.push({ index, suffix, field });\n }\n }\n const firstPrefix = byPrefix.keys().next().value;\n if (firstPrefix) {\n const entries = byPrefix.get(firstPrefix)!;\n const indices = [...new Set(entries.map((e) => e.index))].sort((a, b) => a - b);\n const suffixOrder = new Map<string, number>();\n entries.forEach((e) => {\n if (!suffixOrder.has(e.suffix)) suffixOrder.set(e.suffix, e.field.order ?? 0);\n });\n const entryFields: InferredFieldDef[] = [...new Set(entries.map((e) => e.suffix))]\n .sort((a, b) => (suffixOrder.get(a) ?? 0) - (suffixOrder.get(b) ?? 0))\n .map((suffix) => {\n const entry = entries.find((e) => e.suffix === suffix)!;\n return {\n key: suffix,\n label: entry.field.label,\n type: mapDynamicTypeToInferred(entry.field.type),\n order: entry.field.order ?? 0,\n templateKey: suffix,\n placeholder: entry.field.placeholder,\n rows: entry.field.rows,\n };\n });\n sections.push({\n id: group.id,\n label: group.label,\n order: group.order,\n type: 'repeatable',\n templateKeyPrefix: firstPrefix,\n minEntries: 1,\n entryFields,\n initialEntryCount: Math.max(1, indices.length),\n });\n }\n continue;\n }\n\n if (withIndexOnly.length > 0) {\n // List-like repeatable: field_skills_1, field_skills_2, field_skills_3\n const byPrefix = new Map<string, DynamicField[]>();\n for (const field of withIndexOnly) {\n const m = field.id.match(REPEATABLE_LIST_PATTERN);\n if (m) {\n const [, prefix] = m;\n const key = `field_${prefix}`;\n if (!byPrefix.has(key)) byPrefix.set(key, []);\n byPrefix.get(key)!.push(field);\n }\n }\n const firstPrefix = byPrefix.keys().next().value;\n if (firstPrefix) {\n const fields = byPrefix.get(firstPrefix)!;\n const count = fields.length;\n const first = fields[0];\n sections.push({\n id: group.id,\n label: group.label,\n order: group.order,\n type: 'repeatable',\n templateKeyPrefix: firstPrefix,\n minEntries: 1,\n entryFields: [\n {\n key: 'item',\n label: first.label.replace(/\\s*\\d+$/, '').trim() || 'Item',\n type: 'text',\n order: 0,\n templateKey: '',\n placeholder: first.placeholder,\n },\n ],\n initialEntryCount: Math.max(1, count),\n });\n }\n continue;\n }\n\n // Single section: field_full_name, field_email, ...\n const fields: InferredFieldDef[] = groupFields.map((f) => ({\n key: f.id.replace(/^field_/, '') || f.id,\n label: f.label,\n type: mapDynamicTypeToInferred(f.type),\n order: f.order ?? 0,\n templateKey: f.id,\n placeholder: f.placeholder,\n rows: f.rows,\n }));\n sections.push({\n id: group.id,\n label: group.label,\n order: group.order,\n type: 'single',\n fields,\n });\n }\n\n // Ungrouped fields → one \"Other\" single section (exclude fields that belong to tree repeatable sections)\n const ungrouped = dynamicFields.filter(\n (f) => !fieldIdsInTreeRepeatable.has(f.id) && (!f.group || !fieldGroups.some((g) => g.id === f.group))\n );\n if (ungrouped.length > 0) {\n sections.push({\n id: 'ungrouped',\n label: 'Other',\n order: 999,\n type: 'single',\n fields: ungrouped\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n .map((f) => ({\n key: f.id.replace(/^field_/, '') || f.id,\n label: f.label,\n type: mapDynamicTypeToInferred(f.type),\n order: f.order ?? 0,\n templateKey: f.id,\n placeholder: f.placeholder,\n rows: f.rows,\n })),\n });\n }\n\n return sections.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n}\n\n/** Section form state: single = object, repeatable = array of entries */\nexport type SectionFormState = Record<string, Record<string, string | string[]> | Array<Record<string, string | string[]>>>;\n\n/** Version 2 default_data: store nested sectionState so save/load round-trip is exact; flatten only when applying to template. */\nexport const DEFAULT_DATA_V2_VERSION = 2;\nexport type DefaultDataV2 = { version: 2; sectionState: SectionFormState };\n\nexport function isDefaultDataV2(data: unknown): data is DefaultDataV2 {\n return (\n typeof data === 'object' &&\n data !== null &&\n !Array.isArray(data) &&\n (data as { version?: number }).version === DEFAULT_DATA_V2_VERSION &&\n typeof (data as { sectionState?: unknown }).sectionState === 'object' &&\n (data as { sectionState: unknown }).sectionState !== null\n );\n}\n\nexport function defaultSectionState(sections: InferredSection[]): SectionFormState {\n const state: SectionFormState = {};\n const repeatables = sections.filter((s): s is InferredSectionRepeatable => s.type === 'repeatable');\n const childrenByParent = new Map<string, InferredSectionRepeatable[]>();\n for (const section of repeatables) {\n if (!section.parentId) continue;\n if (!childrenByParent.has(section.parentId)) childrenByParent.set(section.parentId, []);\n childrenByParent.get(section.parentId)!.push(section);\n }\n\n const createEmptyEntry = (section: InferredSectionRepeatable): Record<string, string | string[]> => {\n const entry: Record<string, string | string[]> = {};\n for (const field of section.entryFields) {\n entry[field.key] = field.type === 'list' ? [] : field.type === 'toggle' ? 'false' : '';\n }\n return entry;\n };\n\n const createInitialEntries = (section: InferredSectionRepeatable): Array<Record<string, string | string[]>> => {\n const count = Math.max(1, section.initialEntryCount || 1);\n return Array.from({ length: count }, () => createEmptyEntry(section));\n };\n\n const seedNested = (parentSection: InferredSectionRepeatable, parentStateKey: string, parentEntryIndex: number): void => {\n const children = childrenByParent.get(parentSection.id) ?? [];\n for (const child of children) {\n const childStateKey = `${parentStateKey}_${parentEntryIndex}_${child.id}`;\n const childEntries = createInitialEntries(child);\n state[childStateKey] = childEntries;\n childEntries.forEach((_, childEntryIndex) => seedNested(child, childStateKey, childEntryIndex));\n }\n };\n\n for (const section of sections) {\n if (section.type !== 'single') continue;\n const obj: Record<string, string | string[]> = {};\n for (const f of section.fields) {\n obj[f.key] = f.type === 'list' ? [] : f.type === 'toggle' ? 'false' : '';\n }\n state[section.id] = obj;\n }\n\n const topLevelRepeatables = repeatables.filter((s) => !s.parentId);\n for (const section of topLevelRepeatables) {\n const entries = createInitialEntries(section);\n state[section.id] = entries;\n entries.forEach((_, entryIndex) => seedNested(section, section.id, entryIndex));\n }\n\n // Seed repeatable sections whose parent is a single (non-repeatable) section.\n // These are direct children of single sections and use their own id as state key (not composite).\n const singleSectionIds = new Set(sections.filter((s) => s.type === 'single').map((s) => s.id));\n for (const section of repeatables) {\n if (section.parentId && singleSectionIds.has(section.parentId)) {\n const entries = createInitialEntries(section);\n state[section.id] = entries;\n entries.forEach((_, entryIndex) => seedNested(section, section.id, entryIndex));\n }\n }\n\n return state;\n}\n\n/** Flatten section state to flat formData (field_id -> value) for applyFormDataToConfig */\nexport function flattenSectionStateToFormData(\n sectionState: SectionFormState,\n sections: InferredSection[]\n): Record<string, unknown> {\n const flat: Record<string, unknown> = {};\n const repeatables = sections.filter((s): s is InferredSectionRepeatable => s.type === 'repeatable');\n const childrenByParent = new Map<string, InferredSectionRepeatable[]>();\n for (const section of repeatables) {\n if (!section.parentId) continue;\n if (!childrenByParent.has(section.parentId)) childrenByParent.set(section.parentId, []);\n childrenByParent.get(section.parentId)!.push(section);\n }\n\n for (const section of sections) {\n if (section.type !== 'single') continue;\n const value = sectionState[section.id];\n if (value === undefined) continue;\n const obj = value as Record<string, string | string[]>;\n for (const f of section.fields) {\n const v = obj[f.key];\n flat[f.templateKey] = v ?? '';\n }\n }\n\n const emitRepeatable = (\n section: InferredSectionRepeatable,\n sectionStateKey: string,\n sectionPrefix: string\n ): void => {\n const entries = sectionState[sectionStateKey] as Array<Record<string, string | string[]>> | undefined;\n if (!Array.isArray(entries)) return;\n const sectionLabel = (section as { label?: string }).label ?? section.id;\n const children = childrenByParent.get(section.id) ?? [];\n\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i];\n const oneBased = i + 1;\n flat[`${sectionPrefix}_${oneBased}_sectionLabel`] = sectionLabel;\n flat[`${sectionPrefix}_${oneBased}_label`] = sectionLabel;\n\n for (const f of section.entryFields) {\n const v = entry[f.key];\n if (f.templateKey) {\n if (f.type === 'list' && Array.isArray(v)) {\n v.forEach((item, j) => {\n flat[`${sectionPrefix}_${oneBased}_${f.templateKey}_${j + 1}`] = String(item ?? '');\n });\n } else {\n flat[`${sectionPrefix}_${oneBased}_${f.templateKey}`] = v ?? '';\n }\n } else {\n flat[`${sectionPrefix}_${oneBased}`] = v ?? '';\n }\n }\n\n for (const child of children) {\n const childToken = child.templateKeyPrefix.startsWith('field_')\n ? child.templateKeyPrefix.slice(6)\n : child.templateKeyPrefix;\n const childStateKey = `${sectionStateKey}_${i}_${child.id}`;\n const childPrefix = `${sectionPrefix}_${oneBased}_${childToken}`;\n emitRepeatable(child, childStateKey, childPrefix);\n }\n }\n };\n\n // Emit top-level repeatables AND repeatables whose parent is a \"single\" section\n // (single parents don't go through emitRepeatable, so their children would be missed)\n const singleSectionIds = new Set(sections.filter((s) => s.type === 'single').map((s) => s.id));\n const effectiveTopLevel = repeatables.filter((s) => !s.parentId || singleSectionIds.has(s.parentId));\n for (const section of effectiveTopLevel) {\n emitRepeatable(section, section.id, section.templateKeyPrefix);\n }\n\n return flat;\n}\n\n/** Build section state from flat formData (e.g. from template elements on load). Pre-fills form so it matches preview. */\nexport function formDataToSectionState(\n flatFormData: Record<string, unknown>,\n sections: InferredSection[]\n): SectionFormState {\n const state: SectionFormState = {};\n\n const repeatables = sections.filter((s): s is InferredSectionRepeatable => s.type === 'repeatable');\n const childrenByParent = new Map<string, InferredSectionRepeatable[]>();\n for (const section of repeatables) {\n if (!section.parentId) continue;\n if (!childrenByParent.has(section.parentId)) childrenByParent.set(section.parentId, []);\n childrenByParent.get(section.parentId)!.push(section);\n }\n\n const escapeRegex = (v: string) => v.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n const buildEntriesForPrefix = (\n section: InferredSectionRepeatable,\n prefix: string\n ): Array<Record<string, string | string[]>> => {\n const hasEntrySuffixes = section.entryFields.some((f) => f.templateKey);\n\n if (hasEntrySuffixes) {\n const indexSet = new Set<number>();\n const prefixRegex = new RegExp(`^${escapeRegex(prefix)}_(\\\\d+)_`);\n for (const key of Object.keys(flatFormData)) {\n const match = key.match(prefixRegex);\n if (match) indexSet.add(parseInt(match[1], 10));\n }\n const indices = [...indexSet].sort((a, b) => a - b);\n if (indices.length === 0) indices.push(1);\n\n return indices.map((oneBased) => {\n const entry: Record<string, string | string[]> = {};\n for (const f of section.entryFields) {\n if (f.templateKey) {\n if (f.type === 'list') {\n const listKeys = Object.keys(flatFormData).filter((k) =>\n k.startsWith(`${prefix}_${oneBased}_${f.templateKey}_`) && /_\\d+$/.test(k)\n );\n entry[f.key] = listKeys\n .sort((a, b) => {\n const na = parseInt(a.split('_').pop()!, 10);\n const nb = parseInt(b.split('_').pop()!, 10);\n return na - nb;\n })\n .map((k) => String(flatFormData[k] ?? ''));\n } else {\n const v = flatFormData[`${prefix}_${oneBased}_${f.templateKey}`];\n entry[f.key] =\n f.type === 'toggle' ? (v === true || v === 'true' ? 'true' : 'false') : String(v ?? '');\n }\n } else {\n const v = flatFormData[`${prefix}_${oneBased}`];\n entry[f.key] = String(v ?? '');\n }\n }\n return entry;\n });\n }\n\n const listKeyRegex = new RegExp(`^${escapeRegex(prefix)}_(\\\\d+)$`);\n const indices = Object.keys(flatFormData)\n .map((k) => k.match(listKeyRegex))\n .filter((m): m is RegExpMatchArray => m !== null)\n .map((m) => parseInt(m[1], 10))\n .filter((n) => !Number.isNaN(n))\n .sort((a, b) => a - b);\n\n if (indices.length === 0) {\n return [{ [section.entryFields[0]?.key ?? 'item']: '' }];\n }\n\n return indices.map((oneBased) => {\n const entry: Record<string, string | string[]> = {};\n for (const f of section.entryFields) {\n entry[f.key] = String(flatFormData[`${prefix}_${oneBased}`] ?? '');\n }\n return entry;\n });\n };\n\n const parseRepeatable = (\n section: InferredSectionRepeatable,\n sectionStateKey: string,\n sectionPrefix: string\n ): void => {\n const entries = buildEntriesForPrefix(section, sectionPrefix);\n state[sectionStateKey] = entries;\n const children = childrenByParent.get(section.id) ?? [];\n\n entries.forEach((_, entryIndex) => {\n const oneBased = entryIndex + 1;\n for (const child of children) {\n const childToken = child.templateKeyPrefix.startsWith('field_')\n ? child.templateKeyPrefix.slice(6)\n : child.templateKeyPrefix;\n const childPrefix = `${sectionPrefix}_${oneBased}_${childToken}`;\n const childStateKey = `${sectionStateKey}_${entryIndex}_${child.id}`;\n parseRepeatable(child, childStateKey, childPrefix);\n }\n });\n };\n\n for (const section of sections) {\n if (section.type === 'single') {\n const obj: Record<string, string | string[]> = {};\n for (const f of section.fields) {\n const v = flatFormData[f.templateKey];\n obj[f.key] = f.type === 'list' ? (Array.isArray(v) ? v.map(String) : []) : f.type === 'toggle' ? (v === true || v === 'true' ? 'true' : 'false') : String(v ?? '');\n }\n state[section.id] = obj;\n }\n }\n\n const topLevelRepeatables = repeatables.filter((s) => !s.parentId);\n for (const section of topLevelRepeatables) {\n parseRepeatable(section, section.id, section.templateKeyPrefix);\n }\n\n return state;\n}\n","/**\n * Form schema (middle layer): dynamicFields, fieldGroups, repeatableSections.\n * Stored separately from config (structure). Used for load/merge and build from canvas.\n */\n\nimport type { TemplateConfig, TemplateConfigPage, CanvasNode } from '@/types/editor';\nimport { isGroup, isVerticalStackLayoutMode } from '@/types/editor';\nimport type { FormSchema, FormRenderPreset } from '@/lib/api';\n\nconst CLONE_SUFFIX = /_\\d+$/;\n\n/** Strip trailing _1, _2, etc. to get the base id (e.g. text-xxx_1_1_1 -> text-xxx). Use for storing clean ids. */\nexport function baseId(id: string): string {\n let s = id;\n while (CLONE_SUFFIX.test(s)) s = s.replace(CLONE_SUFFIX, '');\n return s;\n}\n\n/** Build form_schema from current canvas for persisting the Schema layer. Bindings (mappings) live in dynamicFields. */\nexport function buildFormSchemaFromCanvas(\n canvas: {\n dynamicFields: unknown[];\n fieldGroups: unknown[];\n pages: TemplateConfigPage[];\n formRenderPreset?: string;\n },\n options?: { defaultData?: Record<string, unknown> | null }\n): FormSchema {\n const repeatableSections: { nodeId: string; label: string; minEntries?: number; maxEntries?: number }[] = [];\n\n function walk(children: CanvasNode[]) {\n for (const node of children) {\n const rep = (node as CanvasNode & { repeatableSection?: { label: string; minEntries?: number; maxEntries?: number } }).repeatableSection;\n if (rep?.label) {\n repeatableSections.push({\n nodeId: node.id,\n label: rep.label,\n ...(rep.minEntries !== undefined && rep.minEntries !== null ? { minEntries: rep.minEntries } : {}),\n ...(rep.maxEntries !== undefined && rep.maxEntries !== null ? { maxEntries: rep.maxEntries } : {}),\n });\n }\n if (isGroup(node) && node.children?.length) walk(node.children);\n }\n }\n for (const page of canvas.pages ?? []) {\n if (page.children?.length) walk(page.children);\n }\n\n // Do not infer repeatable sections from clone IDs (e.g. group-xxx_1, group-xxx_2). Only nodes with\n // explicit repeatableSection are included. Otherwise after saving repeatableSections: [] the Schema\n // JSON would show inferred sections again because clone nodes still exist in the canvas.\n\n const out: NonNullable<FormSchema> = {\n dynamicFields: canvas.dynamicFields ?? [],\n fieldGroups: canvas.fieldGroups ?? [],\n repeatableSections,\n };\n if (options?.defaultData !== undefined) out.defaultData = options.defaultData;\n if (canvas.formRenderPreset) out.formRenderPreset = canvas.formRenderPreset as FormRenderPreset;\n return out;\n}\n\n/** Strip repeatableSection from every node in the tree (mutates in place). */\nfunction stripRepeatableFromNodes(nodes: CanvasNode[] | undefined): void {\n if (!nodes) return;\n for (const node of nodes) {\n delete (node as CanvasNode & { repeatableSection?: unknown }).repeatableSection;\n if (isGroup(node) && node.children?.length) stripRepeatableFromNodes(node.children);\n }\n}\n\n/** Merge form_schema into config (structure): add dynamicFields, fieldGroups, paint repeatableSection on nodes. */\nexport function mergeFormSchemaIntoConfig(\n config: TemplateConfig,\n formSchema: FormSchema | null | undefined\n): TemplateConfig {\n if (!formSchema || (typeof formSchema !== 'object')) return config;\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n cloned.dynamicFields = (formSchema.dynamicFields ?? []) as TemplateConfig['dynamicFields'];\n cloned.fieldGroups = (formSchema.fieldGroups ?? []) as TemplateConfig['fieldGroups'];\n if (formSchema.formRenderPreset) {\n (cloned as TemplateConfig & { formRenderPreset?: FormRenderPreset }).formRenderPreset = formSchema.formRenderPreset;\n }\n const repeatableSections = formSchema.repeatableSections ?? [];\n\n // Always strip repeatableSection from all nodes first, so saving empty list actually clears them\n for (const page of cloned.pages ?? []) {\n if (page.children) stripRepeatableFromNodes(page.children);\n }\n\n const setRepeatable = (nodes: CanvasNode[] | undefined, nodeId: string, section: { label: string; minEntries?: number; maxEntries?: number }): boolean => {\n if (!nodes) return false;\n for (const node of nodes) {\n if (node.id === nodeId || baseId(node.id) === baseId(nodeId)) {\n (node as CanvasNode & { repeatableSection?: { label: string; minEntries?: number; maxEntries?: number } }).repeatableSection = section;\n return true;\n }\n if (isGroup(node) && node.children?.length && setRepeatable(node.children, nodeId, section)) return true;\n }\n return false;\n };\n for (const section of repeatableSections) {\n const { nodeId, label, minEntries, maxEntries } = section;\n const payload = { label, ...(minEntries !== undefined && minEntries !== null ? { minEntries } : {}), ...(maxEntries !== undefined && maxEntries !== null ? { maxEntries } : {}) };\n for (const page of cloned.pages ?? []) {\n if (page.children && setRepeatable(page.children, nodeId, payload)) break;\n }\n }\n return cloned;\n}\n\n/** Normalize repeatableSections nodeIds to base ids only (dynamic field mappings keep full ids to match structure). */\nexport function normalizeFormSchemaToBaseIds(schema: FormSchema): FormSchema {\n const out = JSON.parse(JSON.stringify(schema)) as FormSchema;\n if (out.repeatableSections?.length) {\n out.repeatableSections = out.repeatableSections.map((s) => ({\n ...s,\n nodeId: baseId(s.nodeId),\n }));\n }\n return out;\n}\n\n/** Normalize ids inside repeatable section subtrees to base ids (mutates config). Call before saving structure so we persist clean ids. */\nexport function normalizeRepeatableSubtreesToBaseIds(config: TemplateConfig): void {\n const pages = config.pages ?? [];\n function normalizeNodeId(n: CanvasNode): void {\n (n as { id: string }).id = baseId(n.id);\n if (isGroup(n) && n.children?.length) n.children.forEach(normalizeNodeId);\n }\n function walk(children: CanvasNode[]): void {\n for (const node of children) {\n if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && node.children?.length) {\n for (const child of node.children) {\n const rep = (child as CanvasNode & { repeatableSection?: { label: string } }).repeatableSection;\n if (rep?.label) {\n normalizeNodeId(child);\n }\n }\n for (const child of node.children) {\n if (isGroup(child) && child.children?.length) walk(child.children);\n }\n return;\n }\n if (isGroup(node) && node.children?.length) walk(node.children);\n }\n }\n for (const page of pages) {\n if (page.children?.length) walk(page.children);\n }\n}\n","/**\n * Apply form field values to a template config using a form-template mapping.\n * Returns a new config with element properties updated (e.g. text, src) so preview shows filled data.\n * Supports repeatable sections: expands nodes marked repeatableSection (direct children of stack)\n * using form keys field_<nodeId>_<index>_<fieldId>, then applies values to cloned elements.\n * Runs stack reflow so vertical stack groups shift when text/height changes.\n */\n\nimport type { TemplateConfig, TemplateConfigPage, CanvasNode, DynamicField, CanvasElement } from '@/types/editor';\nimport type { FormTemplateMappingEntry } from '@/lib/api';\nimport { applyStackReflowToPageTree } from '@/lib/layoutEngine';\nimport { isGroup, isElement, isVerticalStackLayoutMode, generateId, PAGE_BACKGROUND_ELEMENT_ID } from '@/types/editor';\nimport { baseId } from '@/lib/formSchema';\nimport { renderSmartElementToDataUri } from '@/lib/smartElements';\n\n/** Form key pattern for repeatable entries: field_<nodeId>_<oneBasedIndex>_<fieldId> */\nconst REPEATABLE_KEY_REGEX = /^field_(.+)_(\\d+)_(.+)$/;\n/** Nested: field_<parentId>_<parentIndex>_field_<childId>_<childIndex>_<fieldId> */\nconst NESTED_REPEATABLE_KEY_REGEX = /^field_(.+)_(\\d+)_field_(.+)_(\\d+)_(.+)$/;\n/** Canonical form key pattern: field_<path>_N_<suffix> (can include multiple _N_) */\nconst FORMDEF_CANONICAL_KEY_REGEX = /^field_.+_N_.+$/;\n\n/** Apply textCase transform to a string value */\nfunction applyTextCase(text: string, textCase?: string): string {\n if (!textCase || textCase === 'none') return text;\n switch (textCase) {\n case 'uppercase': return text.toUpperCase();\n case 'lowercase': return text.toLowerCase();\n case 'capitalize': return text.replace(/(?<!\\S)\\w|(?<=[-])\\w/g, (c) => c.toUpperCase());\n default: return text;\n }\n}\n\nfunction setInTree(nodes: any[], elementId: string, targetProperty: string, value: unknown): boolean {\n for (const node of nodes) {\n if (node.id === elementId) {\n if (targetProperty === 'link') {\n node.linkConfig = { ...(node.linkConfig || {}), url: String(value ?? '') };\n } else if (targetProperty.startsWith('smartProp:') && node.smartElementType) {\n // Smart element property binding: update the specific smart prop and re-render SVG\n const propKey = targetProperty.slice('smartProp:'.length);\n const newSmartProps = { ...(node.smartProps || {}), [propKey]: value };\n node.smartProps = newSmartProps;\n // Re-render SVG data URI for the smart element\n const newSrc = renderSmartElementToDataUri(node.smartElementType, newSmartProps, node.width, node.height);\n if (newSrc) node.src = newSrc;\n } else if (targetProperty === 'text' && node.type === 'text' && typeof value === 'string') {\n // Use a space for empty text so Fabric renders blank instead of showing \"Text\" placeholder\n const textVal = value === '' ? ' ' : value;\n node[targetProperty] = applyTextCase(textVal, node.textCase);\n } else {\n node[targetProperty] = value;\n }\n // When text changes, clear height so layout reflow will measure and siblings below move correctly\n if (targetProperty === 'text' && node.type === 'text') {\n delete node.height;\n }\n // When image src changes, clear stale natural dimensions so the server\n // renderer computes cover layout from the actual loaded image dimensions.\n if ((targetProperty === 'src' || targetProperty === 'imageUrl') && node.type === 'image') {\n delete node.imageNaturalWidth;\n delete node.imageNaturalHeight;\n }\n return true;\n }\n if (node.children && Array.isArray(node.children)) {\n if (setInTree(node.children, elementId, targetProperty, value)) return true;\n }\n }\n return false;\n}\n\n/** Collect all groups whose __baseNodeId or baseId(id) equals baseNodeId, in tree order. */\nfunction collectGroupsByBaseId(pages: TemplateConfigPage[], baseNodeId: string): CanvasNode[] {\n const groups: CanvasNode[] = [];\n function collect(children: CanvasNode[]) {\n if (!Array.isArray(children)) return;\n for (const node of children) {\n const base = (node as CanvasNode & { __baseNodeId?: string }).__baseNodeId ?? baseId(node.id);\n if (base === baseNodeId) groups.push(node);\n if (isGroup(node) && node.children?.length) collect(node.children);\n }\n }\n for (const page of pages) if (page.children?.length) collect(page.children);\n return groups;\n}\n\n/** Find descendant with __sourceId === sourceId or id === sourceId (for unexpanded trees). Also matches by baseId when mapping has base id but clone has suffixed id. */\nfunction findElementBySourceIdInSubtree(nodes: CanvasNode[], sourceId: string): string | undefined {\n const sourceBase = baseId(sourceId);\n for (const node of nodes) {\n const src = (node as CanvasNode & { __sourceId?: string }).__sourceId;\n const nodeId = node.id;\n if (src === sourceId || nodeId === sourceId) return node.id;\n if (sourceBase && (baseId(src ?? nodeId) === sourceBase)) return node.id;\n if (isGroup(node) && node.children?.length) {\n const found = findElementBySourceIdInSubtree(node.children, sourceId);\n if (found) return found;\n }\n }\n return undefined;\n}\n\n/** Find the resolved element id for a repeatable entry when map lookup fails: i-th group with __baseNodeId, then descendant with __sourceId === sourceElementId. */\nfunction findRepeatableElementBySourceId(\n pages: TemplateConfigPage[],\n baseNodeId: string,\n oneBasedIndex: number,\n sourceElementId: string\n): string | undefined {\n const groups = collectGroupsByBaseId(pages, baseNodeId);\n const group = groups[oneBasedIndex - 1];\n if (!group) return undefined;\n if (!isGroup(group) || !group.children?.length) return undefined;\n return findElementBySourceIdInSubtree(group.children, sourceElementId) ?? ((group as CanvasNode & { __sourceId?: string }).__sourceId === sourceElementId ? group.id : undefined);\n}\n\n/** Find element in nested repeatable when map fails: parentPi-th parent group, then childCi-th child group inside it, then descendant with __sourceId. */\nfunction findNestedRepeatableElementBySourceId(\n pages: TemplateConfigPage[],\n parentBaseNodeId: string,\n parentPi: number,\n childBaseNodeId: string,\n childCi: number,\n sourceElementId: string\n): string | undefined {\n const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);\n const parentGroup = parentGroups[parentPi - 1];\n if (!parentGroup || !isGroup(parentGroup) || !parentGroup.children?.length) return undefined;\n const childGroups: CanvasNode[] = [];\n function collectChild(children: CanvasNode[]) {\n for (const node of children) {\n const base = (node as CanvasNode & { __baseNodeId?: string }).__baseNodeId ?? baseId(node.id);\n if (base === childBaseNodeId) childGroups.push(node);\n if (isGroup(node) && node.children?.length) collectChild(node.children);\n }\n }\n collectChild(parentGroup.children);\n const childGroup = childGroups[childCi - 1];\n if (!childGroup || !isGroup(childGroup) || !childGroup.children?.length) return undefined;\n return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? ((childGroup as CanvasNode & { __sourceId?: string }).__sourceId === sourceElementId ? childGroup.id : undefined);\n}\n\n/** Prefix for generateId based on node type. */\nfunction idPrefix(node: CanvasNode): string {\n if (isGroup(node)) return 'group';\n const t = (node as CanvasElement).type;\n if (t === 'text') return 'text';\n if (t === 'image') return 'img';\n if (t === 'shape') return 'shape';\n return 'el';\n}\n\n/** Deep clone a node and every descendant with new unique UUIDs. Sets __baseNodeId and __sourceId on each clone so phase-2 and fallback apply can resolve elements. Returns [clone, map of oldId -> newId]. */\nfunction cloneNodeWithNewIds(node: CanvasNode): [CanvasNode, Map<string, string>] {\n const oldToNew = new Map<string, string>();\n function clone(n: CanvasNode): CanvasNode {\n const newId = generateId(idPrefix(n));\n oldToNew.set(n.id, newId);\n const base = baseId(n.id);\n const withMeta = { ...n, id: newId, __baseNodeId: base, __sourceId: n.id } as CanvasNode & { __baseNodeId?: string; __sourceId?: string };\n if (isGroup(n) && n.children?.length) {\n return { ...withMeta, children: n.children.map(clone) } as CanvasNode;\n }\n return withMeta as CanvasNode;\n }\n const copy = JSON.parse(JSON.stringify(node)) as CanvasNode;\n const cloned = clone(copy);\n return [cloned, oldToNew];\n}\n\ntype RepeatableInfo = { parentChildren: CanvasNode[]; startIndex: number; count: number; node: CanvasNode; baseNodeId: string; parentBaseNodeId?: string };\n\n/** Derive repeatable list from config (legacy: nodes with repeatableSection). Single source of shape { nodeId, label }. */\nfunction getRepeatableFromConfig(pages: TemplateConfigPage[]): { nodeId: string; label: string }[] {\n const result: { nodeId: string; label: string }[] = [];\n function walk(children: CanvasNode[]) {\n if (!children) return;\n for (const node of children) {\n if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && node.children?.length) {\n for (const child of node.children) {\n const rep = (child as CanvasNode & { repeatableSection?: { label: string } }).repeatableSection;\n if (rep?.label) result.push({ nodeId: child.id, label: rep.label });\n }\n }\n if (isGroup(node) && node.children?.length) walk(node.children);\n }\n }\n for (const page of pages) if (page.children?.length) walk(page.children);\n return result;\n}\n\n/** Find repeatable blocks by node id or base id. Supports unexpanded (one node) or expanded (base_1, base_2, ...). Returns one entry per repeatable so we replace the whole block with N clones. parentBaseNodeId = base id of the stack that contains this block (for nested sections). */\nfunction findRepeatableByNodeIds(\n pages: TemplateConfigPage[],\n nodeIds: string[]\n): RepeatableInfo[] {\n const schemaBaseIds = new Set(nodeIds.map((id) => baseId(id)));\n const result: RepeatableInfo[] = [];\n const clonePattern = /^(.+)_(\\d+)$/;\n const hasRepeatableSection = (n: CanvasNode) => !!(n as CanvasNode & { repeatableSection?: { label?: string } }).repeatableSection?.label;\n\n function walk(children: CanvasNode[], parentRepeatableBaseId?: string, parentInfo?: { parentChildren: CanvasNode[]; startIndex: number }) {\n if (!Array.isArray(children)) return;\n for (let i = 0; i < children.length; i++) {\n const node = children[i];\n if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && node.children?.length) {\n const kids = node.children;\n let j = 0;\n while (j < kids.length) {\n const child = kids[j];\n const childBase = baseId(child.id);\n const childBaseFromMeta = (child as { __baseNodeId?: string }).__baseNodeId;\n const isMatch =\n schemaBaseIds.has(child.id) ||\n schemaBaseIds.has(childBase) ||\n (childBaseFromMeta != null && schemaBaseIds.has(childBaseFromMeta));\n if (!isMatch) {\n if (isGroup(child) && Array.isArray(child.children)) walk(child.children, parentRepeatableBaseId);\n j++;\n continue;\n }\n let count = 1;\n const nextBaseFromMeta = (child as { __baseNodeId?: string }).__baseNodeId;\n const effectiveChildBase = nextBaseFromMeta ?? childBase;\n if (child.id !== childBase && child.id !== effectiveChildBase) {\n while (j + count < kids.length) {\n const next = kids[j + count];\n const nextMatch = next.id.match(clonePattern);\n const nextMeta = (next as { __baseNodeId?: string }).__baseNodeId;\n const nextBase = nextMeta ?? (nextMatch ? baseId(nextMatch[1]) : baseId(next.id));\n if (nextBase === effectiveChildBase) count++;\n else break;\n }\n }\n result.push({\n parentChildren: kids,\n startIndex: j,\n count,\n node: child,\n baseNodeId: childBaseFromMeta ?? childBase,\n parentBaseNodeId: parentRepeatableBaseId,\n });\n if (isGroup(child) && Array.isArray(child.children)) walk(child.children, childBase);\n j += count;\n }\n } else if (isGroup(node) && node.children?.length) {\n // Also find repeatable groups that are not direct children of a stack (match by id so inference and apply use same nodes)\n const nodeBase = baseId(node.id);\n const selfMatch =\n schemaBaseIds.has(node.id) ||\n schemaBaseIds.has(nodeBase) ||\n ((node as { __baseNodeId?: string }).__baseNodeId != null && schemaBaseIds.has((node as { __baseNodeId?: string }).__baseNodeId!));\n if (selfMatch && hasRepeatableSection(node)) {\n const parentChildren = parentInfo?.parentChildren ?? children;\n const startIndex = parentInfo ? parentInfo.startIndex : i;\n let count = 1;\n const effectiveBase = (node as { __baseNodeId?: string }).__baseNodeId ?? nodeBase;\n if (node.id !== nodeBase && node.id !== effectiveBase) {\n while (i + count < children.length) {\n const next = children[i + count];\n const nextMatch = next.id.match(clonePattern);\n const nextMeta = (next as { __baseNodeId?: string }).__baseNodeId;\n const nextBase = nextMeta ?? (nextMatch ? baseId(nextMatch[1]) : baseId(next.id));\n if (nextBase === effectiveBase) count++;\n else break;\n }\n }\n result.push({\n parentChildren: children,\n startIndex: i,\n count,\n node,\n baseNodeId: effectiveBase,\n parentBaseNodeId: parentRepeatableBaseId,\n });\n }\n walk(node.children, parentRepeatableBaseId);\n }\n }\n }\n for (const page of pages) if (page.children?.length) walk(page.children);\n return result;\n}\n\n/** Find every group that has repeatableSection (for fallback when nodeIds don't match). Tree order, top-level only. */\nfunction findAllTopLevelRepeatableBlocks(pages: TemplateConfigPage[]): RepeatableInfo[] {\n const result: RepeatableInfo[] = [];\n const hasRepeatableSection = (n: CanvasNode) => !!(n as CanvasNode & { repeatableSection?: { label?: string } }).repeatableSection?.label;\n function walk(children: CanvasNode[]) {\n if (!Array.isArray(children)) return;\n for (let i = 0; i < children.length; i++) {\n const node = children[i];\n if (!isGroup(node) || !node.children?.length) continue;\n if (hasRepeatableSection(node) && !result.some((r) => r.baseNodeId === baseId(node.id))) {\n result.push({\n parentChildren: children,\n startIndex: i,\n count: 1,\n node,\n baseNodeId: baseId(node.id),\n parentBaseNodeId: undefined,\n });\n }\n walk(node.children);\n }\n }\n for (const page of pages) if (page.children?.length) walk(page.children);\n return result;\n}\n\n/** Infer entry count for a repeatable nodeId from form value keys field_<nodeId>_<i>_*. */\nfunction getRepeatableEntryCount(nodeId: string, formValues: Record<string, unknown>): number {\n let maxIndex = 0;\n const prefix = `field_${baseId(nodeId)}_`;\n for (const key of Object.keys(formValues)) {\n if (!key.startsWith(prefix)) continue;\n const rest = key.slice(prefix.length);\n const match = /^(\\d+)_/.exec(rest);\n if (match) maxIndex = Math.max(maxIndex, parseInt(match[1], 10));\n }\n return Math.max(1, maxIndex);\n}\n\n/** Infer max child index for nested: field_<parentId>_<pi>_field_<childId>_<ci>_* */\nfunction getNestedRepeatableEntryCount(parentId: string, parentIndex: number, childId: string, formValues: Record<string, unknown>): number {\n let maxIndex = 0;\n const prefix = `field_${baseId(parentId)}_${parentIndex}_field_${baseId(childId)}_`;\n for (const key of Object.keys(formValues)) {\n if (!key.startsWith(prefix)) continue;\n const rest = key.slice(prefix.length);\n const match = /^(\\d+)(_|$)/.exec(rest);\n if (match) maxIndex = Math.max(maxIndex, parseInt(match[1], 10));\n }\n return Math.max(1, maxIndex);\n}\n\n\n/** Repeatable section from schema or inferred; entryCount from form state so \"Add\" reflects in preview. */\nexport type RepeatableSectionInput = { nodeId: string; label: string; entryCount?: number };\n\n/**\n * Returns a deep clone of config with form values applied per mapping.\n * Repeatable: single code path. List comes from schema when provided, else derived from config (legacy).\n * Always expand via findRepeatableByNodeIds.\n * When repeatable list items have entryCount (from section state), that drives how many clones are created.\n */\nexport function applyFormDataToConfig(\n config: TemplateConfig,\n mappings: FormTemplateMappingEntry[],\n formValues: Record<string, unknown>,\n repeatableSectionsFromSchema?: RepeatableSectionInput[] | { nodeId: string; label: string }[],\n repeatableEntryCounts?: Record<string, number>,\n repeatableNestedEntryCounts?: Record<string, number>,\n /** Map of field key -> inputDisplay type (e.g. 'date', 'time') for formatting values on preview */\n displayFormatMap?: Map<string, string>\n): TemplateConfig {\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n if (!cloned.pages) return cloned;\n\n const dynamicFields = cloned.dynamicFields as DynamicField[] | undefined;\n const pages = cloned.pages;\n\n const repeatableList =\n repeatableSectionsFromSchema?.length\n ? repeatableSectionsFromSchema\n : getRepeatableFromConfig(pages);\n const nodeIds = repeatableList.map((r) => r.nodeId);\n const entryCountFromList = (baseNodeId: string): number | undefined => {\n const item = repeatableList.find((r) => baseId(r.nodeId) === baseNodeId || r.nodeId === baseNodeId);\n return (item as RepeatableSectionInput | undefined)?.entryCount;\n };\n /** Map key `${baseNodeId}_${entryIndex}_${oldElementId}` or same with fieldId (e.g. company_name) for apply; nested: `${parentId}_${pi}_${childId}_${ci}_${oldId}`. */\n const resolvedIdMap = new Map<string, string>();\n const mappedNewIds = new Set<string>();\n /** elementId -> field.id so we can store baseNodeId_i_fieldId in resolvedIdMap for form-key lookup. */\n const elementIdToFieldId = new Map<string, string>();\n if (dynamicFields?.length) {\n for (const f of dynamicFields) {\n const elId = (f.mappings?.[0] as { elementId?: string } | undefined)?.elementId;\n if (elId) elementIdToFieldId.set(elId, f.id);\n }\n }\n\n const repeatableInfosFirst = findRepeatableByNodeIds(pages, nodeIds);\n let topLevel = repeatableInfosFirst.filter((r) => !r.parentBaseNodeId);\n if (topLevel.length === 0 && repeatableList.length > 0) {\n topLevel = findAllTopLevelRepeatableBlocks(pages);\n }\n const nestedFirst = repeatableInfosFirst.filter((r) => r.parentBaseNodeId);\n /** For nested sections we need parent base id when building map keys in phase 2. */\n const nestedChildToParent = new Map<string, string>();\n for (const r of nestedFirst) {\n if (r.parentBaseNodeId) nestedChildToParent.set(r.baseNodeId, r.parentBaseNodeId);\n }\n\n // Phase 1: expand top-level repeatables only (one splice per baseNodeId so already-expanded config is handled).\n // Build phase1NewToOriginal so Phase 2 can resolve template element ids (mappings use original ids; nodes inside clones have new ids).\n const phase1NewToOriginal = new Map<string, Map<string, string>>(); // key: `${parentBaseNodeId}_${i}`, value: newId -> originalId\n const topLevelByBase = new Map<string, RepeatableInfo[]>();\n for (const r of topLevel) {\n if (!topLevelByBase.has(r.baseNodeId)) topLevelByBase.set(r.baseNodeId, []);\n topLevelByBase.get(r.baseNodeId)!.push(r);\n }\n for (const [, blocks] of topLevelByBase) {\n const first = blocks[0];\n const startIndex = Math.min(...blocks.map((b) => b.startIndex));\n const endIndex = Math.max(...blocks.map((b) => b.startIndex + b.count));\n const count = endIndex - startIndex;\n const { parentChildren, node, baseNodeId } = first;\n const countFromList = entryCountFromList(baseNodeId);\n const countFromState =\n countFromList ??\n repeatableEntryCounts?.[baseNodeId] ??\n repeatableEntryCounts?.[node.id] ??\n (repeatableEntryCounts && (() => {\n for (const k of Object.keys(repeatableEntryCounts)) {\n if (baseId(k) === baseNodeId) return repeatableEntryCounts[k];\n }\n return undefined;\n })());\n const N = Math.max(1, countFromState ?? getRepeatableEntryCount(baseNodeId, formValues));\n const clones: CanvasNode[] = [];\n for (let i = 1; i <= N; i++) {\n const [clone, oldToNew] = cloneNodeWithNewIds(node);\n delete (clone as unknown as Record<string, unknown>).repeatableSection;\n clones.push(clone);\n const newToOriginal = new Map<string, string>();\n for (const [oldId, newId] of oldToNew) {\n resolvedIdMap.set(`${baseNodeId}_${i}_${oldId}`, newId);\n newToOriginal.set(newId, oldId);\n const fieldId = elementIdToFieldId.get(oldId);\n if (fieldId) resolvedIdMap.set(`${baseNodeId}_${i}_${fieldId}`, newId);\n mappedNewIds.add(newId);\n }\n phase1NewToOriginal.set(`${baseNodeId}_${i}`, newToOriginal);\n }\n parentChildren.splice(startIndex, count, ...clones);\n }\n\n // Phase 2: if we had nested sections, find blocks again (now one per parent clone) and expand.\n // Nodes we clone here have Phase-1 ids; mappings use original template ids. Resolve via phase1NewToOriginal.\n if (nestedChildToParent.size > 0) {\n const repeatableInfosSecond = findRepeatableByNodeIds(pages, nodeIds);\n const nestedBlocks = repeatableInfosSecond.filter((r) => nestedChildToParent.has(r.baseNodeId));\n const byChildBase = new Map<string, RepeatableInfo[]>();\n for (const r of nestedBlocks) {\n if (!byChildBase.has(r.baseNodeId)) byChildBase.set(r.baseNodeId, []);\n byChildBase.get(r.baseNodeId)!.push(r);\n }\n for (const [childBaseNodeId, blocks] of byChildBase) {\n const parentBaseNodeId = nestedChildToParent.get(childBaseNodeId)!;\n blocks.forEach((block, parentIndex0) => {\n const parentIndex1Based = parentIndex0 + 1;\n const { parentChildren, startIndex, count, node, baseNodeId } = block;\n const nestedKey = `${parentBaseNodeId}_${parentIndex1Based}_${baseNodeId}`;\n const N = Math.max(\n 1,\n repeatableNestedEntryCounts?.[nestedKey] ?? getNestedRepeatableEntryCount(parentBaseNodeId, parentIndex1Based, baseNodeId, formValues)\n );\n const phase1Map = phase1NewToOriginal.get(`${parentBaseNodeId}_${parentIndex1Based}`);\n const clones: CanvasNode[] = [];\n for (let i = 1; i <= N; i++) {\n const [clone, oldToNew] = cloneNodeWithNewIds(node);\n delete (clone as unknown as Record<string, unknown>).repeatableSection;\n clones.push(clone);\n for (const [oldId, newId] of oldToNew) {\n const originalId = phase1Map?.get(oldId) ?? oldId;\n resolvedIdMap.set(`${parentBaseNodeId}_${parentIndex1Based}_${baseNodeId}_${i}_${originalId}`, newId);\n resolvedIdMap.set(`${parentBaseNodeId}_${parentIndex1Based}_${baseNodeId}_${i}_${oldId}`, newId);\n const fieldId = elementIdToFieldId.get(originalId) ?? elementIdToFieldId.get(oldId);\n if (fieldId) resolvedIdMap.set(`${parentBaseNodeId}_${parentIndex1Based}_${baseNodeId}_${i}_${fieldId}`, newId);\n mappedNewIds.add(newId);\n }\n }\n parentChildren.splice(startIndex, count, ...clones);\n });\n }\n }\n\n (cloned as unknown as Record<string, unknown>).__cloneIdMap = Object.fromEntries(resolvedIdMap);\n (cloned as unknown as Record<string, unknown>).__mappedElementIds = Array.from(mappedNewIds);\n\n // Field label as fallback when value is empty (preview shows \"Text Content\" instead of template placeholder \"Text\")\n const fieldLabelByKey = new Map<string, string>();\n const fieldTypeByKey = new Map<string, string>();\n if (dynamicFields?.length) {\n for (const f of dynamicFields) {\n fieldLabelByKey.set(f.id, f.label ?? f.id);\n if (f.type) fieldTypeByKey.set(f.id, f.type);\n }\n }\n\n const pad2 = (v: string) => v.padStart(2, '0');\n\n /** Format date from yyyy-mm-dd (and ISO datetime variants) to dd/mm/yyyy for display */\n function formatDateForDisplay(val: string): string {\n if (!val || typeof val !== 'string') return val;\n const trimmed = val.trim();\n\n const isoMatch = trimmed.match(/^(\\d{4})-(\\d{1,2})-(\\d{1,2})(?:$|[T\\s])/);\n if (isoMatch) {\n const [, y, m, d] = isoMatch;\n return `${pad2(d)}/${pad2(m)}/${y}`;\n }\n\n const yFirstSlashMatch = trimmed.match(/^(\\d{4})\\/(\\d{1,2})\\/(\\d{1,2})$/);\n if (yFirstSlashMatch) {\n const [, y, m, d] = yFirstSlashMatch;\n return `${pad2(d)}/${pad2(m)}/${y}`;\n }\n\n const dmyOrMdyMatch = trimmed.match(/^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{4})$/);\n if (dmyOrMdyMatch) {\n const [, d, m, y] = dmyOrMdyMatch;\n return `${pad2(d)}/${pad2(m)}/${y}`;\n }\n\n return val;\n }\n\n /** Format time from HH:MM (24h) to hh:mm AM/PM (12h) for display */\n function formatTimeForDisplay(val: string): string {\n if (!val || typeof val !== 'string') return val;\n const timeMatch = val.trim().match(/^(\\d{1,2}):(\\d{2})/);\n if (!timeMatch) return val;\n let h = parseInt(timeMatch[1], 10);\n const min = timeMatch[2];\n if (isNaN(h)) return val;\n const ampm = h >= 12 ? 'PM' : 'AM';\n h = h % 12 || 12;\n return `${h}:${min} ${ampm}`;\n }\n\n function formatDateTimeForDisplay(val: string): string {\n if (!val || typeof val !== 'string') return val;\n const trimmed = val.trim();\n const isoDateTimeMatch = trimmed.match(/^(\\d{4}-\\d{1,2}-\\d{1,2})[T\\s](\\d{1,2}:\\d{2})/);\n if (!isoDateTimeMatch) return formatDateForDisplay(trimmed);\n const [, datePart, timePart] = isoDateTimeMatch;\n return `${formatDateForDisplay(datePart)} ${formatTimeForDisplay(timePart)}`;\n }\n\n /** Resolve the display format for a given fieldKey. Checks displayFormatMap first, then falls back to dynamicField type. */\n function resolveDisplayFormat(fieldKey: string | undefined): string | undefined {\n if (!fieldKey) return undefined;\n\n const getCanonicalSuffix = (key: string): string | undefined => {\n if (!FORMDEF_CANONICAL_KEY_REGEX.test(key)) return undefined;\n const raw = key.replace(/^field_/, '');\n const parts = raw.split('_N_').map((p) => p.trim()).filter(Boolean);\n return parts.length > 1 ? parts[parts.length - 1] : undefined;\n };\n\n // Check displayFormatMap (from form config inputDisplay) first\n if (displayFormatMap) {\n const direct = displayFormatMap.get(fieldKey);\n if (direct) return direct;\n\n const canonicalSuffix = getCanonicalSuffix(fieldKey);\n if (canonicalSuffix) {\n const fmt = displayFormatMap.get(canonicalSuffix);\n if (fmt) return fmt;\n }\n\n if (fieldKey.startsWith('field_')) {\n const noPrefix = fieldKey.replace(/^field_/, '');\n const byNoPrefix = displayFormatMap.get(noPrefix);\n if (byNoPrefix) return byNoPrefix;\n }\n\n // For repeatable keys, extract the trailing fieldId\n const nestedMatch = fieldKey.match(NESTED_REPEATABLE_KEY_REGEX);\n if (nestedMatch) {\n const fmt = displayFormatMap.get(nestedMatch[5]);\n if (fmt) return fmt;\n }\n const match = fieldKey.match(REPEATABLE_KEY_REGEX);\n if (match) {\n const fmt = displayFormatMap.get(match[3]);\n if (fmt) return fmt;\n }\n }\n // Fall back to dynamic field type\n const direct = fieldTypeByKey.get(fieldKey);\n if (direct) return direct;\n\n const canonicalSuffix = getCanonicalSuffix(fieldKey);\n if (canonicalSuffix) {\n const byCanonicalSuffix = fieldTypeByKey.get(canonicalSuffix);\n if (byCanonicalSuffix) return byCanonicalSuffix;\n }\n\n const nestedMatch = fieldKey.match(NESTED_REPEATABLE_KEY_REGEX);\n if (nestedMatch) {\n return fieldTypeByKey.get(nestedMatch[5]);\n }\n const match = fieldKey.match(REPEATABLE_KEY_REGEX);\n if (match) {\n return fieldTypeByKey.get(match[3]);\n }\n return undefined;\n }\n\n const applyValue = (elementId: string, targetProperty: string, value: unknown, fieldKey?: string): boolean => {\n const isTextLike = targetProperty === 'text' || targetProperty === 'content';\n // Do not apply missing values so template defaults stay intact in preview/export.\n if (value === undefined || value === null) {\n return false;\n }\n // For non-text properties (color, stroke, font, opacity, link, image src, etc.), empty string means \"unchanged\".\n if (value === '' && !isTextLike) {\n return false;\n }\n let effectiveValue = (value === undefined || value === null || value === '') && isTextLike && fieldKey\n ? (fieldLabelByKey.get(fieldKey) ?? value)\n : value;\n if (effectiveValue === undefined) return false;\n\n // Format date/time values for text display on the template preview\n if (isTextLike && typeof effectiveValue === 'string' && effectiveValue !== '') {\n const fType = resolveDisplayFormat(fieldKey);\n const trimmed = effectiveValue.trim();\n const isIsoDateTimeLike = /^\\d{4}-\\d{1,2}-\\d{1,2}[T\\s]\\d{1,2}:\\d{2}/.test(trimmed);\n const isIsoDateLike = /^\\d{4}-\\d{1,2}-\\d{1,2}(?:$|[T\\s])/.test(trimmed);\n const is24hTimeLike = /^\\d{1,2}:\\d{2}(?::\\d{2})?$/.test(trimmed);\n\n // Value-shape detection takes precedence to handle repeated generic keys (e.g. many rows using key=\"value\")\n // where one row might be date and another time.\n if (fType === 'datetime-local' || isIsoDateTimeLike) {\n effectiveValue = formatDateTimeForDisplay(effectiveValue);\n } else if (fType === 'date' || isIsoDateLike) {\n effectiveValue = formatDateForDisplay(effectiveValue);\n } else if (fType === 'time' || is24hTimeLike) {\n effectiveValue = formatTimeForDisplay(effectiveValue);\n }\n }\n\n // Page background: apply to first page's settings.backgroundColor when mapping targets __pageBackground__.\n if (elementId === PAGE_BACKGROUND_ELEMENT_ID && targetProperty === 'backgroundColor' && typeof effectiveValue === 'string') {\n if (pages[0]?.settings) {\n pages[0].settings.backgroundColor = effectiveValue;\n return true;\n }\n }\n for (const page of pages) {\n if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue)) return true;\n }\n return false;\n };\n\n /** Find dynamic field by id or by mapping elementId (form key may use either). Tries fullKey so nested keys like field_parent_1_field_child_1_company_name match. Also matches by canonical suffix (field_<prefix>_N_<fieldId> ending with _<fieldId>). */\n const getFieldForRepeatableKey = (fieldId: string, fullKey?: string): DynamicField | undefined => {\n if (!dynamicFields?.length) return undefined;\n if (fullKey) {\n const byFullId = dynamicFields.find((f) => f.id === fullKey);\n if (byFullId) return byFullId;\n }\n const byId = dynamicFields.find((f) => f.id === fieldId);\n if (byId) return byId;\n const byElementId = dynamicFields.find((f) => (f.mappings?.[0] as { elementId?: string } | undefined)?.elementId === fieldId);\n if (byElementId) return byElementId;\n // Try matching by canonical suffix: dynamic field id ends with _N_<fieldId> or _<fieldId>\n const suffixMatch = dynamicFields.find((f) => {\n if (f.id.endsWith(`_N_${fieldId}`)) return true;\n if (f.id.endsWith(`_${fieldId}`) && f.id.includes('_N_')) return true;\n return false;\n });\n return suffixMatch;\n };\n\n // 2) Apply nested repeatable form values: field_<parentId>_<pi>_field_<childId>_<ci>_<fieldId>\n if (dynamicFields?.length) {\n for (const key of Object.keys(formValues)) {\n const nestedMatch = key.match(NESTED_REPEATABLE_KEY_REGEX);\n if (nestedMatch) {\n const [, parentId, parentIndexStr, childId, childIndexStr, fieldId] = nestedMatch;\n const value = formValues[key];\n const field = getFieldForRepeatableKey(fieldId, key);\n const mapping = field?.mappings?.[0];\n if (!mapping) continue;\n const oldElementId = (mapping as { elementId: string }).elementId;\n const mapKeyByElement = `${baseId(parentId)}_${parentIndexStr}_${baseId(childId)}_${childIndexStr}_${oldElementId}`;\n const mapKeyByField = `${baseId(parentId)}_${parentIndexStr}_${baseId(childId)}_${childIndexStr}_${fieldId}`;\n let elementId = resolvedIdMap.get(mapKeyByElement) ?? resolvedIdMap.get(mapKeyByField);\n if (!elementId) {\n const mapKeyByElementBase = `${baseId(parentId)}_${parentIndexStr}_${baseId(childId)}_${childIndexStr}_${baseId(oldElementId)}`;\n elementId = resolvedIdMap.get(mapKeyByElementBase);\n }\n if (!elementId) {\n elementId = findNestedRepeatableElementBySourceId(\n pages, baseId(parentId), parseInt(parentIndexStr, 10), baseId(childId), parseInt(childIndexStr, 10), oldElementId\n );\n }\n if (!elementId) {\n elementId = findNestedRepeatableElementBySourceId(\n pages, baseId(parentId), parseInt(parentIndexStr, 10), baseId(childId), parseInt(childIndexStr, 10), baseId(oldElementId)\n );\n }\n if (!elementId) continue;\n const targetProperty = (mapping as { targetProperty: string }).targetProperty || 'text';\n applyValue(elementId, targetProperty, value, key);\n }\n }\n }\n\n // 3) Apply top-level repeatable form values: field_<nodeId>_<i>_<fieldId> (skip if looks like nested)\n if (dynamicFields?.length) {\n for (const key of Object.keys(formValues)) {\n if (NESTED_REPEATABLE_KEY_REGEX.test(key)) continue;\n const match = key.match(REPEATABLE_KEY_REGEX);\n if (!match) continue;\n const [, nodeId, indexStr, fieldId] = match;\n const value = formValues[key];\n const field = getFieldForRepeatableKey(fieldId);\n const mapping = field?.mappings?.[0];\n if (!mapping) continue;\n const oldElementId = (mapping as { elementId: string }).elementId;\n const mapKeyByElement = `${baseId(nodeId)}_${indexStr}_${oldElementId}`;\n const mapKeyByField = `${baseId(nodeId)}_${indexStr}_${fieldId}`;\n let elementId = resolvedIdMap.get(mapKeyByElement) ?? resolvedIdMap.get(mapKeyByField);\n if (!elementId) {\n elementId = findRepeatableElementBySourceId(pages, baseId(nodeId), parseInt(indexStr, 10), oldElementId);\n }\n if (!elementId) continue;\n const targetProperty = (mapping as { targetProperty: string }).targetProperty || 'text';\n applyValue(elementId, targetProperty, value, key);\n }\n }\n\n // 4) Apply non-repeatable mappings (skip keys that look like repeatable so we don't double-apply)\n const repeatableKeySet = new Set(\n Object.keys(formValues).filter((k) => REPEATABLE_KEY_REGEX.test(k) || NESTED_REPEATABLE_KEY_REGEX.test(k))\n );\n for (const m of mappings) {\n if (repeatableKeySet.has(m.field_key)) continue;\n const value = formValues[m.field_key];\n applyValue(m.element_id, m.target_property, value, m.field_key);\n }\n\n // 5) Stack reflow\n for (const page of pages) {\n if (page.children?.length) {\n page.children = applyStackReflowToPageTree(page.children);\n }\n }\n\n return cloned;\n}\n\n/**\n * Returns a copy of config with only structure: no dynamicFields, no fieldGroups, no repeatableSection on nodes.\n * Use when persisting the \"Structure\" layer; schema lives in form_schema, data in default_data.\n */\nexport function stripConfigToStructure(config: TemplateConfig): TemplateConfig {\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n delete (cloned as unknown as Record<string, unknown>).dynamicFields;\n delete (cloned as unknown as Record<string, unknown>).fieldGroups;\n delete (cloned as unknown as Record<string, unknown>).__cloneIdMap;\n delete (cloned as unknown as Record<string, unknown>).__mappedElementIds;\n function walk(nodes: CanvasNode[] | undefined) {\n if (!nodes) return;\n for (const node of nodes) {\n delete (node as unknown as Record<string, unknown>).repeatableSection;\n if (isGroup(node) && node.children?.length) walk(node.children);\n }\n }\n for (const page of cloned.pages ?? []) {\n if (page.children?.length) walk(page.children);\n }\n return cloned;\n}\n\n/**\n * Returns a copy of config with all mapped element values cleared (structure only).\n * Uses dynamicFields mappings; supports both suffix-style clone ids and UUID clones (via __mappedElementIds).\n * Use when exporting or saving \"structure only\" so data lives only in default_data.\n */\nexport function stripMappedContentFromConfig(config: TemplateConfig): TemplateConfig {\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n const dynamicFields = cloned.dynamicFields as DynamicField[] | undefined;\n const pages = cloned.pages ?? [];\n const mappedIds = new Set((cloned as unknown as Record<string, unknown>).__mappedElementIds as string[] | undefined);\n if (!dynamicFields?.length && mappedIds.size === 0) return cloned;\n\n function clearNode(node: CanvasNode, isUuidClone: boolean) {\n const n = node as unknown as Record<string, unknown>;\n if (isUuidClone) {\n // UUID clone: clear common mapped properties (we don't have mapping for this id)\n n.text = '';\n n.content = '';\n n.src = '';\n n.fill = '';\n n.stroke = '';\n n.opacity = '';\n n.linkConfig = { ...((n.linkConfig as object) || {}), url: '' };\n if ((node as { type?: string }).type === 'text') delete n.height;\n return;\n }\n for (const field of dynamicFields!) {\n for (const mapping of field.mappings ?? []) {\n const base = mapping.elementId;\n const targetProperty = mapping.targetProperty || 'text';\n if (node.id === base || node.id.startsWith(base + '_')) {\n if (targetProperty === 'link') {\n n.linkConfig = { ...((n.linkConfig as object) || {}), url: '' };\n } else {\n n[targetProperty] = '';\n }\n if (targetProperty === 'text' && (node as { type?: string }).type === 'text') {\n delete n.height;\n }\n return;\n }\n }\n }\n }\n\n function walk(nodes: CanvasNode[]) {\n if (!nodes) return;\n for (const node of nodes) {\n const id = node.id;\n const matchSuffix = dynamicFields?.length && dynamicFields.some((f) =>\n (f.mappings ?? []).some((m) => id === m.elementId || id.startsWith((m.elementId ?? '') + '_'))\n );\n const isUuidClone = mappedIds.has(id);\n if (matchSuffix || isUuidClone) clearNode(node, isUuidClone);\n if (isGroup(node) && node.children?.length) walk(node.children);\n }\n }\n for (const page of pages) if (page.children?.length) walk(page.children);\n return cloned;\n}\n","/**\n * Content-bounds pagination: when the first page has contentTop/contentBottom and a\n * single root-level repeatable stack (vertical stack with __baseNodeId children),\n * split overflow to continuation pages (with static elements copied) and remove\n * empty last pages (underflow). Used on the Use page for auto-paginate.\n * Supports nested repeatable sections: if an overflowing entry contains a nested\n * vertical stack with __baseNodeId children, we split at that level so part stays\n * and part overflows to the next page.\n */\n\nimport type { TemplateConfig, TemplateConfigPage, CanvasNode, GroupNode, CanvasElement } from '@/types/editor';\nimport { isGroup, isVerticalStackLayoutMode, generateId } from '@/types/editor';\nimport {\n getAbsoluteBounds,\n getNodeBounds,\n applyStackReflowToPageTree,\n} from '@/lib/layoutEngine';\n\nfunction hasBaseNodeId(node: CanvasNode): boolean {\n return (node as CanvasNode & { __baseNodeId?: string }).__baseNodeId != null;\n}\n\nfunction isStaticOnNewPage(node: CanvasNode): boolean {\n return !!(node as CanvasNode & { staticOnNewPage?: boolean }).staticOnNewPage;\n}\n\nfunction nodeLeft(node: CanvasNode): number {\n return typeof node.left === 'number' ? node.left : 0;\n}\n\nfunction nodeTop(node: CanvasNode): number {\n return typeof node.top === 'number' ? node.top : 0;\n}\n\nfunction cloneNodeWithNewIds(node: CanvasNode): CanvasNode {\n const base = (node as CanvasNode & { __baseNodeId?: string }).__baseNodeId;\n const source = (node as CanvasNode & { __sourceId?: string }).__sourceId;\n if (isGroup(node)) {\n const g = node as GroupNode;\n const cloned: GroupNode & { __baseNodeId?: string; __sourceId?: string } = {\n ...g,\n id: generateId('group'),\n children: (g.children ?? []).map(cloneNodeWithNewIds),\n } as GroupNode;\n if (base != null) cloned.__baseNodeId = base;\n if (source != null) cloned.__sourceId = source;\n return cloned as GroupNode;\n }\n const el = node as CanvasElement;\n const prefix = el.type === 'text' ? 'text' : el.type === 'image' ? 'img' : el.type === 'shape' ? 'shape' : 'line';\n const cloned = { ...el, id: generateId(prefix) } as CanvasElement & { __baseNodeId?: string; __sourceId?: string };\n if (base != null) cloned.__baseNodeId = base;\n if (source != null) cloned.__sourceId = source;\n return cloned as CanvasElement;\n}\n\n/** Clone node tree with deterministic ids so continuation page doesn't remount on form change. */\nfunction cloneNodeWithStableIds(node: CanvasNode, baseId: string, path: string): CanvasNode {\n const base = (node as CanvasNode & { __baseNodeId?: string }).__baseNodeId;\n const source = (node as CanvasNode & { __sourceId?: string }).__sourceId;\n const stableId = `${baseId}-${path}`;\n // Preserve link to original element for theme matching: if no __sourceId exists,\n // use the original node's ID so applyThemeToConfig can find these clones.\n const effectiveSource = source ?? node.id;\n if (isGroup(node)) {\n const g = node as GroupNode;\n const cloned: GroupNode & { __baseNodeId?: string; __sourceId?: string } = {\n ...g,\n id: stableId,\n children: (g.children ?? []).map((child, i) => cloneNodeWithStableIds(child, baseId, `${path}-${i}`)),\n } as GroupNode;\n if (base != null) cloned.__baseNodeId = base;\n cloned.__sourceId = effectiveSource;\n return cloned as GroupNode;\n }\n const el = node as CanvasElement;\n const cloned = { ...el, id: stableId } as CanvasElement & { __baseNodeId?: string; __sourceId?: string };\n if (base != null) cloned.__baseNodeId = base;\n cloned.__sourceId = effectiveSource;\n return cloned as CanvasElement;\n}\n\nfunction findFlowStack(pageChildren: CanvasNode[]): GroupNode | null {\n const all = findAllFlowStacks(pageChildren);\n return all.length > 0 ? all[0] : null;\n}\n\nfunction findAllFlowStacks(pageChildren: CanvasNode[]): GroupNode[] {\n const result: GroupNode[] = [];\n for (const node of pageChildren) {\n if (!isGroup(node)) continue;\n const g = node as GroupNode;\n if (!isVerticalStackLayoutMode(g.layoutMode) || !g.children?.length) continue;\n if (g.children.some(hasBaseNodeId)) result.push(g);\n }\n return result;\n}\n\n/** Find a nested vertical stack with repeatable-style children (__baseNodeId or group children) inside an entry. */\nfunction findNestedFlowStack(entry: GroupNode): GroupNode | null {\n const queue: CanvasNode[] = [...(entry.children ?? [])];\n while (queue.length > 0) {\n const node = queue.shift()!;\n if (!isGroup(node)) continue;\n const g = node as GroupNode;\n const hasRepeatableChildren =\n g.children?.length &&\n (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));\n if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;\n if (g.children?.length) queue.push(...g.children);\n }\n return null;\n}\n\n/** Replace a nested flow stack's children inside an entry (by id); optionally set all child tops to 0 for continuation layout. */\nfunction replaceNestedFlowStackChildren(\n entry: GroupNode,\n nestedStackId: string,\n newChildren: CanvasNode[],\n setTopsToZero: boolean\n): GroupNode {\n function walk(node: CanvasNode): CanvasNode {\n if (isGroup(node)) {\n const g = node as GroupNode;\n if (g.id === nestedStackId) {\n const children = setTopsToZero\n ? newChildren.map((c) => ({ ...c, top: 0 } as CanvasNode))\n : newChildren;\n return { ...g, children } as GroupNode;\n }\n return { ...g, children: (g.children ?? []).map(walk) } as GroupNode;\n }\n return node;\n }\n return walk(entry) as GroupNode;\n}\n\n/**\n * If entry contains a nested repeatable stack and it can be split by contentBottom,\n * return { stayVersion, overflowVersion }. Otherwise return { stayVersion: null, overflowVersion: entry } (whole entry overflows).\n * The \"entry\" from the root flow stack may itself BE the nested stack (e.g. \"Family Details\" section is a vertical stack\n * whose children are the family members). In that case we split the entry's own children.\n */\nfunction splitEntryAtNested(\n entry: GroupNode,\n pageChildren: CanvasNode[],\n contentBottom: number\n): { stayVersion: GroupNode | null; overflowVersion: GroupNode | null } {\n // Case 1: Entry itself is the nested flow stack (section = vertical stack, children = nested items).\n const entryIsNestedStack =\n isVerticalStackLayoutMode(entry.layoutMode) &&\n entry.children?.length &&\n (entry.children.some(hasBaseNodeId) || entry.children.some((c) => isGroup(c)));\n const nested = entryIsNestedStack ? entry : findNestedFlowStack(entry);\n if (!nested) return { stayVersion: null, overflowVersion: entry };\n\n const nestedKids = nested.children ?? [];\n const { stayIndices: nestedStay, overflowIndices: nestedOverflow } = splitFlowStackByBounds(\n nested,\n pageChildren,\n contentBottom\n );\n if (nestedOverflow.length === 0) return { stayVersion: entry, overflowVersion: null };\n if (nestedStay.length === 0) return { stayVersion: null, overflowVersion: entry };\n\n const nestedStayChildren = nestedStay.map((i) => nestedKids[i]);\n const nestedOverflowChildren = nestedOverflow.map((i) => nestedKids[i]).map((c) => cloneNodeWithNewIds(c));\n // Keep each child's own left position — don't force firstLeft from nestedKids[0]\n // which could be a section title at a different x than the data rows.\n const overflowWithZeroTop = nestedOverflowChildren.map((c) => ({\n ...c,\n top: 0,\n })) as CanvasNode[];\n\n if (entryIsNestedStack) {\n // Entry is the stack: stay = entry with stay children only, overflow = clone of entry with overflow children only.\n const nestedStayCloned = nestedStayChildren.map((c) => cloneNodeWithNewIds(c));\n const stayVersion = { ...entry, children: nestedStayCloned } as GroupNode;\n const overflowEntryClone = cloneNodeWithNewIds(entry) as GroupNode;\n const overflowVersion = { ...overflowEntryClone, children: overflowWithZeroTop } as GroupNode;\n return { stayVersion, overflowVersion };\n }\n\n // Case 2: Nested stack is inside entry — copy whole parent for both stay and overflow.\n const stayEntryClone = cloneNodeWithNewIds(entry) as GroupNode;\n const nestedInStayClone = findNestedFlowStack(stayEntryClone);\n if (!nestedInStayClone) return { stayVersion: null, overflowVersion: entry };\n const nestedStayCloned = nestedStayChildren.map((c) => cloneNodeWithNewIds(c));\n const stayVersion = replaceNestedFlowStackChildren(stayEntryClone, nestedInStayClone.id, nestedStayCloned, false);\n\n const overflowEntryClone = cloneNodeWithNewIds(entry) as GroupNode;\n const nestedInClone = findNestedFlowStack(overflowEntryClone);\n if (!nestedInClone) return { stayVersion, overflowVersion: entry };\n const overflowVersion = replaceNestedFlowStackChildren(overflowEntryClone, nestedInClone.id, overflowWithZeroTop, false);\n\n return { stayVersion, overflowVersion };\n}\n\nfunction splitFlowStackByBounds(\n flowStack: GroupNode,\n pageChildren: CanvasNode[],\n contentBottom: number\n): { stayIndices: number[]; overflowIndices: number[] } {\n const kids = flowStack.children ?? [];\n const stayIndices: number[] = [];\n const overflowIndices: number[] = [];\n for (let i = 0; i < kids.length; i++) {\n const abs = getAbsoluteBounds(kids[i], pageChildren);\n if (abs.bottom <= contentBottom) stayIndices.push(i);\n else overflowIndices.push(i);\n }\n return { stayIndices, overflowIndices };\n}\n\nfunction applyStackPositionsToGroup(group: GroupNode, pageChildren: CanvasNode[]): GroupNode {\n const kids = group.children ?? [];\n if (kids.length === 0) return group;\n const gap = group.stackSpacing ?? 8;\n const firstLeft = nodeLeft(kids[0]);\n let prevBottom = 0;\n const newKids = kids.map((child, i) => {\n const b = getNodeBounds(child, pageChildren);\n const top = i === 0 ? nodeTop(child) : prevBottom + gap;\n prevBottom = top + b.height;\n return { ...child, top, left: i === 0 ? nodeLeft(child) : firstLeft } as CanvasNode;\n });\n return { ...group, children: newKids } as GroupNode;\n}\n\nfunction buildContinuationPage(\n sourcePage: TemplateConfigPage,\n overflowStacks: Array<{ flowStack: GroupNode; overflowChildren: CanvasNode[] }>,\n contentTop: number,\n pageIndex: number\n): TemplateConfigPage {\n const newPageId = `page-continuation-${pageIndex}`;\n const stableBaseId = `cont-${pageIndex}`;\n const rootChildren: CanvasNode[] = [];\n let staticIndex = 0;\n for (const node of sourcePage.children ?? []) {\n if (isStaticOnNewPage(node)) {\n rootChildren.push(cloneNodeWithStableIds(node, stableBaseId, `static-${staticIndex++}`));\n }\n }\n\n const overflowStackTop = contentTop;\n\n for (let si = 0; si < overflowStacks.length; si++) {\n const { flowStack: flowStackOriginal, overflowChildren } = overflowStacks[si];\n const stackLeft = (flowStackOriginal.left ?? 0) as number;\n const clonedOverflow = overflowChildren.map((child, i) => {\n const cloned = cloneNodeWithStableIds(child, stableBaseId, `entry-${si}-${i}`);\n return { ...cloned, top: 0 } as CanvasNode;\n });\n const overflowStack: GroupNode = {\n ...flowStackOriginal,\n id: `${stableBaseId}-stack-${si}`,\n left: stackLeft,\n top: overflowStackTop,\n children: clonedOverflow,\n layoutMode: flowStackOriginal.layoutMode ?? 'vertical-stack',\n } as GroupNode;\n rootChildren.push(overflowStack);\n }\n\n const withHeights = applyStackReflowToPageTree(rootChildren);\n const finalChildren = withHeights;\n return {\n id: newPageId,\n name: `Page ${pageIndex + 2}`,\n children: finalChildren,\n settings: { ...sourcePage.settings },\n };\n}\n\ninterface FlowStackOverflow {\n flowStack: GroupNode;\n flowStackIndex: number;\n stayChildren: CanvasNode[];\n overflowChildren: CanvasNode[];\n}\n\n/** Try to split nested entries for a single flow stack's overflow, returns updated stay/overflow. */\nfunction splitNestedForOverflow(\n flowStack: GroupNode,\n stayChildren: CanvasNode[],\n overflowChildren: CanvasNode[],\n overflowIndices: number[],\n pageChildren: CanvasNode[],\n contentBottom: number\n): { stayChildren: CanvasNode[]; overflowChildren: CanvasNode[] } {\n const kids = flowStack.children ?? [];\n let newStay = [...stayChildren];\n let newOverflow = [...overflowChildren];\n\n for (let j = 0; j < overflowIndices.length; j++) {\n const entry = kids[overflowIndices[j]] as GroupNode;\n const { stayVersion, overflowVersion } = splitEntryAtNested(entry, pageChildren, contentBottom);\n if (stayVersion != null && overflowVersion != null) {\n newStay = newStay.concat(stayVersion);\n newOverflow = [\n ...overflowIndices.slice(0, j).map((i) => kids[i]),\n overflowVersion as CanvasNode,\n ...overflowIndices.slice(j + 1).map((i) => kids[i]),\n ];\n break;\n }\n if (stayVersion != null && overflowVersion == null) {\n newStay = newStay.concat(stayVersion);\n newOverflow = [\n ...overflowIndices.slice(0, j).map((i) => kids[i]),\n ...overflowIndices.slice(j + 1).map((i) => kids[i]),\n ];\n break;\n }\n }\n return { stayChildren: newStay, overflowChildren: newOverflow };\n}\n\nexport function applyContentBoundsPagination(config: TemplateConfig): TemplateConfig {\n const pages = config.pages ?? [];\n if (pages.length === 0) return config;\n const firstPage = pages[0];\n const contentTop = firstPage.settings?.contentTop;\n const contentBottom = firstPage.settings?.contentBottom;\n if (contentTop == null || contentBottom == null || contentBottom <= contentTop) return config;\n\n const pageChildren = firstPage.children ?? [];\n const allFlowStacks = findAllFlowStacks(pageChildren);\n if (allFlowStacks.length === 0) return config;\n\n // Process each flow stack independently\n const stackResults: FlowStackOverflow[] = [];\n let anyOverflow = false;\n\n for (const flowStack of allFlowStacks) {\n const flowStackIndex = pageChildren.findIndex((n) => n.id === flowStack.id);\n const kids = flowStack.children ?? [];\n const { stayIndices, overflowIndices } = splitFlowStackByBounds(flowStack, pageChildren, contentBottom);\n\n let stayChildren: CanvasNode[] = stayIndices.map((i) => kids[i]);\n let overflowChildren: CanvasNode[] = overflowIndices.map((i) => kids[i]);\n\n if (overflowIndices.length > 0) {\n anyOverflow = true;\n const result = splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overflowIndices, pageChildren, contentBottom);\n stayChildren = result.stayChildren;\n overflowChildren = result.overflowChildren;\n }\n\n stackResults.push({ flowStack, flowStackIndex, stayChildren, overflowChildren });\n }\n\n if (!anyOverflow) return config;\n\n // Build first page with all flow stacks trimmed\n const flowStackIds = new Set(allFlowStacks.map((s) => s.id));\n let newFirstRoot = pageChildren.map((node) => {\n if (!flowStackIds.has(node.id)) return node;\n const result = stackResults.find((r) => r.flowStack.id === node.id)!;\n const newStack: GroupNode = { ...result.flowStack, children: result.stayChildren };\n return applyStackReflowToPageTree([newStack])[0] as GroupNode;\n });\n newFirstRoot = applyStackReflowToPageTree(newFirstRoot);\n\n const newFirstPage: TemplateConfigPage = {\n ...firstPage,\n children: newFirstRoot,\n };\n\n const resultPages: TemplateConfigPage[] = [newFirstPage];\n\n // Collect overflow from all stacks\n let currentOverflows = stackResults\n .filter((r) => r.overflowChildren.length > 0)\n .map((r) => ({ flowStack: r.flowStack, overflowChildren: r.overflowChildren }));\n\n let currentPageIndex = 1;\n const MAX_CONTINUATION_PAGES = 50;\n\n while (currentOverflows.length > 0 && currentPageIndex < MAX_CONTINUATION_PAGES) {\n const contPage = buildContinuationPage(firstPage, currentOverflows, contentTop, currentPageIndex);\n resultPages.push(contPage);\n\n const contChildren = contPage.children ?? [];\n const contFlowStacks = findAllFlowStacks(contChildren);\n // If no flow stacks found, also check last N children as fallback\n if (contFlowStacks.length === 0) break;\n\n const nextOverflows: Array<{ flowStack: GroupNode; overflowChildren: CanvasNode[] }> = [];\n let anyStay = false;\n\n for (let si = 0; si < contFlowStacks.length; si++) {\n const contFlowStack = contFlowStacks[si];\n const contKids = contFlowStack.children ?? [];\n const { stayIndices: contStay, overflowIndices: contOverflow } = splitFlowStackByBounds(\n contFlowStack, contChildren, contentBottom\n );\n\n if (contStay.length > 0) anyStay = true;\n\n if (contOverflow.length === 0) continue;\n\n let contStayChildren: CanvasNode[] = contStay.map((i) => contKids[i]);\n let nextOverflowChildren: CanvasNode[] = contOverflow.map((i) => contKids[i]);\n\n if (contOverflow.length > 0) {\n const result = splitNestedForOverflow(contFlowStack, contStayChildren, nextOverflowChildren, contOverflow, contChildren, contentBottom);\n contStayChildren = result.stayChildren;\n nextOverflowChildren = result.overflowChildren;\n }\n\n // Update this continuation page's flow stack with only stay children\n const updatedContChildren = contChildren.map((node) => {\n if (node.id !== contFlowStack.id) return node;\n return applyStackReflowToPageTree([{ ...contFlowStack, children: contStayChildren }])[0] as GroupNode;\n });\n resultPages[resultPages.length - 1] = {\n ...contPage,\n children: applyStackReflowToPageTree(updatedContChildren),\n };\n\n if (nextOverflowChildren.length > 0) {\n // Find the original flow stack this corresponds to\n const originalFlowStack = currentOverflows[si]?.flowStack ?? contFlowStack;\n nextOverflows.push({ flowStack: originalFlowStack, overflowChildren: nextOverflowChildren });\n }\n }\n\n if (!anyStay) break; // avoid infinite loop\n currentOverflows = nextOverflows;\n currentPageIndex++;\n }\n\n // Remove trailing empty pages\n while (resultPages.length > 1) {\n const last = resultPages[resultPages.length - 1];\n const lastChildren = last.children ?? [];\n if (lastChildren.length === 0) {\n resultPages.pop();\n continue;\n }\n let hasContent = false;\n for (const node of lastChildren) {\n if (!isGroup(node)) continue;\n const g = node as GroupNode;\n if (isVerticalStackLayoutMode(g.layoutMode) && (g.children?.length ?? 0) > 0) {\n hasContent = true;\n break;\n }\n }\n // Also check via findFlowStack fallback\n let lastFlow = findFlowStack(lastChildren);\n if (!lastFlow && lastChildren.length > 0) {\n const lastRoot = lastChildren[lastChildren.length - 1];\n if (isGroup(lastRoot) && isVerticalStackLayoutMode((lastRoot as GroupNode).layoutMode) && (lastRoot as GroupNode).children?.length) {\n lastFlow = lastRoot as GroupNode;\n }\n }\n const flowChildCount = lastFlow ? (lastFlow.children?.length ?? 0) : -1;\n if (flowChildCount === 0 && !hasContent) resultPages.pop();\n else break;\n }\n\n return { ...config, pages: resultPages };\n}\n","/**\n * Data Resolver — Fetches template + form schema from database and applies\n * V2 sectionState to produce a fully resolved TemplateConfig ready for rendering.\n *\n * This mirrors the server's /render-from-form pipeline so external consumers\n * can pass the same minimal payload: { templateId, formSchemaId, sectionState }.\n */\n\nimport type { TemplateConfig, DynamicField } from './types';\nimport type { FormTemplateMappingEntry } from '@/lib/api';\nimport type { InferredSection, SectionFormState } from '@/lib/inferFormSchemaFromTemplate';\nimport type { FormDefSection } from '@/lib/formSchemasApi';\nimport type { FormConfig } from '@/lib/formsApi';\nimport {\n inferFormSchemaFromTemplate,\n formDefSectionsToInferred,\n flattenSectionStateToFormData,\n defaultSectionState,\n isDefaultDataV2,\n} from '@/lib/inferFormSchemaFromTemplate';\nimport { applyFormDataToConfig } from '@/lib/applyFormDataToConfig';\nimport { applyContentBoundsPagination } from '@/lib/layoutWithContentBounds';\nimport { baseId } from '@/lib/formSchema';\n\n// ─── Public types ────────────────────────────────────────────────\n\nexport interface ResolveOptions {\n /** Template UUID */\n templateId: string;\n /** Optional flat formData (legacy, simple fields only) */\n formData?: Record<string, any>;\n /** Supabase project URL */\n supabaseUrl: string;\n /** Supabase anon key */\n supabaseAnonKey: string;\n}\n\n/** V2 resolution options — matches the server API payload */\nexport interface ResolveFromFormOptions {\n /** Template UUID */\n templateId: string;\n /** Form schema UUID */\n formSchemaId: string;\n /** V2 section state (same shape sent to /render-from-form) */\n sectionState: SectionFormState;\n /** Optional theme variant ID (default: 'default') */\n themeId?: string;\n /** Supabase project URL */\n supabaseUrl: string;\n /** Supabase anon key */\n supabaseAnonKey: string;\n}\n\nexport interface ResolvedTemplate {\n config: TemplateConfig;\n templateName: string;\n templateId: string;\n}\n\n// ─── Internal helpers ────────────────────────────────────────────\n\n/** Fetch a single row from a Supabase table via REST */\nasync function fetchRow(\n supabaseUrl: string,\n anonKey: string,\n table: string,\n id: string,\n): Promise<any> {\n const url = `${supabaseUrl}/rest/v1/${table}?id=eq.${id}&select=*`;\n const res = await fetch(url, {\n headers: {\n apikey: anonKey,\n Authorization: `Bearer ${anonKey}`,\n Accept: 'application/json',\n },\n });\n if (!res.ok) throw new Error(`Failed to fetch ${table}/${id}: ${res.status}`);\n const rows = await res.json();\n if (!rows.length) throw new Error(`${table}/${id} not found`);\n return rows[0];\n}\n\n/** Fetch default form for a form schema */\nasync function fetchDefaultForm(\n supabaseUrl: string,\n anonKey: string,\n formSchemaId: string,\n): Promise<{ config: FormConfig; values: Record<string, any>; saved_data: Record<string, any> } | null> {\n const url = `${supabaseUrl}/rest/v1/forms?form_schema_id=eq.${formSchemaId}&is_default=eq.true&select=config,values,saved_data&limit=1`;\n const res = await fetch(url, {\n headers: {\n apikey: anonKey,\n Authorization: `Bearer ${anonKey}`,\n Accept: 'application/json',\n },\n });\n if (!res.ok) return null;\n const rows = await res.json();\n return rows.length ? rows[0] : null;\n}\n\n// ─── Simple resolution (legacy flat formData) ────────────────────\n\nfunction applyFormDataSimple(config: TemplateConfig, formData: Record<string, any>): TemplateConfig {\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n if (!cloned.pages || !cloned.dynamicFields?.length) return cloned;\n const dynamicFields = cloned.dynamicFields as DynamicField[];\n for (const field of dynamicFields) {\n const value = formData[field.id];\n if (value === undefined) continue;\n for (const mapping of field.mappings) {\n setInTree(cloned.pages.flatMap(p => p.children), mapping.elementId, mapping.targetProperty, value);\n }\n }\n return cloned;\n}\n\nfunction setInTree(nodes: any[], elementId: string, targetProperty: string, value: unknown): boolean {\n for (const node of nodes) {\n if (node.id === elementId) {\n if (targetProperty === 'text' && node.type === 'text' && typeof value === 'string') {\n node[targetProperty] = value === '' ? ' ' : value;\n } else if (targetProperty === 'link') {\n node.linkConfig = { ...(node.linkConfig || {}), url: String(value ?? '') };\n } else {\n node[targetProperty] = value;\n }\n if (targetProperty === 'text' && node.type === 'text') delete node.height;\n return true;\n }\n if (node.children && Array.isArray(node.children)) {\n if (setInTree(node.children, elementId, targetProperty, value)) return true;\n }\n }\n return false;\n}\n\n// ─── Legacy: fetch + simple flat apply ───────────────────────────\n\nexport async function resolveTemplateData(options: ResolveOptions): Promise<ResolvedTemplate> {\n const { templateId, formData, supabaseUrl, supabaseAnonKey } = options;\n const template = await fetchRow(supabaseUrl, supabaseAnonKey, 'templates', templateId);\n let config = template.config as TemplateConfig;\n const defaultData = template.default_data as Record<string, any> | null;\n if (defaultData) config = applyFormDataSimple(config, defaultData);\n if (formData && Object.keys(formData).length > 0) config = applyFormDataSimple(config, formData);\n return { config, templateName: template.name || 'Untitled', templateId };\n}\n\n// ─── V2 resolution: sectionState → fully resolved config ────────\n\n/**\n * Resolve a template using the V2 sectionState format.\n * This is the primary API for external consumers and matches the server's /render-from-form pipeline.\n */\nexport async function resolveFromForm(options: ResolveFromFormOptions): Promise<ResolvedTemplate> {\n const { templateId, formSchemaId, sectionState, themeId, supabaseUrl, supabaseAnonKey } = options;\n\n // Fetch template and form schema in parallel\n const [templateRow, formSchemaRow, defaultForm] = await Promise.all([\n fetchRow(supabaseUrl, supabaseAnonKey, 'templates', templateId),\n fetchRow(supabaseUrl, supabaseAnonKey, 'form_schemas', formSchemaId),\n fetchDefaultForm(supabaseUrl, supabaseAnonKey, formSchemaId),\n ]);\n\n const templateConfig = templateRow.config as TemplateConfig;\n const templateFormSchema = templateRow.form_schema as {\n repeatableSections?: { nodeId: string; label: string; minEntries?: number; maxEntries?: number }[];\n } | null;\n\n // Build form schema sections → InferredSection[]\n const schemaSections = formSchemaRow.schema?.sections as FormDefSection[] | undefined;\n const repeatableFromSchema = templateFormSchema?.repeatableSections;\n const repeatableNodeMap = new Map<string, string>();\n if (repeatableFromSchema) {\n for (const r of repeatableFromSchema) {\n repeatableNodeMap.set(r.label, r.nodeId);\n }\n }\n\n let inferredSections: InferredSection[];\n if (schemaSections?.length) {\n inferredSections = formDefSectionsToInferred(schemaSections, repeatableNodeMap);\n } else if (templateConfig.dynamicFields?.length) {\n const groups = templateConfig.fieldGroups || [];\n inferredSections = inferFormSchemaFromTemplate(\n templateConfig.dynamicFields as any[],\n groups as any[],\n templateConfig.pages?.length ? { pages: templateConfig.pages as any[] } : undefined,\n );\n } else {\n inferredSections = [];\n }\n\n // Apply default data first (from template default_data V2 or default form values)\n let mergedSectionState = { ...sectionState };\n const templateDefaultData = templateRow.default_data;\n if (templateDefaultData && isDefaultDataV2(templateDefaultData)) {\n // Merge: user sectionState overrides defaults\n const defaults = templateDefaultData.sectionState as SectionFormState;\n for (const key of Object.keys(defaults)) {\n if (!(key in mergedSectionState)) {\n mergedSectionState[key] = defaults[key];\n }\n }\n }\n\n // Flatten sectionState → flat formData\n const flatFormData = flattenSectionStateToFormData(mergedSectionState, inferredSections);\n\n // Build mappings array from dynamicFields\n const dynamicFields = templateConfig.dynamicFields as DynamicField[] | undefined;\n const mappings: FormTemplateMappingEntry[] = [];\n if (dynamicFields) {\n for (const field of dynamicFields) {\n if (field.mappings) {\n for (const m of field.mappings) {\n mappings.push({\n field_key: field.id,\n element_id: m.elementId,\n target_property: m.targetProperty,\n });\n }\n }\n }\n }\n\n // Build repeatable section info\n const topLevelRepeatables = inferredSections\n .filter((s): s is Extract<typeof s, { type: 'repeatable' }> => s.type === 'repeatable' && !(s as any).parentId)\n .map((s) => {\n const entries = (mergedSectionState[s.id] ?? []) as Array<Record<string, string | string[]>>;\n const nodeId = (s as any).treeNodeId ?? s.id;\n return { nodeId, label: s.label, entryCount: Math.max(1, entries.length) };\n });\n const nestedRepeatables = inferredSections\n .filter((s): s is Extract<typeof s, { type: 'repeatable' }> => s.type === 'repeatable' && (s as any).parentId != null)\n .map((s) => ({ nodeId: (s as any).treeNodeId ?? s.id, label: s.label }));\n const repeatableList = [...topLevelRepeatables, ...nestedRepeatables];\n\n // Build nested entry counts\n const repeatableNestedEntryCounts: Record<string, number> = {};\n for (const s of inferredSections) {\n if (s.type !== 'repeatable') continue;\n const parentId = (s as any).parentId;\n if (parentId == null) continue;\n const parentEntries = (mergedSectionState[parentId] ?? []) as any[];\n const parentSection = inferredSections.find((ps) => ps.id === parentId);\n const parentTreeNodeId = parentSection ? ((parentSection as any).treeNodeId ?? parentSection.id) : parentId;\n const childTreeNodeId = (s as any).treeNodeId ?? s.id;\n for (let pi = 0; pi < parentEntries.length; pi++) {\n const compositeKey = `${parentId}_${pi}_${s.id}`;\n const entries = (mergedSectionState[compositeKey] ?? []) as any[];\n const nestedKey = `${baseId(parentTreeNodeId)}_${pi + 1}_${baseId(childTreeNodeId)}`;\n repeatableNestedEntryCounts[nestedKey] = Math.max(1, entries.length);\n }\n }\n\n // Build display format map from form config\n const displayFormatMap = new Map<string, string>();\n const formConfig = defaultForm?.config as FormConfig | undefined;\n if (formConfig?.sections) {\n const collectFormats = (sections: FormConfig['sections']) => {\n for (const s of sections) {\n if (s.fields) {\n for (const f of s.fields) {\n if (f.inputDisplay && f.inputDisplay !== 'text') {\n displayFormatMap.set(f.key, f.inputDisplay);\n }\n }\n }\n if (s.entryFieldConfigs) {\n for (const configs of Object.values(s.entryFieldConfigs)) {\n for (const f of configs) {\n if (f.inputDisplay && f.inputDisplay !== 'text') {\n displayFormatMap.set(f.key, f.inputDisplay);\n }\n }\n }\n }\n if (s.children) collectFormats(s.children);\n }\n };\n collectFormats(formConfig.sections);\n }\n\n // Apply form data to config (handles repeatable sections, cloning, reflowing)\n let resolvedConfig = applyFormDataToConfig(\n templateConfig as any,\n mappings,\n flatFormData,\n repeatableList.length > 0 ? repeatableList : (repeatableFromSchema ?? []),\n undefined,\n Object.keys(repeatableNestedEntryCounts).length > 0 ? repeatableNestedEntryCounts : undefined,\n displayFormatMap.size > 0 ? displayFormatMap : undefined,\n ) as unknown as TemplateConfig;\n\n // Apply theme variant if specified\n if (themeId && themeId !== 'default' && templateConfig.themeConfig) {\n const tc = templateConfig.themeConfig as any;\n const variant = tc.variants?.find((v: any) => v.id === themeId);\n if (variant && tc.properties) {\n const themed = JSON.parse(JSON.stringify(resolvedConfig)) as any;\n for (const prop of tc.properties) {\n const value = variant.values?.[prop.id];\n if (value === undefined) continue;\n if (prop.targetProperty === 'backgroundColor' && prop.elementId === '__pageBackground__') {\n themed.pages.forEach((p: any) => { p.settings.backgroundColor = value; });\n continue;\n }\n // Apply to matching elements\n for (const page of themed.pages) {\n const els = flattenAll(page.children || []);\n for (const el of els) {\n if (el.id === prop.elementId || baseId(el.id) === baseId(prop.elementId)) {\n if (prop.svgColorKey && el.svgColorMap) {\n el.svgColorMap = { ...el.svgColorMap, [prop.svgColorKey]: value };\n } else {\n el[prop.targetProperty] = value;\n }\n }\n }\n }\n }\n resolvedConfig = themed;\n }\n }\n\n // Apply content bounds pagination (auto-paginate)\n resolvedConfig = applyContentBoundsPagination(resolvedConfig as any) as unknown as TemplateConfig;\n\n return {\n config: resolvedConfig,\n templateName: templateRow.name || 'Untitled',\n templateId,\n };\n}\n\n/** Flatten all nodes in a tree */\nfunction flattenAll(nodes: any[]): any[] {\n const result: any[] = [];\n for (const node of nodes) {\n result.push(node);\n if (node.children) result.push(...flattenAll(node.children));\n }\n return result;\n}\n","/**\n * PixldocsPreview — React component for rendering Pixldocs templates with full parity.\n *\n * Supports two modes:\n * 1. Pass a pre-resolved `config` directly\n * 2. Pass `templateId` + `formSchemaId` + `sectionState` to auto-resolve from database\n */\n\nimport { useEffect, useState, useMemo } from 'react';\n\nimport type { TemplateConfig } from './types';\nimport type { SectionFormState } from '@/lib/inferFormSchemaFromTemplate';\nimport { setPackageApiUrl } from './shims/app-api';\nimport { PreviewCanvas as AppPreviewCanvas } from '../../../src/components/PreviewCanvas';\nimport { resolveFromForm } from './data-resolver';\n\nexport interface PixldocsPreviewBaseProps {\n /** Page index to render (default: 0) */\n pageIndex?: number;\n /** Zoom / scale factor (default: 1) */\n zoom?: number;\n /** When true, zoom is used as-is without auto-fit calculation */\n absoluteZoom?: boolean;\n /** Image proxy URL for CORS-safe external image loading */\n imageProxyUrl?: string;\n /** CSS class name for the outer container */\n className?: string;\n /** Inline styles for the outer container */\n style?: Record<string, string | number>;\n /** Optional dynamic field click callback */\n onDynamicFieldClick?: (elementId: string, fieldId: string) => void;\n /** Called when the canvas has fully loaded fonts and is ready to display */\n onReady?: () => void;\n /** Called when resolution or rendering fails */\n onError?: (error: Error) => void;\n}\n\n/** Mode 1: Pre-resolved config */\nexport interface PixldocsPreviewConfigProps extends PixldocsPreviewBaseProps {\n /** Template configuration object (pre-resolved) */\n config: TemplateConfig;\n templateId?: never;\n formSchemaId?: never;\n sectionState?: never;\n themeId?: never;\n supabaseUrl?: never;\n supabaseAnonKey?: never;\n}\n\n/** Mode 2: Auto-resolve from database */\nexport interface PixldocsPreviewResolveProps extends PixldocsPreviewBaseProps {\n config?: never;\n /** Template UUID to fetch and resolve */\n templateId: string;\n /** Form schema UUID */\n formSchemaId: string;\n /** V2 section state data */\n sectionState: SectionFormState;\n /** Optional theme variant ID */\n themeId?: string;\n /** Supabase project URL */\n supabaseUrl: string;\n /** Supabase anon key */\n supabaseAnonKey: string;\n}\n\nexport type PixldocsPreviewProps = PixldocsPreviewConfigProps | PixldocsPreviewResolveProps;\n\nexport function PixldocsPreview(props: PixldocsPreviewProps) {\n const {\n pageIndex = 0,\n zoom = 1,\n absoluteZoom = false,\n imageProxyUrl,\n className,\n style,\n onDynamicFieldClick,\n onReady,\n onError,\n } = props;\n\n // Set image proxy URL\n useEffect(() => {\n setPackageApiUrl(imageProxyUrl);\n }, [imageProxyUrl]);\n\n // Handle auto-resolution mode\n const [resolvedConfig, setResolvedConfig] = useState<TemplateConfig | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n const isResolveMode = !('config' in props && props.config);\n\n useEffect(() => {\n if (!isResolveMode) {\n setResolvedConfig(null);\n return;\n }\n\n const p = props as PixldocsPreviewResolveProps;\n if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;\n\n let cancelled = false;\n setIsLoading(true);\n\n resolveFromForm({\n templateId: p.templateId,\n formSchemaId: p.formSchemaId,\n sectionState: p.sectionState,\n themeId: p.themeId,\n supabaseUrl: p.supabaseUrl,\n supabaseAnonKey: p.supabaseAnonKey,\n })\n .then((resolved) => {\n if (!cancelled) {\n setResolvedConfig(resolved.config);\n setIsLoading(false);\n }\n })\n .catch((err) => {\n if (!cancelled) {\n setIsLoading(false);\n onError?.(err instanceof Error ? err : new Error(String(err)));\n }\n });\n\n return () => { cancelled = true; };\n }, [\n isResolveMode,\n // For resolve mode, re-resolve when these change\n isResolveMode ? (props as PixldocsPreviewResolveProps).templateId : undefined,\n isResolveMode ? (props as PixldocsPreviewResolveProps).formSchemaId : undefined,\n isResolveMode ? JSON.stringify((props as PixldocsPreviewResolveProps).sectionState) : undefined,\n isResolveMode ? (props as PixldocsPreviewResolveProps).themeId : undefined,\n ]);\n\n const config = isResolveMode ? resolvedConfig : (props as PixldocsPreviewConfigProps).config;\n\n if (isLoading) {\n return (\n <div className={className} style={{ ...style as any, display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: 200 }}>\n <div style={{ color: '#888', fontSize: 14 }}>Loading preview...</div>\n </div>\n );\n }\n\n if (!config) return null;\n\n return (\n <div className={className} style={style as any}>\n <AppPreviewCanvas\n config={config as any}\n pageIndex={pageIndex}\n zoom={zoom}\n absoluteZoom={absoluteZoom}\n onDynamicFieldClick={onDynamicFieldClick}\n onReady={onReady}\n />\n </div>\n );\n}\n","/**\n * Font Loader — Loads Google Fonts via CSS injection.\n * Framework-agnostic, works in any browser environment.\n */\n\nimport type { TemplateConfig } from './types';\n\n/** Fonts already loaded in this session */\nconst loadedFonts = new Set<string>();\n/** Loading promises to deduplicate concurrent requests */\nconst loadingPromises = new Map<string, Promise<void>>();\n\n/**\n * Load a Google Font by injecting a <link> stylesheet.\n * Resolves when the font is ready for use.\n */\nexport async function loadGoogleFontCSS(fontFamily: string): Promise<void> {\n if (!fontFamily || typeof document === 'undefined') return;\n if (loadedFonts.has(fontFamily)) return;\n\n const existing = loadingPromises.get(fontFamily);\n if (existing) return existing;\n\n const promise = (async () => {\n try {\n // Check if font is already available in the browser\n if (document.fonts?.check(`16px \"${fontFamily}\"`)) {\n loadedFonts.add(fontFamily);\n return;\n }\n\n // Inject Google Fonts stylesheet\n const weights = '100;200;300;400;500;600;700;800;900';\n const encoded = encodeURIComponent(fontFamily);\n const url = `https://fonts.googleapis.com/css2?family=${encoded}:ital,wght@0,${weights};1,${weights}&display=swap`;\n\n const link = document.createElement('link');\n link.rel = 'stylesheet';\n link.href = url;\n link.crossOrigin = 'anonymous';\n\n await new Promise<void>((resolve, reject) => {\n link.onload = () => resolve();\n link.onerror = () => reject(new Error(`Failed to load font: ${fontFamily}`));\n document.head.appendChild(link);\n });\n\n // Wait for the font to be usable\n if (document.fonts) {\n await document.fonts.load(`16px \"${fontFamily}\"`);\n await document.fonts.ready;\n }\n\n loadedFonts.add(fontFamily);\n } catch (e) {\n console.warn(`[@pixldocs/canvas-renderer] Font load failed: ${fontFamily}`, e);\n // Don't throw — missing font shouldn't break rendering\n }\n })();\n\n loadingPromises.set(fontFamily, promise);\n await promise;\n loadingPromises.delete(fontFamily);\n}\n\n/**\n * Collect all font families used in a template config.\n */\nexport function collectFontsFromConfig(config: TemplateConfig): Set<string> {\n const fonts = new Set<string>();\n fonts.add('Open Sans'); // Default fallback\n\n function walk(nodes: any[]) {\n if (!nodes) return;\n for (const node of nodes) {\n if (node.fontFamily) fonts.add(node.fontFamily);\n if (node.smartProps?.fontFamily) fonts.add(node.smartProps.fontFamily);\n if (node.children) walk(node.children);\n }\n }\n\n for (const page of config.pages || []) {\n walk(page.children || []);\n }\n\n return fonts;\n}\n","/**\n * Core Renderer — Uses the full PageCanvas/PreviewCanvas engine for 1:1 parity rendering.\n * Also provides a lightweight renderNodes fallback for simple templates.\n */\n\nimport { createRoot } from 'react-dom/client';\nimport { createElement } from 'react';\nimport type { TemplateConfig, CanvasNode } from './types';\nimport type { SectionFormState } from '@/lib/inferFormSchemaFromTemplate';\nimport { loadGoogleFontCSS, collectFontsFromConfig } from './font-loader';\nimport { resolveFromForm, resolveTemplateData } from './data-resolver';\nimport type { ResolveFromFormOptions } from './data-resolver';\n\nexport interface RendererConfig {\n /** Supabase project URL for fetching templates */\n supabaseUrl: string;\n /** Supabase anon key */\n supabaseAnonKey: string;\n /** Optional: image proxy URL for CORS-safe image loading */\n imageProxyUrl?: string;\n /** Optional: pixel ratio for high-DPI rendering (default: 2) */\n pixelRatio?: number;\n}\n\nexport interface RenderOptions {\n /** Page index to render (default: 0) */\n pageIndex?: number;\n /** Output format (default: 'png') */\n format?: 'png' | 'jpeg' | 'webp';\n /** Quality for jpeg/webp (0-1, default: 0.92) */\n quality?: number;\n /** Scale multiplier (default: 1) */\n scale?: number;\n /** Custom pixel ratio override */\n pixelRatio?: number;\n}\n\nexport interface RenderResult {\n /** Data URL of the rendered image */\n dataUrl: string;\n /** Width of the rendered image in CSS pixels */\n width: number;\n /** Height of the rendered image in CSS pixels */\n height: number;\n /** Actual pixel width (width * pixelRatio) */\n pixelWidth: number;\n /** Actual pixel height (height * pixelRatio) */\n pixelHeight: number;\n}\n\n/** Options for renderFromForm — matches the server API payload */\nexport interface RenderFromFormOptions extends Omit<RenderOptions, 'pageIndex'> {\n templateId: string;\n formSchemaId: string;\n sectionState: SectionFormState;\n themeId?: string;\n}\n\nexport class PixldocsRenderer {\n private config: RendererConfig;\n\n constructor(config: RendererConfig) {\n this.config = config;\n }\n\n /**\n * Render a pre-resolved template config to an image using the full PageCanvas engine.\n * Mounts a hidden PreviewCanvas component and captures the Fabric canvas output.\n */\n async render(templateConfig: TemplateConfig, options: RenderOptions = {}): Promise<RenderResult> {\n const pageIndex = options.pageIndex ?? 0;\n const format = options.format ?? 'png';\n const quality = options.quality ?? 0.92;\n const pixelRatio = options.pixelRatio ?? this.config.pixelRatio ?? 2;\n\n const canvasWidth = templateConfig.canvas.width;\n const canvasHeight = templateConfig.canvas.height;\n\n const page = templateConfig.pages[pageIndex];\n if (!page) {\n throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);\n }\n\n // Load fonts first\n const fonts = collectFontsFromConfig(templateConfig);\n await Promise.all([...fonts].map(f => loadGoogleFontCSS(f)));\n\n // Set up image proxy URL for the preview\n const { setPackageApiUrl } = await import('./shims/app-api');\n setPackageApiUrl(this.config.imageProxyUrl);\n\n // Mount a hidden PreviewCanvas and capture\n const dataUrl = await this.renderPageViaPreviewCanvas(\n templateConfig,\n pageIndex,\n pixelRatio,\n format,\n quality,\n );\n\n return {\n dataUrl,\n width: canvasWidth,\n height: canvasHeight,\n pixelWidth: canvasWidth * pixelRatio,\n pixelHeight: canvasHeight * pixelRatio,\n };\n }\n\n /**\n * Render all pages and return array of results.\n */\n async renderAllPages(\n templateConfig: TemplateConfig,\n options: Omit<RenderOptions, 'pageIndex'> = {},\n ): Promise<RenderResult[]> {\n const results: RenderResult[] = [];\n for (let i = 0; i < templateConfig.pages.length; i++) {\n results.push(await this.render(templateConfig, { ...options, pageIndex: i }));\n }\n return results;\n }\n\n /**\n * Resolve from V2 sectionState (like the server API) and render all pages.\n * This is the primary external API for the package.\n */\n async renderFromForm(options: RenderFromFormOptions): Promise<RenderResult[]> {\n const { templateId, formSchemaId, sectionState, themeId, ...renderOpts } = options;\n\n const resolved = await resolveFromForm({\n templateId,\n formSchemaId,\n sectionState,\n themeId,\n supabaseUrl: this.config.supabaseUrl,\n supabaseAnonKey: this.config.supabaseAnonKey,\n });\n\n return this.renderAllPages(resolved.config, renderOpts);\n }\n\n /**\n * Convenience: fetch by ID with simple flat data and render.\n */\n async renderById(\n templateId: string,\n formData?: Record<string, any>,\n options?: RenderOptions,\n ): Promise<RenderResult> {\n const resolved = await resolveTemplateData({\n templateId,\n formData,\n supabaseUrl: this.config.supabaseUrl,\n supabaseAnonKey: this.config.supabaseAnonKey,\n });\n return this.render(resolved.config, options);\n }\n\n // ─── Internal: render a page using the full PreviewCanvas engine ───\n\n /**\n * Wait until every image on the Fabric canvas inside the container has loaded.\n * Polls img elements since we can't hook into Fabric's internal load events.\n */\n private waitForCanvasImages(\n container: HTMLElement,\n maxWaitMs = 15000,\n pollMs = 200,\n ): Promise<void> {\n return new Promise<void>((resolve) => {\n const start = Date.now();\n const check = () => {\n const images = container.querySelectorAll('img') as NodeListOf<HTMLImageElement>;\n let allLoaded = true;\n images.forEach((img) => {\n if (!img.complete) allLoaded = false;\n });\n if (allLoaded || Date.now() - start > maxWaitMs) {\n // Give Fabric time to paint the loaded images onto the canvas\n requestAnimationFrame(() => setTimeout(resolve, 400));\n return;\n }\n setTimeout(check, pollMs);\n };\n setTimeout(check, 500);\n });\n }\n\n private getNormalizedGradientStops(gradient: any): Array<{ offset: number; color: string }> {\n const stops = Array.isArray(gradient?.stops)\n ? gradient.stops\n .map((stop: any) => ({\n offset: Math.max(0, Math.min(1, Number(stop?.offset ?? 0))),\n color: String(stop?.color ?? '#ffffff'),\n }))\n .filter((stop: { offset: number; color: string }) => Number.isFinite(stop.offset))\n .sort((a, b) => a.offset - b.offset)\n : [];\n\n if (stops.length === 0) return [];\n const normalized = [...stops];\n if (normalized[0].offset > 0) {\n normalized.unshift({ offset: 0, color: normalized[0].color });\n }\n if (normalized[normalized.length - 1].offset < 1) {\n normalized.push({ offset: 1, color: normalized[normalized.length - 1].color });\n }\n return normalized;\n }\n\n private paintPageBackground(\n ctx: CanvasRenderingContext2D,\n page: TemplateConfig['pages'][number] | undefined,\n width: number,\n height: number,\n ) {\n const backgroundColor = page?.settings?.backgroundColor || '#ffffff';\n const gradient = page?.settings?.backgroundGradient;\n\n ctx.clearRect(0, 0, width, height);\n ctx.fillStyle = backgroundColor;\n ctx.fillRect(0, 0, width, height);\n\n const stops = this.getNormalizedGradientStops(gradient);\n if (stops.length < 2) return;\n\n try {\n let canvasGradient: CanvasGradient | null = null;\n\n if (gradient?.type === 'radial') {\n const cx = Number.isFinite(gradient?.cx) ? gradient.cx : 0.5;\n const cy = Number.isFinite(gradient?.cy) ? gradient.cy : 0.5;\n const centerX = width * cx;\n const centerY = height * cy;\n const radius = Math.max(\n Math.hypot(centerX, centerY),\n Math.hypot(width - centerX, centerY),\n Math.hypot(centerX, height - centerY),\n Math.hypot(width - centerX, height - centerY),\n );\n canvasGradient = ctx.createRadialGradient(centerX, centerY, 0, centerX, centerY, radius);\n } else if (gradient?.type === 'conic' && typeof (ctx as any).createConicGradient === 'function') {\n const cx = Number.isFinite(gradient?.cx) ? gradient.cx : 0.5;\n const cy = Number.isFinite(gradient?.cy) ? gradient.cy : 0.5;\n const startAngle = (((gradient?.angle ?? 0) - 90) * Math.PI) / 180;\n canvasGradient = (ctx as any).createConicGradient(startAngle, width * cx, height * cy);\n } else {\n const angleDeg = gradient?.angle ?? 90;\n const angleRad = (angleDeg * Math.PI) / 180;\n const sinA = Math.sin(angleRad);\n const cosA = Math.cos(angleRad);\n const midX = width / 2;\n const midY = height / 2;\n const corners = [\n [0, 0],\n [width, 0],\n [width, height],\n [0, height],\n ];\n const projections = corners.map(([x, y]) => x * sinA - y * cosA);\n const minProjection = Math.min(...projections);\n const maxProjection = Math.max(...projections);\n canvasGradient = ctx.createLinearGradient(\n midX + minProjection * sinA,\n midY - minProjection * cosA,\n midX + maxProjection * sinA,\n midY - maxProjection * cosA,\n );\n }\n\n if (!canvasGradient) return;\n\n stops.forEach((stop) => canvasGradient!.addColorStop(stop.offset, stop.color));\n ctx.fillStyle = canvasGradient;\n ctx.fillRect(0, 0, width, height);\n } catch {\n // Fall back to solid backgroundColor only.\n }\n }\n\n private async renderPageViaPreviewCanvas(\n config: TemplateConfig,\n pageIndex: number,\n pixelRatio: number,\n format: string,\n quality: number,\n ): Promise<string> {\n // Dynamically import PreviewCanvas to avoid circular deps at module load\n const { PreviewCanvas } = await import('../../../src/components/PreviewCanvas');\n\n const canvasWidth = config.canvas.width;\n const canvasHeight = config.canvas.height;\n\n return new Promise<string>((resolve, reject) => {\n // Create a hidden container\n const container = document.createElement('div');\n container.style.cssText = `\n position: fixed; left: -99999px; top: -99999px;\n width: ${canvasWidth}px; height: ${canvasHeight}px;\n overflow: hidden; pointer-events: none; opacity: 0;\n `;\n document.body.appendChild(container);\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Render timeout (30s)'));\n }, 30000);\n\n const cleanup = () => {\n clearTimeout(timeout);\n try {\n root.unmount();\n } catch {}\n container.remove();\n };\n\n const onReady = () => {\n // Wait for all images (photos, SVGs, etc.) to fully load before capture\n this.waitForCanvasImages(container).then(() => {\n try {\n const fabricCanvas = container.querySelector('canvas.upper-canvas, canvas') as HTMLCanvasElement;\n if (!fabricCanvas) {\n cleanup();\n reject(new Error('No canvas element found after render'));\n return;\n }\n const lowerCanvas = container.querySelector('canvas.lower-canvas') as HTMLCanvasElement;\n const sourceCanvas = lowerCanvas || fabricCanvas;\n const exportCanvas = document.createElement('canvas');\n exportCanvas.width = sourceCanvas.width;\n exportCanvas.height = sourceCanvas.height;\n const exportCtx = exportCanvas.getContext('2d');\n if (!exportCtx) {\n cleanup();\n reject(new Error('Failed to create export canvas'));\n return;\n }\n\n exportCtx.save();\n exportCtx.scale(sourceCanvas.width / canvasWidth, sourceCanvas.height / canvasHeight);\n this.paintPageBackground(exportCtx, config.pages[pageIndex], canvasWidth, canvasHeight);\n exportCtx.restore();\n exportCtx.drawImage(sourceCanvas, 0, 0);\n\n const mimeType =\n format === 'jpeg' ? 'image/jpeg' : format === 'webp' ? 'image/webp' : 'image/png';\n const dataUrl = exportCanvas.toDataURL(mimeType, quality);\n cleanup();\n resolve(dataUrl);\n } catch (err) {\n cleanup();\n reject(err);\n }\n });\n };\n\n const root = createRoot(container);\n root.render(\n createElement(PreviewCanvas, {\n config: config as any,\n pageIndex,\n zoom: pixelRatio,\n absoluteZoom: true,\n onReady,\n }),\n );\n });\n }\n}\n","/**\n * Theme — Apply theme variable overrides to a template config.\n */\n\nimport type { TemplateConfig, CanvasNode } from './types';\n\nexport interface ThemeVariables {\n [variableName: string]: string;\n}\n\n/**\n * Apply theme variable overrides to a template config.\n * Walks the tree and replaces any property value that matches a theme variable reference.\n * \n * Theme variables in config look like: {{theme.variableName}}\n * Or are stored in themeConfig.variables as { [name]: { value, label } }\n */\nexport function applyThemeToConfig(\n config: TemplateConfig,\n themeOverrides: ThemeVariables\n): TemplateConfig {\n if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;\n\n const cloned = JSON.parse(JSON.stringify(config)) as TemplateConfig;\n\n // Update themeConfig.variables with overrides\n if (cloned.themeConfig?.variables) {\n for (const [key, value] of Object.entries(themeOverrides)) {\n if (cloned.themeConfig.variables[key]) {\n cloned.themeConfig.variables[key].value = value;\n }\n }\n }\n\n // Build a map of variable name -> new value\n const varMap = new Map<string, string>();\n if (cloned.themeConfig?.variables) {\n for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {\n varMap.set(key, themeOverrides[key] ?? def.value);\n }\n }\n\n // Walk tree and apply theme bindings\n function walkAndApply(nodes: CanvasNode[]) {\n if (!nodes) return;\n for (const node of nodes) {\n // Check for theme bindings on the node\n const bindings = (node as any).themeBindings as Record<string, string> | undefined;\n if (bindings) {\n for (const [prop, varName] of Object.entries(bindings)) {\n const value = varMap.get(varName);\n if (value !== undefined) {\n (node as any)[prop] = value;\n }\n }\n }\n if (node.children) walkAndApply(node.children);\n }\n }\n\n for (const page of cloned.pages || []) {\n // Apply to page settings\n const bgBinding = (page as any).themeBindings?.backgroundColor;\n if (bgBinding && varMap.has(bgBinding) && page.settings) {\n page.settings.backgroundColor = varMap.get(bgBinding)!;\n }\n walkAndApply(page.children || []);\n }\n\n return cloned;\n}\n"],"names":["fabric","cloneNodeWithNewIds","create","id","expandedIds","_a","loadingPromises","url","minDim","buildRoundedRectPath","startX","startY","buildCanonicalRoundedRectPath","forwardRef","useRef","useState","useMemo","useEffect","useCallback","useImperativeHandle","canvas","zoom","scaledWidth","scaledHeight","elements","selectedGroup","flushSync","currentPage","transform","corner","updateElement","_b","_c","modifiedTarget","pageChildren","e","commitHistory","shouldSkipUpdates","isTransforming","fc","isDynamicField","canBeEvented","baseScaleX","baseScaleY","needCropGroup","elementWidth","elementHeight","jsxs","jsx","isActive","Fragment","fields","setInTree","direct","canonicalSuffix","nestedMatch","match","cloned","baseId","nestedStayCloned","stayVersion","overflowEntryClone","overflowVersion","AppPreviewCanvas","setPackageApiUrl","PreviewCanvas","createRoot","createElement"],"mappings":"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"}