/** Serializable authoring state. Rendered trees are projections of this document. */ export type DocumentValue = null | boolean | number | string | DocumentValue[] | { [key: string]: DocumentValue; }; export type DocumentMatrix = [number, number, number, number, number, number]; export type DocumentNodeKind = 'root' | 'frame' | 'group' | 'component' | 'instance' | 'slot' | 'text' | 'vector'; export interface DocumentLayout { direction: 'horizontal' | 'vertical' | 'grid'; gap: number; padding: number; align?: 'start' | 'center' | 'end' | 'stretch'; sizing?: 'fixed' | 'hug'; crossSizing?: 'fixed' | 'hug'; wrap?: boolean; crossGap?: number; distribute?: 'start' | 'center' | 'end' | 'space-between'; paddingTop?: number; paddingRight?: number; paddingBottom?: number; paddingLeft?: number; columns?: number; minWidth?: number; maxWidth?: number; minHeight?: number; maxHeight?: number; } export type DocumentConstraint = 'start' | 'end' | 'center' | 'stretch' | 'scale'; /** How a child follows its parent's resize; an omitted axis behaves as `start`. */ export interface DocumentNodeConstraints { horizontal?: DocumentConstraint; vertical?: DocumentConstraint; } export interface DocumentNodePatch { /** Nested instance target only; ordinary patch commands use swapInstance instead. */ definitionId?: string; name?: string; transform?: DocumentMatrix; width?: number; height?: number; layout?: DocumentLayout | null; constraints?: DocumentNodeConstraints; properties?: Record; } export interface DocumentNode { id: string; kind: DocumentNodeKind; name: string; parentId: string | null; children: string[]; /** Local affine transform, including translation relative to the parent. */ transform: DocumentMatrix; width: number; height: number; layout?: DocumentLayout | null; constraints?: DocumentNodeConstraints; properties: Record; /** Slots are explicit containers with optional child-kind restrictions. */ accepts?: DocumentNodeKind[]; /** An instance has no owned children; its render tree comes from its definition. */ definitionId?: string; /** Definition-relative overrides. Instance sizes inherit unless overridden here. */ overrides?: Record; } export interface DesignDocument { schemaVersion: 1; id: string; sourceRevision: string; rootId: string; nodes: Record; /** Domain adapters may store serializable sidecars, including bindings, here. */ metadata: Record; } export interface DocumentDiagnostic { code: string; message: string; nodeId?: string; } export interface DocumentPreflightInput { before: DesignDocument; after: DesignDocument; operations: readonly string[]; } export interface DocumentEngineOptions { /** Completes linked edits inside the same undo step, before geometry and preflight. Inputs are immutable. */ completeTransaction?: (input: DocumentPreflightInput, transaction: DocumentTransaction) => void; preflight?: (input: DocumentPreflightInput) => readonly DocumentDiagnostic[]; historyLimit?: number; } export interface DocumentInstanceSwapOptions { /** Explicit definition-relative addresses; names and child positions are never matched. */ overrideTargets?: Readonly>; /** Unmapped or incompatible overrides reject the swap unless discarding is explicit. */ incompatibleOverrides?: 'reject' | 'discard'; } export interface DocumentInstanceResetOptions { /** Adapter-owned identity/source properties that are not presentation overrides. */ preserveProperties?: readonly string[]; } export interface DocumentDuplicateMetadataInput { /** State at the start of the duplicate command, including earlier transaction commands. */ before: DesignDocument; /** Copied structure before sidecar remapping and final layout/preflight. */ after: DesignDocument; nodeIds: Readonly>; } export type DocumentDuplicateMetadataRemapper = (input: DocumentDuplicateMetadataInput) => Record; export interface DocumentDuplicateOptions { /** Defaults to the original parent, immediately after the selected subtree. */ parentId?: string; index?: number; preserveWorld?: boolean; /** Pure callback, required for nonempty metadata. The adapter owns sidecar IDs and scope references. */ remapMetadata?: DocumentDuplicateMetadataRemapper; } export interface DocumentTransaction { /** Restores a same-identity snapshot as one undoable transaction. Use edit intent when a command composes its delta through a temporary document. */ replaceDocument(document: DesignDocument, options?: { intent: 'edit' | 'restore'; }): void; add(node: DocumentNode, parentId: string, index?: number): void; patch(nodeId: string, patch: DocumentNodePatch): void; move(nodeId: string, x: number, y: number): void; resize(nodeId: string, width: number, height: number): void; reparent(nodeId: string, parentId: string, index?: number, options?: { preserveWorld?: boolean; }): void; moveOut(nodeId: string): void; group(nodeIds: readonly string[], groupId: string, name?: string): void; ungroup(groupId: string): void; remove(nodeId: string): void; defineComponent(nodeId: string): void; createInstance(definitionId: string, instanceId: string, parentId: string, index?: number): void; setOverride(instanceId: string, definitionNodeId: string, patch: DocumentNodePatch | null): void; /** Keeps placement and explicit compatible overrides; host semantics use preflight. */ swapInstance(instanceId: string, definitionId: string, options?: DocumentInstanceSwapOptions): void; /** Clears definition overrides and direct instance properties, retaining placement and name. */ resetInstanceOverrides(instanceId: string, options?: DocumentInstanceResetOptions): void; detachInstance(instanceId: string, allocateId: (definitionNodeId: string) => string): void; /** Copies owned children only; the ID allocator is pure and external definitions remain shared. */ duplicateSubtree(nodeId: string, allocateId: (sourceNodeId: string) => string, options?: DocumentDuplicateOptions): Readonly>; setMetadata(key: string, value: DocumentValue | undefined): void; } export interface DocumentEngine { readonly snapshot: DesignDocument; readonly revision: number; readonly canUndo: boolean; readonly canRedo: boolean; transact(label: string, apply: (transaction: DocumentTransaction) => void): boolean; undo(): boolean; redo(): boolean; serialize(): string; subscribe(listener: () => void): () => void; } export interface ResolvedDocumentNode { /** Definition identity is retained so the UI can address explicit overrides. */ sourceNodeId: string; /** Authored instance properties, excluding values inherited from its main component. */ instanceProperties?: Record; instanceId?: string; node: DocumentNode; children: ResolvedDocumentNode[]; } /** Optional presentation-time instance resolution; normal editing uses canonical definitions. */ export interface DocumentResolveOptions { /** * Expand a source occurrence before resolving its component subtree. The * continuation retains inherited overrides and cycle checks, skips this * occurrence's expansion hook, and accepts callback overrides for its subtree. * A definition root is resolved through its instance, not expanded separately. */ expandNode?: (input: { node: DocumentNode; instancePath: readonly string[]; }, resolve: (options?: DocumentResolveOptions) => ResolvedDocumentNode) => readonly ResolvedDocumentNode[]; resolveInstance?: (input: { node: DocumentNode; /** Outer-to-inner instance IDs. IDs remain opaque, including slashes. */ instancePath: readonly string[]; /** Effective overrides after outer and nested instance overrides have been merged. */ overrides: Readonly>; }) => { definitionId: string; overrides: Record; } | undefined; }