export type CanvasLayerKind = 'image' | 'video' | 'audio' | 'text' | 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'star' | 'hexagon' | 'pentagon' | 'diamond' | 'heart' | 'line' | 'arrow' | 'path' | 'sticky' | 'canvasPanel' | 'overlay' | 'border' | 'hBox' | 'vBox' | 'sizeBox' | 'scaleBox' | 'group' | 'widget' | 'namedSlot' | 'scrollBox' | 'wrapBox' | 'uniformGrid'; export type CanvasObjectFit = 'cover' | 'contain' | 'fill'; export type CanvasTextAlign = 'left' | 'center' | 'right'; export type CanvasFontStyle = 'normal' | 'italic'; export type CanvasTextDecoration = 'none' | 'underline' | 'line-through'; export type CanvasAlign = 'left' | 'center-h' | 'right' | 'top' | 'center-v' | 'bottom' | 'center' | 'full'; /** Normalized point inside a layer rect (0–1). */ export interface CanvasPoint { x: number; y: number; } export interface CanvasLayerRect { x: number; y: number; w: number; h: number; } /** UMG-style anchors (0–1 normalized against parent). */ export interface CanvasAnchors { minX: number; minY: number; maxX: number; maxY: number; } /** UMG slot: anchors + offsets in px relative to anchored edges. */ export interface CanvasSlot { anchors: CanvasAnchors; offsets: { left: number; top: number; right: number; bottom: number; }; padding?: { left: number; top: number; right: number; bottom: number; }; sizeRule?: 'auto' | 'fill' | 'fixed'; alignment?: { x: number; y: number; }; order?: number; } export interface CanvasExposedProp { id: string; targetLayerId: string; field: string; label: string; /** true = editable on instances (eye open) */ exposed: boolean; } export interface CanvasWidgetDefinition { id: string; name: string; width: number; height: number; background?: string; layers: CanvasLayer[]; exposed: CanvasExposedProp[]; } export interface CanvasLayer { id: string; kind: CanvasLayerKind; name: string; src?: string; text?: string; fill?: string; stroke?: string; strokeWidth?: number; fontSize?: number; fontWeight?: number | string; fontFamily?: string; fontStyle?: CanvasFontStyle; textDecoration?: CanvasTextDecoration; letterSpacing?: number; lineHeight?: number; textAlign?: CanvasTextAlign; /** * When true, resizing the text box scales `fontSize` with the box * (geometric mean of width/height scale — Unreal/Canva-style). */ autoSize?: boolean; /** * When true, resizing this container scales children (position + size + text) * relative to the parent — same idea as text `autoSize`. Groups default on. */ autoSizeChildren?: boolean; color?: string; /** Text box background */ textBackground?: string; objectFit?: CanvasObjectFit; borderRadius?: number; rotation?: number; flipX?: boolean; flipY?: boolean; shadowBlur?: number; shadowColor?: string; blur?: number; /** For `path`: normalized points (0–1) relative to rect. */ points?: CanvasPoint[]; /** For `path`: close the shape into a filled polygon. */ closed?: boolean; /** Parent layer id; null/undefined = artboard root. */ parentId?: string | null; /** UMG slot (preferred). When missing, derived from rect + top-left anchors. */ slot?: CanvasSlot; clipChildren?: boolean; /** Gap for hBox / vBox / wrapBox / uniformGrid */ gap?: number; /** namedSlot: public slot name exposed on the User Widget */ slotName?: string; /** Child of a widget instance: which named slot to fill */ fillSlot?: string; /** uniformGrid column count */ columns?: number; /** kind === 'widget' */ definitionId?: string; /** Exposed prop overrides on widget instances */ overrides?: Record; rect: CanvasLayerRect; zIndex: number; opacity: number; visible: boolean; locked: boolean; } export type CanvasGuideOrientation = 'horizontal' | 'vertical'; export interface CanvasGuide { id: string; orientation: CanvasGuideOrientation; /** X for vertical, Y for horizontal (document px). */ position: number; locked?: boolean; } export interface CanvasDocument { version: 1 | 2; width: number; height: number; background: string; layers: CanvasLayer[]; guides?: CanvasGuide[]; /** When true, all guides are locked. */ guidesLocked?: boolean; /** Local User Widget library (v2). */ widgets?: CanvasWidgetDefinition[]; } /** Top-left point anchors; offsets encode x/y/w/h without needing parent size. */ export declare function defaultSlotFromRect(rect: CanvasLayerRect, anchors?: CanvasAnchors): CanvasSlot; /** For top-left point anchors, offsets.left/top = x/y and right/bottom = -(x+w)/-(y+h) when parent size unknown. * Prefer slotFromLocalRect from canvasHierarchy when parent size is known. */ export declare function rectFromSlot(slot: CanvasSlot, parentSize: { width: number; height: number; }): CanvasLayerRect; export declare function emptyCanvasDocument(partial?: Partial> & { layers?: CanvasLayer[]; widgets?: CanvasWidgetDefinition[]; }): CanvasDocument; /** Migrate v1 (flat absolute layers) → v2 (parentId + slot). */ export declare function migrateCanvasDocument(doc: CanvasDocument | (Omit & { version?: number; })): CanvasDocument; export declare function ensureLayerSlot(layer: CanvasLayer): CanvasLayer; export declare function createCanvasGuide(orientation: CanvasGuideOrientation, position: number, partial?: Partial>): CanvasGuide; export declare function createCanvasLayer(kind: CanvasLayerKind, partial?: Partial>): CanvasLayer; /** Scalar fields that can reset to createCanvasLayer defaults (UE-style details). */ export type CanvasResettableField = 'opacity' | 'rotation' | 'visible' | 'locked' | 'flipX' | 'flipY' | 'blur' | 'shadowBlur' | 'shadowColor' | 'fill' | 'stroke' | 'strokeWidth' | 'fontSize' | 'fontWeight' | 'fontFamily' | 'fontStyle' | 'textDecoration' | 'letterSpacing' | 'lineHeight' | 'textAlign' | 'autoSize' | 'color' | 'textBackground' | 'objectFit' | 'borderRadius' | 'gap' | 'columns' | 'clipChildren' | 'autoSizeChildren' | 'text' | 'name'; /** Default value for a layer field (same as createCanvasLayer would use). */ export declare function canvasLayerFieldDefault(kind: CanvasLayerKind, field: CanvasResettableField): unknown; export declare function isCanvasFieldModified(layer: CanvasLayer, field: CanvasResettableField): boolean; export declare function resetCanvasField(layer: CanvasLayer, field: CanvasResettableField): Partial; /** * Scale fontSize with the box when `autoSize` is on (text / sticky). * Uses geometric mean of width & height scale so both axes affect size. */ export declare function applyTextAutoSize(from: CanvasLayer, to: CanvasLayer, prev: Pick, next: Pick): CanvasLayer; export declare function reorderCanvasLayers(layers: CanvasLayer[], orderedIds: string[]): CanvasLayer[]; export declare function updateCanvasLayer(doc: CanvasDocument, layerId: string, updater: (l: CanvasLayer) => CanvasLayer): CanvasDocument; export declare function updateCanvasLayers(doc: CanvasDocument, ids: string[], updater: (l: CanvasLayer) => CanvasLayer): CanvasDocument; export declare function snapLayerRect(rect: CanvasLayerRect, cellSize: number, enabled: boolean, bounds: { width: number; height: number; }, minW?: number, minH?: number, guides?: Pick[], guideThreshold?: number, clampToBounds?: boolean): CanvasLayerRect; /** Snap left / center / right (or top / middle / bottom) edges to guides. */ export declare function snapRectToGuides(rect: CanvasLayerRect, guides: Pick[], threshold?: number): CanvasLayerRect; export declare function alignLayerRect(rect: CanvasLayerRect, bounds: { width: number; height: number; }, align: CanvasAlign): CanvasLayerRect; export declare const CANVAS_PRESETS: readonly [{ readonly id: "hd"; readonly label: "HD · 1280×720"; readonly width: 1280; readonly height: 720; }, { readonly id: "fhd"; readonly label: "Full HD · 1920×1080"; readonly width: 1920; readonly height: 1080; }, { readonly id: "square"; readonly label: "Instagram · 1080×1080"; readonly width: 1080; readonly height: 1080; }, { readonly id: "story"; readonly label: "Story · 1080×1920"; readonly width: 1080; readonly height: 1920; }, { readonly id: "landscape"; readonly label: "Landscape · 1600×900"; readonly width: 1600; readonly height: 900; }, { readonly id: "presentation"; readonly label: "Presentation · 1920×1080"; readonly width: 1920; readonly height: 1080; }, { readonly id: "a4"; readonly label: "A4 · 794×1123"; readonly width: 794; readonly height: 1123; }, { readonly id: "twitter"; readonly label: "X Post · 1600×900"; readonly width: 1600; readonly height: 900; }]; export declare function presetIdForSize(width: number, height: number): string; /** Build a path layer from absolute document points. */ export declare function createPathFromDocPoints(docPoints: { x: number; y: number; }[], opts?: { closed?: boolean; fill?: string; strokeWidth?: number; name?: string; zIndex?: number; }): CanvasLayer; /** Recompute rect + normalized points after editing absolute doc points. */ export declare function rebakePathLayer(layer: CanvasLayer, docPoints: { x: number; y: number; }[]): CanvasLayer; /** Map normalized path points → document space using an absolute (or local) rect. */ export declare function pathPointsToDoc(layer: CanvasLayer, absRect?: Pick): { x: number; y: number; }[];