/**
 * Design-canvas scene model — the product-agnostic document behind the visual
 * asset editor. A document is an ordered list of pages; a page is an ordered
 * list of elements (index order IS z-order, bottom→top); every element is a
 * typed node with a closed attribute vocabulary. The whole document
 * serializes as one JSON value and persists atomically with a revision
 * counter (see ./store) — every canvas action is a durable operation against
 * this schema, which is what makes the canvas automatable: agents and
 * external data sources mutate the same document the editor renders.
 *
 * Units are CSS pixels throughout; `settings.dpi` carries the print
 * conversion factor for bleed/trim-aware exports. Angles are degrees.
 * Nothing here touches Konva, React, or a database.
 */
declare const SCENE_SCHEMA_VERSION = 1;
interface SceneDocument {
    schemaVersion: typeof SCENE_SCHEMA_VERSION;
    title: string;
    pages: ScenePage[];
    settings: SceneSettings;
    metadata: Record<string, unknown>;
}
interface SceneSettings {
    /** Print conversion factor for mm↔px math at export; 96 = CSS default. */
    dpi: number;
}
/** Per-side bleed extents in px, drawn OUTSIDE the page bounds. Trim = the
 *  page rect itself; exports may include or exclude the bleed area. */
interface PageBleed {
    top: number;
    right: number;
    bottom: number;
    left: number;
}
/** Saved ruler guides, in page coordinates. */
interface PageGuides {
    vertical: number[];
    horizontal: number[];
}
interface ScenePage {
    id: string;
    name: string;
    width: number;
    height: number;
    /** Page background fill (color string); elements paint over it. */
    background: string;
    bleed: PageBleed | null;
    guides: PageGuides;
    elements: SceneElement[];
}
/** Attributes every element carries. `x`/`y` are the element's top-left in
 *  page coordinates (for `group`, children are relative to the group origin).
 *  `slot` names a template binding point — `apply_data` targets it. */
interface SceneElementBase {
    id: string;
    name: string;
    x: number;
    y: number;
    /** Degrees, clockwise, about the element's top-left origin (Konva default). */
    rotation: number;
    /** 0..1 */
    opacity: number;
    locked: boolean;
    visible: boolean;
    slot?: string;
}
interface RectElement extends SceneElementBase {
    kind: 'rect';
    width: number;
    height: number;
    fill: string;
    stroke?: string;
    strokeWidth?: number;
    cornerRadius?: number;
}
interface EllipseElement extends SceneElementBase {
    kind: 'ellipse';
    width: number;
    height: number;
    fill: string;
    stroke?: string;
    strokeWidth?: number;
}
interface LineElement extends SceneElementBase {
    kind: 'line';
    /** Flat [x0, y0, x1, y1, ...] relative to (x, y); ≥ 2 points. */
    points: number[];
    stroke: string;
    strokeWidth: number;
    dash?: number[];
}
interface TextElement extends SceneElementBase {
    kind: 'text';
    text: string;
    /** Wrap width; height derives from content. */
    width: number;
    fontFamily: string;
    fontSize: number;
    fontStyle: 'normal' | 'bold' | 'italic' | 'bold italic';
    fill: string;
    align: 'left' | 'center' | 'right';
    lineHeight: number;
    letterSpacing: number;
}
interface ImageElement extends SceneElementBase {
    kind: 'image';
    width: number;
    height: number;
    /** http(s) or rooted /api/ path — same boundary rule as sequences media. */
    src: string;
    /** How the source maps into the frame; 'fill' stretches, 'cover' crops. */
    fit: 'fill' | 'cover' | 'contain';
}
/** Video placed on a canvas renders and exports as its poster frame — motion
 *  belongs to the sequences surface; this keeps video assets placeable in
 *  static layouts (e.g. a thumbnail mock) without a playback engine. */
interface VideoElement extends SceneElementBase {
    kind: 'video';
    width: number;
    height: number;
    src: string;
    posterSrc?: string;
}
interface GroupElement extends SceneElementBase {
    kind: 'group';
    children: SceneElement[];
}
type SceneElement = RectElement | EllipseElement | LineElement | TextElement | ImageElement | VideoElement | GroupElement;
type SceneElementKind = SceneElement['kind'];
declare const SCENE_ELEMENT_KINDS: readonly SceneElementKind[];
interface Bounds {
    x: number;
    y: number;
    width: number;
    height: number;
}
/** Unrotated local extent of an element (line/group derive from content). */
declare function elementExtent(element: SceneElement): {
    width: number;
    height: number;
};
declare function estimateTextHeight(element: Pick<TextElement, 'text' | 'fontSize' | 'lineHeight'>): number;
/** Axis-aligned bounding box in the parent's coordinate space, accounting for
 *  rotation about the element's top-left origin. */
declare function elementAabb(element: SceneElement): Bounds;
declare function boundsIntersect(a: Bounds, b: Bounds): boolean;
declare function requirePage(document: SceneDocument, pageId: string): ScenePage;
/** Depth-first search across a page including group children. Returns the
 *  element and the array that owns it (page.elements or a group's children),
 *  so callers can splice in place. */
declare function findElement(page: ScenePage, elementId: string): {
    element: SceneElement;
    owner: SceneElement[];
    index: number;
} | null;
declare function requireElement(page: ScenePage, elementId: string): {
    element: SceneElement;
    owner: SceneElement[];
    index: number;
};
/** All slot names declared across the document — the template's fillable
 *  surface. Duplicate slot names are a validation error (see ./validate). */
declare function collectSlots(document: SceneDocument): Map<string, {
    pageId: string;
    elementId: string;
    kind: SceneElementKind;
}>;
interface NewPageOptions {
    name?: string;
    width?: number;
    height?: number;
    background?: string;
}
declare function createEmptyDocument(title: string, page?: NewPageOptions): SceneDocument;
declare function createPage(options: NewPageOptions, id: string): ScenePage;
declare function assertPositiveFinite(value: number, label: string): void;
declare function assertFinite(value: number, label: string): void;
declare function assertColor(value: string, label: string): void;
/** Media boundary rule shared with sequences: remote http(s) or a rooted
 *  /api/ path — never sandbox-local files or data:/blob: blobs. Delegates to the
 *  canonical `assertMediaUrl` boundary in `../web`; the two surfaces share ONE
 *  rule (trims, named-rejects local/inline schemes) so they cannot drift. */
declare function assertSceneMediaSrc(value: string, label: string): void;

export { type Bounds as B, type EllipseElement as E, type GroupElement as G, type ImageElement as I, type LineElement as L, type NewPageOptions as N, type PageBleed as P, type RectElement as R, SCENE_ELEMENT_KINDS as S, type TextElement as T, type VideoElement as V, type PageGuides as a, SCENE_SCHEMA_VERSION as b, type SceneDocument as c, type SceneElement as d, type SceneElementBase as e, type SceneElementKind as f, type ScenePage as g, type SceneSettings as h, assertColor as i, assertFinite as j, assertPositiveFinite as k, assertSceneMediaSrc as l, boundsIntersect as m, collectSlots as n, createEmptyDocument as o, createPage as p, elementAabb as q, elementExtent as r, estimateTextHeight as s, findElement as t, requireElement as u, requirePage as v };
