import type { CanvasAnchors, CanvasDocument, CanvasLayer, CanvasLayerKind, CanvasLayerRect, CanvasSlot } from './canvasDocument.js'; export declare const CONTAINER_KINDS: ReadonlySet; export declare const LAYOUT_BOX_KINDS: ReadonlySet; /** Parents that own child X/Y (UMG slot layout). Group / panel / overlay stay freeform. */ export declare const LAYOUT_POSITION_KINDS: ReadonlySet; /** Parents that own child W/H (cell / uniform scale). */ export declare const LAYOUT_SIZE_KINDS: ReadonlySet; /** * Structural shells whose empty area should start a marquee (not a layer drag), * matching Figma-style select-drag on the canvas / inside frames. * Groups are omitted: they behave like a single object (click/drag the group). * Widgets pass when unselected so flattened instance bounds don't eat rubber-band; * a selected widget still drag-moves. */ export declare const MARQUEE_PASS_KINDS: ReadonlySet; export declare function isContainerKind(kind: CanvasLayerKind): boolean; export declare function isMarqueePassThroughKind(kind: CanvasLayerKind): boolean; export declare function canHaveChildren(layer: CanvasLayer): boolean; export declare function getChildren(layers: CanvasLayer[], parentId: string | null): CanvasLayer[]; export declare function getDescendantIds(layers: CanvasLayer[], rootId: string): string[]; export declare function getSubtreeIds(layers: CanvasLayer[], rootId: string): string[]; export declare function getAncestors(layers: CanvasLayer[], layerId: string): CanvasLayer[]; /** Outermost `group` ancestor, or null if the layer is not inside a group. */ export declare function enclosingGroupId(layers: CanvasLayer[], layerId: string): string | null; /** True when the layer and every ancestor is visible (flat stage paint must check this). */ export declare function isEffectivelyVisible(layers: CanvasLayer[], layerId: string, map?: Map): boolean; /** True when the layer or any ancestor is locked (parent lock freezes the whole subtree). */ export declare function isEffectivelyLocked(layers: CanvasLayer[], layerId: string, map?: Map): boolean; /** Child X/Y is owned by an hBox / vBox / wrap / grid parent. */ export declare function isLayoutPositionLocked(layers: CanvasLayer[], layerId: string, map?: Map): boolean; /** Child W/H is owned by a grid / scaleBox parent. */ export declare function isLayoutSizeLocked(layers: CanvasLayer[], layerId: string, map?: Map): boolean; /** * Ancestors to draw while a nested layer is selected (frame clip bounds). * Skips the artboard, the current selection, and `group` — groups hug the * children AABB, so outlining them looks like extra selection. */ export declare function selectionAncestorIds(layers: CanvasLayer[], selectedIds: string[]): string[]; /** * CSS transform for a flat-stage layer so ancestor rotations orbit children * around each ancestor's center (DOM is not nested). * Uses transform-origin top-left (0 0) of the layer box. */ export declare function paintTransformForLayer(layers: CanvasLayer[], layerId: string, absMap: Map, map?: Map): string | undefined; export declare function isDescendant(layers: CanvasLayer[], maybeChildId: string, ancestorId: string): boolean; export declare function canReparent(layers: CanvasLayer[], layerId: string, newParentId: string | null): boolean; export declare const ANCHOR_PRESETS: { readonly topLeft: { readonly minX: 0; readonly minY: 0; readonly maxX: 0; readonly maxY: 0; }; readonly topCenter: { readonly minX: 0.5; readonly minY: 0; readonly maxX: 0.5; readonly maxY: 0; }; readonly topRight: { readonly minX: 1; readonly minY: 0; readonly maxX: 1; readonly maxY: 0; }; readonly centerLeft: { readonly minX: 0; readonly minY: 0.5; readonly maxX: 0; readonly maxY: 0.5; }; readonly center: { readonly minX: 0.5; readonly minY: 0.5; readonly maxX: 0.5; readonly maxY: 0.5; }; readonly centerRight: { readonly minX: 1; readonly minY: 0.5; readonly maxX: 1; readonly maxY: 0.5; }; readonly bottomLeft: { readonly minX: 0; readonly minY: 1; readonly maxX: 0; readonly maxY: 1; }; readonly bottomCenter: { readonly minX: 0.5; readonly minY: 1; readonly maxX: 0.5; readonly maxY: 1; }; readonly bottomRight: { readonly minX: 1; readonly minY: 1; readonly maxX: 1; readonly maxY: 1; }; readonly stretch: { readonly minX: 0; readonly minY: 0; readonly maxX: 1; readonly maxY: 1; }; readonly stretchHorizontal: { readonly minX: 0; readonly minY: 0; readonly maxX: 1; readonly maxY: 0; }; readonly stretchVertical: { readonly minX: 0; readonly minY: 0; readonly maxX: 0; readonly maxY: 1; }; }; /** Resolve UMG slot against parent content size → local rect. */ export declare function resolveSlotRect(parentSize: { width: number; height: number; }, slot: CanvasSlot): CanvasLayerRect; /** Build slot offsets from a local rect + anchors (keeps anchors, recomputes offsets). */ export declare function slotFromLocalRect(parentSize: { width: number; height: number; }, rect: CanvasLayerRect, anchors?: CanvasAnchors): CanvasSlot; /** * Translate a UMG slot without changing size. * `right` / `bottom` are inverted (distance from the opposite anchored edge). */ export declare function translateSlot(slot: CanvasSlot, dx: number, dy: number): CanvasSlot; /** * Keyboard nudge along one axis. * With snap: land on the next/previous grid line (if already on-grid, move one cell; Shift = two). */ export declare function stepAxis(value: number, dir: 1 | -1, opts: { snap: boolean; cell: number; coarse?: boolean; }): number; export declare function contentPadding(layer: CanvasLayer): { left: number; top: number; right: number; bottom: number; }; export declare function contentSize(layerRect: CanvasLayerRect, layer: CanvasLayer): { width: number; height: number; }; /** * Compute absolute (document) rect for every layer via parent walk + layout boxes. * Returns map layerId → absolute rect. */ export declare function computeAbsoluteRects(layers: CanvasLayer[], rootSize: { width: number; height: number; }): Map; export declare function absoluteRectFor(layerId: string, layers: CanvasLayer[], rootSize: { width: number; height: number; }): CanvasLayerRect | null; /** * CSS clip-path inset for a child absolute rect clipped by ancestors with clipChildren. * Values are relative to the child's own box. * `scrollBox` always clips (UMG-style viewport). */ export declare function clipPathForLayer(layerId: string, layers: CanvasLayer[], absMap: Map): string | undefined; /** Scrollbar thumb size/position for a scrollBox viewport (preview chrome). */ export declare function scrollBarMetrics(viewport: number, maxScroll: number, offset: number): { thumb: number; offset: number; track: number; } | null; /** Max scrollable overflow of a scrollBox (content extent beyond the viewport). */ export declare function scrollBoxOverflow(scrollBoxId: string, layers: CanvasLayer[], absMap: Map): { maxX: number; maxY: number; }; /** * Shift descendants of scrollBoxes by ephemeral scroll offsets (preview only). * ScrollBox frames themselves stay put. */ export declare function applyScrollBoxOffsets(layers: CanvasLayer[], absMap: Map, offsets: ReadonlyMap): Map; export declare function reparentLayer(layers: CanvasLayer[], layerId: string, newParentId: string | null, rootSize: { width: number; height: number; }): CanvasLayer[]; /** Wrap one or more layers in a new parent (`group`, `hBox`, `border`, …). */ export declare function wrapSelection(doc: CanvasDocument, selectedIds: string[], wrapKind?: CanvasLayerKind): { doc: CanvasDocument; wrapperId: string; } | null; export declare function groupLayers(doc: CanvasDocument, selectedIds: string[]): { doc: CanvasDocument; wrapperId: string; } | null; export declare function ungroupLayers(doc: CanvasDocument, groupId: string): CanvasDocument; /** Delete layer and all descendants. */ export declare function deleteSubtree(doc: CanvasDocument, ids: string[]): CanvasDocument; /** Duplicate subtree with new ids; keeps relative hierarchy. */ export declare function duplicateSubtree(doc: CanvasDocument, rootIds: string[], offset?: { x: number; y: number; }): { doc: CanvasDocument; newIds: string[]; }; export declare function reorderSiblings(layers: CanvasLayer[], parentId: string | null, orderedChildIds: string[]): CanvasLayer[]; /** Sync layer.rect from its slot given parent size (for editors that edit rect). */ export declare function syncRectFromSlot(layer: CanvasLayer, parentSize: { width: number; height: number; }): CanvasLayer; export declare function syncSlotFromRect(layer: CanvasLayer, parentSize: { width: number; height: number; }): CanvasLayer; /** Write an absolute rect back to layer.rect / slot in parent-local space. */ export declare function layerFromAbsoluteRect(layer: CanvasLayer, absRect: CanvasLayerRect, parentAbs: CanvasLayerRect | null, parentLayer: CanvasLayer | null, rootSize: { width: number; height: number; }): CanvasLayer; /** * Figma-style groups: origin + size is the AABB of direct children. * Children keep their absolute positions (local coords are rebased). * Nested groups are fitted innermost-first. */ export declare function fitGroupsToChildren(layers: CanvasLayer[], rootSize: { width: number; height: number; }): CanvasLayer[]; /** Groups default on; other containers opt in via `autoSizeChildren`. */ export declare function shouldAutoSizeChildren(layer: CanvasLayer): boolean; export declare function topLevelSelectedIds(layers: CanvasLayer[], ids: string[]): string[]; export declare function unionAbsRect(absMap: Map, ids: string[]): CanvasLayerRect | null; /** Clamp a rigid-body delta so `union` stays on the artboard (snap-to-bounds). */ export declare function clampDeltaToKeepUnion(union: CanvasLayerRect, dx: number, dy: number, bounds: { width: number; height: number; }): { dx: number; dy: number; }; /** Selected group/container that contains `layerId`, if any. */ export declare function selectedAncestorId(layers: CanvasLayer[], layerId: string, selectedIds: Iterable): string | null; /** * Scale a group (or any subtree) from `prevAbs` to `nextAbs`. * Descendants keep their relative placement inside the box unless * `scaleDescendants` is false (then they keep world position). */ export declare function scaleSubtreeAbsolute(layers: CanvasLayer[], rootId: string, prevAbs: CanvasLayerRect, nextAbs: CanvasLayerRect, rootSize: { width: number; height: number; }, opts?: { scaleDescendants?: boolean; }): CanvasLayer[]; /** * Scale (or move) a multi-selection as one AABB — Figma/Canva transform box. * Top-level selected layers map into `nextBox`; nested children follow if the * parent has auto-size children (groups default on). */ export declare function scaleSelectionAbsolute(layers: CanvasLayer[], selectedIds: string[], prevBox: CanvasLayerRect, nextBox: CanvasLayerRect, rootSize: { width: number; height: number; }): CanvasLayer[]; /** * Translate a multi-selection as one rigid body. Only top-level selected layers * move; descendants keep parent-local coords so a group+child selection does not * double-shift nested items. */ export declare function translateSelectionAbsolute(layers: CanvasLayer[], selectedIds: string[], dx: number, dy: number, rootSize: { width: number; height: number; }, absMap?: Map): CanvasLayer[];