@dragcraft/core 0.0.1

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,840 @@
1
+ import { Ref, ShallowRef } from "vue";
2
+ //#region src/types.d.ts
3
+ type DeepReadonly<T> = T extends ((...args: infer Args) => infer Result) ? (...args: Args) => Result : T extends readonly unknown[] ? { readonly [Key in keyof T]: DeepReadonly<T[Key]>; } : T extends object ? { readonly [Key in keyof T]: DeepReadonly<T[Key]>; } : T;
4
+ interface SchemaNode {
5
+ id: string;
6
+ type: string;
7
+ props: Record<string, unknown>;
8
+ style?: NodeStyle;
9
+ layout?: NodeLayout;
10
+ container?: ContainerState;
11
+ /** Only used on the root node to hold the flat widget list */
12
+ children?: SchemaNode[];
13
+ }
14
+ type StyleValueMap = Record<string, unknown>;
15
+ interface NodeStyle {
16
+ /** Styles applied to the node's layout box in its assigned surface. */
17
+ container?: StyleValueMap;
18
+ /** Styles applied to the rendered widget component. */
19
+ content?: StyleValueMap;
20
+ /** Styles applied to a page or container surface owned by this node. */
21
+ surface?: StyleValueMap;
22
+ }
23
+ interface DesignerSchema {
24
+ version: string;
25
+ globalConfig: Record<string, unknown>;
26
+ root: SchemaNode;
27
+ }
28
+ type SchemaDiagnosticSeverity = 'warning' | 'error';
29
+ interface SchemaDiagnostic {
30
+ code: string;
31
+ severity: SchemaDiagnosticSeverity;
32
+ nodeId?: string;
33
+ ownerId?: string;
34
+ regionId?: string;
35
+ path?: string;
36
+ details?: Record<string, unknown>;
37
+ }
38
+ interface IndexedNodeLocation {
39
+ node: SchemaNode;
40
+ owner: 'root' | string;
41
+ regionId?: string;
42
+ index: number;
43
+ depth: 1 | 2;
44
+ }
45
+ interface SchemaIndexResult {
46
+ index: Map<string, IndexedNodeLocation>;
47
+ diagnostics: SchemaDiagnostic[];
48
+ }
49
+ type NodeOwner = {
50
+ kind: 'root';
51
+ sortScope?: string;
52
+ } | {
53
+ kind: 'container';
54
+ containerId: string;
55
+ regionId: string;
56
+ };
57
+ type NodeDestination = ({
58
+ kind: 'root';
59
+ sortScope?: string;
60
+ } & {
61
+ index?: number;
62
+ }) | ({
63
+ kind: 'container';
64
+ containerId: string;
65
+ regionId: string;
66
+ } & {
67
+ index?: number;
68
+ });
69
+ interface ResolvedNodeSource {
70
+ location: IndexedNodeLocation;
71
+ children: SchemaNode[];
72
+ index: number;
73
+ destination: NodeDestination;
74
+ }
75
+ interface ResolvedNodeDestination {
76
+ children: SchemaNode[];
77
+ destination: NodeDestination;
78
+ container?: SchemaNode;
79
+ definition?: ContainerDefinition;
80
+ variant?: ContainerVariantDefinition;
81
+ region?: ContainerRegionDefinition;
82
+ }
83
+ type OwnerResolutionResult<T> = {
84
+ ok: true;
85
+ value: T;
86
+ } | {
87
+ ok: false;
88
+ code: string;
89
+ message?: string;
90
+ };
91
+ interface SchemaValidationResult {
92
+ valid: boolean;
93
+ schema: DesignerSchema;
94
+ diagnostics: SchemaDiagnostic[];
95
+ }
96
+ interface NodeLayout {
97
+ placement?: NodePlacement;
98
+ order?: number;
99
+ visible?: boolean | ((ctx: {
100
+ node: SchemaNode;
101
+ schema: DesignerSchema;
102
+ }) => boolean);
103
+ }
104
+ interface ResolvedNodeLayout {
105
+ placement: ResolvedNodePlacement;
106
+ region?: string;
107
+ sortScope: string | false;
108
+ order?: number;
109
+ visible: boolean;
110
+ }
111
+ interface LayoutNodeEntry {
112
+ node: SchemaNode;
113
+ arrayIndex: number;
114
+ layout: ResolvedNodeLayout;
115
+ }
116
+ interface LayoutPlan {
117
+ entries: LayoutNodeEntry[];
118
+ regions: Map<string, LayoutNodeEntry[]>;
119
+ chrome: LayoutNodeEntry[];
120
+ layers: Map<string, LayoutNodeEntry[]>;
121
+ sortScopes: Map<string, LayoutNodeEntry[]>;
122
+ insets: LayoutInsetPlan;
123
+ }
124
+ type LayoutPlacementKind = 'flow' | 'chrome' | 'layer';
125
+ type LayoutEdge = 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
126
+ type LayoutAnchor = 'start' | 'center' | 'end';
127
+ type LayoutChromePosition = 'fixed' | 'sticky' | 'flow';
128
+ type LayoutLayerMode = 'framework' | 'self';
129
+ type LayoutReserveMode = 'measure' | 'size' | 'none';
130
+ type LayoutAvoidTarget = 'safe-area' | 'chrome' | 'viewport';
131
+ interface LayoutAnchorSpec {
132
+ anchor: {
133
+ block?: LayoutAnchor;
134
+ inline?: LayoutAnchor;
135
+ };
136
+ }
137
+ interface LayoutOffsets {
138
+ blockStart?: string | number;
139
+ blockEnd?: string | number;
140
+ inlineStart?: string | number;
141
+ inlineEnd?: string | number;
142
+ }
143
+ interface LayoutReserveSpec {
144
+ mode?: LayoutReserveMode;
145
+ size?: string | number;
146
+ }
147
+ interface ResolvedLayoutReserveSpec {
148
+ mode: LayoutReserveMode;
149
+ size?: string | number;
150
+ }
151
+ interface FlowPlacement {
152
+ kind: 'flow';
153
+ region?: string;
154
+ sortScope?: string | false;
155
+ }
156
+ interface ChromePlacement {
157
+ kind: 'chrome';
158
+ edge: LayoutEdge;
159
+ position?: LayoutChromePosition;
160
+ reserve?: LayoutReserveSpec;
161
+ avoidContent?: boolean;
162
+ }
163
+ interface LayerPlacement {
164
+ kind: 'layer';
165
+ layer?: string;
166
+ mode?: LayoutLayerMode;
167
+ anchor?: LayoutAnchorSpec['anchor'];
168
+ offset?: LayoutOffsets;
169
+ avoid?: LayoutAvoidTarget[];
170
+ }
171
+ type NodePlacement = FlowPlacement | ChromePlacement | LayerPlacement;
172
+ interface ResolvedFlowPlacement extends FlowPlacement {
173
+ kind: 'flow';
174
+ region: string;
175
+ sortScope: string | false;
176
+ }
177
+ interface ResolvedChromePlacement extends ChromePlacement {
178
+ kind: 'chrome';
179
+ position: LayoutChromePosition;
180
+ reserve: ResolvedLayoutReserveSpec;
181
+ avoidContent: boolean;
182
+ }
183
+ interface ResolvedLayerPlacement extends LayerPlacement {
184
+ kind: 'layer';
185
+ layer: string;
186
+ mode: LayoutLayerMode;
187
+ anchor: LayoutAnchorSpec['anchor'];
188
+ avoid: LayoutAvoidTarget[];
189
+ }
190
+ type ResolvedNodePlacement = ResolvedFlowPlacement | ResolvedChromePlacement | ResolvedLayerPlacement;
191
+ interface LayoutInsetContribution {
192
+ edge: LayoutEdge;
193
+ sourceNodeId: string;
194
+ reserve: ResolvedLayoutReserveSpec;
195
+ }
196
+ interface LayoutInsetPlan {
197
+ contributors: LayoutInsetContribution[];
198
+ }
199
+ interface DragTarget {
200
+ /** ID of the node being dragged (null if dragging from material panel) */
201
+ sourceNodeId: string | null;
202
+ /** Widget type being dragged from material panel (null if moving existing) */
203
+ widgetType: string | null;
204
+ }
205
+ /**
206
+ * Context provided when evaluating per-instance behavior fields
207
+ * (mask, selectable, draggable, sortable, deletable).
208
+ * Available inside a computed — schema changes trigger re-evaluation.
209
+ */
210
+ interface InstanceBehaviorContext {
211
+ /** The specific node being evaluated */
212
+ node: DeepReadonly<SchemaNode>;
213
+ /** A deeply readonly snapshot of the current designer schema. */
214
+ schema: DeepReadonly<DesignerSchema>;
215
+ }
216
+ /**
217
+ * Context provided when evaluating per-type behavior fields (creatable).
218
+ * No specific node exists yet — used in the material panel.
219
+ */
220
+ interface TypeBehaviorContext {
221
+ /** The widget type identifier */
222
+ widgetType: string;
223
+ /** A deeply readonly snapshot of the current designer schema. */
224
+ schema: DeepReadonly<DesignerSchema>;
225
+ }
226
+ /**
227
+ * A behavior field that accepts either a static boolean or
228
+ * a predicate function evaluated at runtime.
229
+ *
230
+ * @example
231
+ * // Static
232
+ * draggable: false
233
+ *
234
+ * // Dynamic
235
+ * draggable: ({ node }) => node.type !== 'fixed'
236
+ */
237
+ type BehaviorPredicate<Ctx> = boolean | ((ctx: Ctx) => boolean);
238
+ /**
239
+ * User-facing reason for a blocked creation attempt.
240
+ *
241
+ * `code` is stable for analytics and custom UI mapping.
242
+ * `messageKey` lets applications provide localized copy.
243
+ * `message` is the fallback text when no localized message exists.
244
+ */
245
+ interface CreationBlockReason {
246
+ code?: string;
247
+ messageKey?: string;
248
+ message?: string;
249
+ }
250
+ interface CreatableDecision extends CreationBlockReason {
251
+ allowed: boolean;
252
+ }
253
+ /**
254
+ * Type-level creation rule. Return `false` for a generic blocked state, or
255
+ * return `{ allowed: false, messageKey, message }` to explain the reason.
256
+ */
257
+ type CreatableBehaviorResult = boolean | CreatableDecision;
258
+ type CreatableBehaviorPredicate = CreatableBehaviorResult | ((ctx: TypeBehaviorContext) => CreatableBehaviorResult);
259
+ type ContainerVariantId = string;
260
+ type ContainerRegionId = string;
261
+ interface ContainerState {
262
+ variant: ContainerVariantId;
263
+ regions: Record<ContainerRegionId, SchemaNode[]>;
264
+ }
265
+ interface ContainerRegionConstraints {
266
+ includeTypes?: string[];
267
+ excludeTypes?: string[];
268
+ minItems?: number;
269
+ maxItems?: number;
270
+ }
271
+ interface ContainerRegionDefinition {
272
+ id: ContainerRegionId;
273
+ title: string;
274
+ titleKey?: string;
275
+ constraints?: ContainerRegionConstraints;
276
+ }
277
+ interface ContainerVariantDefinition {
278
+ title: string;
279
+ titleKey?: string;
280
+ regions: ContainerRegionDefinition[];
281
+ }
282
+ interface PlacementDecision extends CreationBlockReason {
283
+ allowed: boolean;
284
+ details?: Record<string, unknown>;
285
+ }
286
+ interface ContainerDefinition {
287
+ defaultVariant: ContainerVariantId;
288
+ variants: Record<ContainerVariantId, ContainerVariantDefinition>;
289
+ createInitialState?: (ctx: ContainerInitContext) => ContainerState;
290
+ canPlace?: (ctx: ContainerPlacementContext) => PlacementDecision;
291
+ migrateVariant?: (ctx: ContainerVariantMigrationContext) => ContainerVariantMigrationResult;
292
+ }
293
+ interface ContainerInitContext {
294
+ containerNode: Readonly<SchemaNode>;
295
+ schema: Readonly<DesignerSchema>;
296
+ createNode: (type: string, overrides?: Partial<Pick<SchemaNode, 'props' | 'style' | 'layout'>>) => SchemaNode;
297
+ }
298
+ interface ContainerPlacementContext {
299
+ operation: 'add' | 'move';
300
+ schema: Readonly<DesignerSchema>;
301
+ container: Readonly<SchemaNode>;
302
+ variant: Readonly<ContainerVariantDefinition>;
303
+ region: Readonly<ContainerRegionDefinition>;
304
+ child: Readonly<SchemaNode>;
305
+ targetIndex: number;
306
+ }
307
+ interface ContainerVariantMigrationContext {
308
+ schema: Readonly<DesignerSchema>;
309
+ container: Readonly<SchemaNode>;
310
+ fromVariantId: ContainerVariantId;
311
+ toVariantId: ContainerVariantId;
312
+ fromVariant: Readonly<ContainerVariantDefinition>;
313
+ toVariant: Readonly<ContainerVariantDefinition>;
314
+ state: Readonly<ContainerState>;
315
+ }
316
+ type ContainerVariantMigrationResult = {
317
+ allowed: true;
318
+ state: ContainerState;
319
+ } | ({
320
+ allowed: false;
321
+ } & Omit<PlacementDecision, 'allowed'>);
322
+ type ContainerDefinitionValidationCode = 'CONTAINER_DEFINITION_INVALID' | 'CONTAINER_VARIANTS_INVALID' | 'CONTAINER_VARIANT_INVALID' | 'CONTAINER_REGIONS_INVALID' | 'CONTAINER_REGION_INVALID' | 'CONTAINER_CONSTRAINTS_INVALID' | 'CONTAINER_DEFAULT_VARIANT_MISSING' | 'CONTAINER_VARIANT_ID_RESERVED' | 'CONTAINER_REGION_ID_RESERVED' | 'CONTAINER_REGION_ID_DUPLICATE' | 'CONTAINER_CARDINALITY_INVALID' | 'CONTAINER_TYPE_ID_INVALID';
323
+ interface ContainerDefinitionValidationError {
324
+ code: ContainerDefinitionValidationCode;
325
+ path: string;
326
+ }
327
+ interface ContainerDefinitionValidationResult {
328
+ valid: boolean;
329
+ errors: ContainerDefinitionValidationError[];
330
+ }
331
+ interface ContainerPlanRegion {
332
+ definition: ContainerRegionDefinition;
333
+ nodes: SchemaNode[];
334
+ isEmpty: boolean;
335
+ }
336
+ interface ContainerPlan {
337
+ containerId: string;
338
+ variant: ContainerVariantDefinition;
339
+ regions: ContainerPlanRegion[];
340
+ }
341
+ type ContainerPlanResult = {
342
+ ok: true;
343
+ plan: ContainerPlan;
344
+ } | {
345
+ ok: false;
346
+ code: 'CONTAINER_UNRESOLVED' | 'CONTAINER_VARIANT_UNKNOWN';
347
+ containerId: string;
348
+ };
349
+ type CreateRegisteredNode = ContainerInitContext['createNode'];
350
+ type ContainerStateCreationResult = {
351
+ ok: true;
352
+ state: ContainerState;
353
+ } | {
354
+ ok: false;
355
+ code: string;
356
+ message?: string;
357
+ details?: Record<string, unknown>;
358
+ };
359
+ interface ResolvePlacementContext {
360
+ definition: ContainerDefinition;
361
+ region: ContainerRegionDefinition;
362
+ child: SchemaNode;
363
+ childHasContainerCapability: boolean;
364
+ targetCount: number;
365
+ callbackContext: ContainerPlacementContext;
366
+ }
367
+ /**
368
+ * Minimal form field schema shape.
369
+ * Matches the structural contract of `@dragcraft/form-generator` FieldSchema
370
+ * without creating a package dependency.
371
+ */
372
+ interface FieldSchemaShape {
373
+ key: string;
374
+ label: string;
375
+ component: string;
376
+ [key: string]: unknown;
377
+ }
378
+ /**
379
+ * Minimal form schema shape for widget property panels.
380
+ * Matches the structural contract of `@dragcraft/form-generator` FormSchema
381
+ * without creating a package dependency.
382
+ */
383
+ interface FormSchemaShape {
384
+ sections: Array<{
385
+ title: string;
386
+ fields: FieldSchemaShape[];
387
+ [key: string]: unknown;
388
+ }>;
389
+ }
390
+ /**
391
+ * Per-widget action configuration.
392
+ * Controls which actions appear in the node toolbar for this widget type.
393
+ */
394
+ interface CoreWidgetActionConfig {
395
+ /** If provided, only show actions with these keys */
396
+ only?: string[];
397
+ /** Exclude actions with these keys */
398
+ exclude?: string[];
399
+ }
400
+ /**
401
+ * Widget meta protocol — the contract every widget registers with the engine.
402
+ */
403
+ interface CoreWidgetMeta {
404
+ /** Unique widget type identifier */
405
+ type: string;
406
+ /** Human-readable title for material panel */
407
+ title: string;
408
+ /** i18n message key for title; overrides `title` when i18n is active */
409
+ titleKey?: string;
410
+ /** Group for categorization in material panel */
411
+ group: string;
412
+ /** Icon identifier */
413
+ icon?: string;
414
+ /** Default prop values when creating a new instance */
415
+ defaultProps: Record<string, unknown>;
416
+ /** Default scoped styles when creating a new instance */
417
+ defaultStyle?: NodeStyle;
418
+ /** Form schema for the property panel */
419
+ formSchema: FormSchemaShape;
420
+ /** Structural container capabilities for this widget type. */
421
+ container?: ContainerDefinition;
422
+ /** Whether to render a mask overlay on the widget in the canvas (default: true). Accepts boolean or predicate. */
423
+ mask?: BehaviorPredicate<InstanceBehaviorContext>;
424
+ /** Whether this widget can be selected in the canvas (default: true). Accepts boolean or predicate. */
425
+ selectable?: BehaviorPredicate<InstanceBehaviorContext>;
426
+ /** Whether this widget can be dragged to reorder (default: true). Accepts boolean or predicate. */
427
+ draggable?: BehaviorPredicate<InstanceBehaviorContext>;
428
+ /**
429
+ * Whether this widget's position can be changed by reordering (default: true).
430
+ * When false, the widget is locked at its current array index — it cannot
431
+ * be dragged, and operations on other widgets that would shift this widget's
432
+ * index are forbidden. Implies draggable=false.
433
+ */
434
+ sortable?: BehaviorPredicate<InstanceBehaviorContext>;
435
+ /** Whether this widget can be deleted via toolbar action (default: true). Accepts boolean or predicate. */
436
+ deletable?: BehaviorPredicate<InstanceBehaviorContext>;
437
+ /** Default open layout metadata for new and existing instances of this widget type. */
438
+ defaultLayout?: NodeLayout;
439
+ /**
440
+ * Whether new instances of this widget type can be created (default: true).
441
+ * Applies to every ADD_NODE entry, including material drops and duplicate
442
+ * actions. Evaluated per-type, not per-instance.
443
+ */
444
+ creatable?: CreatableBehaviorPredicate;
445
+ /** Per-widget toolbar action configuration */
446
+ actions?: CoreWidgetActionConfig;
447
+ }
448
+ type WidgetMeta = CoreWidgetMeta;
449
+ type WidgetActionConfig = CoreWidgetActionConfig;
450
+ interface Command<T = unknown> {
451
+ type: string;
452
+ payload: T;
453
+ }
454
+ interface CommandInteractionStore {
455
+ readonly selectedNodeId: Readonly<Ref<string | null>>;
456
+ readonly hoveredNodeId: Readonly<Ref<string | null>>;
457
+ selectNode: (id: string | null) => void;
458
+ hoverNode: (id: string | null) => void;
459
+ }
460
+ interface CommandContext {
461
+ /** Frozen schema at the start of this command. Safe for user callbacks. */
462
+ schema: DeepReadonly<DesignerSchema>;
463
+ /** Command-owned mutable draft. Committed only when the command changes state. */
464
+ draft: DesignerSchema;
465
+ store: CommandInteractionStore;
466
+ registry: RegistryInstance;
467
+ }
468
+ type CommandExecutionResult = {
469
+ ok: true;
470
+ changed: boolean;
471
+ eventPayload?: unknown;
472
+ } | ({
473
+ ok: false;
474
+ code: string;
475
+ } & CreationBlockReason & {
476
+ details?: Record<string, unknown>;
477
+ });
478
+ type CommandResult = false | void | {
479
+ ok: true;
480
+ changed?: boolean;
481
+ eventPayload?: unknown;
482
+ } | Extract<CommandExecutionResult, {
483
+ ok: false;
484
+ }>;
485
+ declare function commandFailure(code: string, reason?: Omit<Extract<CommandExecutionResult, {
486
+ ok: false;
487
+ }>, 'ok' | 'code'>): Extract<CommandExecutionResult, {
488
+ ok: false;
489
+ }>;
490
+ type CommandHandler<T = unknown> = (ctx: CommandContext, payload: T) => CommandResult;
491
+ interface AddNodePayload {
492
+ node: SchemaNode;
493
+ destination?: NodeDestination;
494
+ }
495
+ interface MoveNodePayload {
496
+ nodeId: string;
497
+ destination: NodeDestination;
498
+ }
499
+ interface RemoveNodePayload {
500
+ nodeId: string;
501
+ }
502
+ interface DuplicateNodePayload {
503
+ nodeId: string;
504
+ }
505
+ interface ChangeContainerVariantPayload {
506
+ containerId: string;
507
+ variant: ContainerVariantId;
508
+ }
509
+ interface UpdatePropsPayload {
510
+ nodeId: string;
511
+ props: Record<string, unknown>;
512
+ style?: NodeStyle;
513
+ }
514
+ interface SetGlobalConfigPayload {
515
+ config: Record<string, unknown>;
516
+ }
517
+ interface HistoryEntry {
518
+ label: string;
519
+ snapshot: DeepReadonly<DesignerSchema>;
520
+ }
521
+ interface EngineOptions {
522
+ maxHistorySize?: number;
523
+ }
524
+ interface EngineState {
525
+ getSchema: () => DeepReadonly<DesignerSchema>;
526
+ getNodeById: (id: string) => DeepReadonly<SchemaNode> | null;
527
+ getSelectedNodeId: () => string | null;
528
+ getHoveredNodeId: () => string | null;
529
+ getDragTarget: () => DragTarget | null;
530
+ }
531
+ interface EngineStore {
532
+ readonly schema: Readonly<ShallowRef<DeepReadonly<DesignerSchema>>>;
533
+ readonly selectedNodeId: Readonly<Ref<string | null>>;
534
+ readonly hoveredNodeId: Readonly<Ref<string | null>>;
535
+ readonly dragTarget: Readonly<Ref<DeepReadonly<DragTarget> | null>>;
536
+ selectNode: (id: string | null) => void;
537
+ hoverNode: (id: string | null) => void;
538
+ setDragTarget: (target: DragTarget | null) => void;
539
+ }
540
+ /**
541
+ * A single migration step that transforms a schema from one version to another.
542
+ */
543
+ interface SchemaMigration {
544
+ /** The version this migration upgrades from */
545
+ fromVersion: string;
546
+ /** The version this migration upgrades to */
547
+ toVersion: string;
548
+ /** Transform function — receives the old schema, returns the migrated schema */
549
+ migrate: (schema: DesignerSchema) => DesignerSchema;
550
+ }
551
+ interface SchemaStoreInstance {
552
+ readonly schema: ShallowRef<DeepReadonly<DesignerSchema>>;
553
+ readonly selectedNodeId: Ref<string | null>;
554
+ readonly hoveredNodeId: Ref<string | null>;
555
+ readonly dragTarget: Ref<DragTarget | null>;
556
+ getSchema: () => DesignerSchema;
557
+ getSnapshot: () => DeepReadonly<DesignerSchema>;
558
+ setSchema: (schema: DesignerSchema) => void;
559
+ commitSchema: (schema: DesignerSchema) => DeepReadonly<DesignerSchema>;
560
+ restoreSnapshot: (schema: DeepReadonly<DesignerSchema>) => void;
561
+ selectNode: (id: string | null) => void;
562
+ hoverNode: (id: string | null) => void;
563
+ setDragTarget: (target: DragTarget | null) => void;
564
+ getNodeById: (id: string) => DeepReadonly<SchemaNode> | null;
565
+ }
566
+ interface RegistryInstance {
567
+ registerWidget: (meta: WidgetMeta) => void;
568
+ registerGlobalConfigSchema: (schema: Record<string, unknown>) => void;
569
+ registerGlobalConfigFormSchema: (schema: FormSchemaShape) => void;
570
+ getWidget: (type: string) => WidgetMeta | undefined;
571
+ getGlobalConfigSchema: () => Record<string, unknown> | undefined;
572
+ getAllWidgets: () => WidgetMeta[];
573
+ }
574
+ //#endregion
575
+ //#region src/behavior.d.ts
576
+ /**
577
+ * Resolves a behavior field that may be a static boolean or a predicate function.
578
+ *
579
+ * - `undefined` → returns `defaultValue` (preserves opt-in defaults)
580
+ * - `boolean` → returns the boolean directly
581
+ * - `function` → invokes the function with the provided context
582
+ *
583
+ * @param field - The behavior field from WidgetMeta (may be undefined)
584
+ * @param ctx - The context to pass if field is a function
585
+ * @param defaultValue - Value when field is undefined (default: true)
586
+ */
587
+ declare function resolveBehavior<Ctx>(field: BehaviorPredicate<Ctx> | undefined, ctx: Ctx, defaultValue?: boolean): boolean;
588
+ declare function resolveCreatable(field: CreatableBehaviorPredicate | undefined, ctx: TypeBehaviorContext, defaultValue?: boolean): CreatableDecision;
589
+ //#endregion
590
+ //#region src/constants.d.ts
591
+ declare const CommandType: {
592
+ readonly ADD_NODE: "ADD_NODE";
593
+ readonly MOVE_NODE: "MOVE_NODE";
594
+ readonly REMOVE_NODE: "REMOVE_NODE";
595
+ readonly DUPLICATE_NODE: "DUPLICATE_NODE";
596
+ readonly CHANGE_CONTAINER_VARIANT: "CHANGE_CONTAINER_VARIANT";
597
+ readonly UPDATE_PROPS: "UPDATE_PROPS";
598
+ readonly SET_GLOBAL_CONFIG: "SET_GLOBAL_CONFIG";
599
+ };
600
+ type CommandTypeValue = typeof CommandType[keyof typeof CommandType];
601
+ declare const EventName: {
602
+ readonly SCHEMA_CHANGED: "schema:changed";
603
+ readonly SELECTION_CHANGED: "selection:changed";
604
+ readonly DRAG_ENTER: "drag:enter";
605
+ readonly DRAG_OVER: "drag:over";
606
+ readonly DRAG_LEAVE: "drag:leave";
607
+ readonly DRAG_DROP: "drag:drop";
608
+ readonly HISTORY_CHANGED: "history:changed";
609
+ readonly NODE_ADDED: "node:added";
610
+ readonly NODE_REMOVED: "node:removed";
611
+ readonly NODE_DUPLICATED: "node:duplicated";
612
+ readonly NODE_MOVED: "node:moved";
613
+ readonly CONTAINER_VARIANT_CHANGED: "container:variant-changed";
614
+ readonly NODE_UPDATED: "node:updated";
615
+ readonly GLOBAL_CONFIG_CHANGED: "global-config:changed";
616
+ };
617
+ type EventNameValue = typeof EventName[keyof typeof EventName];
618
+ declare const DEFAULT_SCHEMA_VERSION = "1.0.0";
619
+ declare const DEFAULT_MAX_HISTORY_SIZE = 50;
620
+ //#endregion
621
+ //#region src/event-hub.d.ts
622
+ type EventListener = (...args: unknown[]) => void;
623
+ declare class EventHub {
624
+ private emitter;
625
+ constructor();
626
+ on(event: EventNameValue | (string & {}), listener: EventListener): void;
627
+ off(event: EventNameValue | (string & {}), listener: EventListener): void;
628
+ emit(event: EventNameValue | (string & {}), ...args: unknown[]): void;
629
+ once(event: EventNameValue | (string & {}), listener: EventListener): void;
630
+ clear(): void;
631
+ }
632
+ declare function createEventHub(): EventHub;
633
+ //#endregion
634
+ //#region src/history-manager.d.ts
635
+ interface HistoryState {
636
+ canUndo: boolean;
637
+ canRedo: boolean;
638
+ undoCount: number;
639
+ redoCount: number;
640
+ }
641
+ interface HistoryManagerInstance {
642
+ readonly state: Readonly<Ref<HistoryState>>;
643
+ pushSnapshot: (label: string, before: DeepReadonly<DesignerSchema>) => void;
644
+ undo: () => void;
645
+ redo: () => void;
646
+ canUndo: () => boolean;
647
+ canRedo: () => boolean;
648
+ beginTransaction: (label?: string) => void;
649
+ commitTransaction: () => void;
650
+ discardTransaction: () => void;
651
+ isInTransaction: () => boolean;
652
+ clear: () => void;
653
+ }
654
+ declare function createHistoryManager(store: SchemaStoreInstance, eventHub: EventHub, maxSize?: number): HistoryManagerInstance;
655
+ //#endregion
656
+ //#region src/command-bus.d.ts
657
+ interface CommandBusInstance {
658
+ execute: <T = unknown>(command: Command<T>) => CommandExecutionResult;
659
+ registerHandler: <T = unknown>(type: string, handler: CommandHandler<T>) => void;
660
+ }
661
+ declare function createCommandBus(store: SchemaStoreInstance, registry: RegistryInstance, eventHub: EventHub, history: HistoryManagerInstance): CommandBusInstance;
662
+ //#endregion
663
+ //#region src/commands/add-node.d.ts
664
+ declare function addNodeHandler(ctx: CommandContext, payload: AddNodePayload): CommandResult;
665
+ //#endregion
666
+ //#region src/commands/change-container-variant.d.ts
667
+ declare function changeContainerVariantHandler(ctx: CommandContext, payload: ChangeContainerVariantPayload): CommandResult;
668
+ //#endregion
669
+ //#region src/commands/duplicate-node.d.ts
670
+ declare function duplicateNodeHandler(ctx: CommandContext, payload: DuplicateNodePayload): CommandResult;
671
+ //#endregion
672
+ //#region src/commands/move-node.d.ts
673
+ declare function moveNodeHandler(ctx: CommandContext, payload: MoveNodePayload): CommandResult;
674
+ //#endregion
675
+ //#region src/commands/remove-node.d.ts
676
+ declare function removeNodeHandler(ctx: CommandContext, payload: RemoveNodePayload): CommandResult;
677
+ //#endregion
678
+ //#region src/commands/set-global-config.d.ts
679
+ declare function setGlobalConfigHandler(ctx: CommandContext, payload: SetGlobalConfigPayload): CommandResult;
680
+ //#endregion
681
+ //#region src/commands/update-props.d.ts
682
+ declare function updatePropsHandler(ctx: CommandContext, payload: UpdatePropsPayload): CommandResult;
683
+ //#endregion
684
+ //#region src/container-definition.d.ts
685
+ declare function validateContainerDefinition(definition: ContainerDefinition): ContainerDefinitionValidationResult;
686
+ //#endregion
687
+ //#region src/container-placement.d.ts
688
+ declare function resolvePlacementDecision(ctx: ResolvePlacementContext): PlacementDecision;
689
+ declare function createContainerState(node: SchemaNode, schema: DesignerSchema, registry: RegistryInstance, createNode: CreateRegisteredNode): ContainerStateCreationResult;
690
+ declare function createRegisteredNode(registry: RegistryInstance, createId?: () => string): CreateRegisteredNode;
691
+ //#endregion
692
+ //#region src/container-plan.d.ts
693
+ declare function createContainerPlan(node: SchemaNode, registry: RegistryInstance): ContainerPlanResult;
694
+ //#endregion
695
+ //#region src/engine.d.ts
696
+ type SchemaImportResult = {
697
+ ok: true;
698
+ diagnostics: SchemaDiagnostic[];
699
+ } | {
700
+ ok: false;
701
+ diagnostics: SchemaDiagnostic[];
702
+ };
703
+ interface DesignerEngine {
704
+ store: EngineStore;
705
+ state: EngineState;
706
+ commandBus: CommandBusInstance;
707
+ history: HistoryManagerInstance;
708
+ registry: RegistryInstance;
709
+ eventHub: EventHub;
710
+ execute: <T = unknown>(command: Command<T>) => CommandExecutionResult;
711
+ registerHandler: <T = unknown>(type: string, handler: CommandHandler<T>) => void;
712
+ registerWidget: (meta: WidgetMeta) => void;
713
+ registerMigration: (migration: SchemaMigration) => void;
714
+ migrateSchema: (schema: DesignerSchema) => DesignerSchema;
715
+ exportSchema: () => DesignerSchema;
716
+ importSchema: (schema: DesignerSchema) => SchemaImportResult;
717
+ dispose: () => void;
718
+ }
719
+ declare function createEngine(options?: EngineOptions): DesignerEngine;
720
+ //#endregion
721
+ //#region src/helpers.d.ts
722
+ declare function cloneNodeSubtree(node: SchemaNode, createId: () => string): SchemaNode;
723
+ declare function collectSubtreeIds(node: SchemaNode): Set<string>;
724
+ /**
725
+ * Find a node by ID in the shallow ownership structure.
726
+ */
727
+ declare function findNodeById(root: SchemaNode, id: string): SchemaNode | null;
728
+ /**
729
+ * Find the parent node of a given node ID.
730
+ * Returns the owning root or container location, or null if not found.
731
+ */
732
+ declare function findParentNode(root: SchemaNode, targetId: string): {
733
+ parent: SchemaNode;
734
+ regionId?: string;
735
+ index: number;
736
+ } | null;
737
+ /**
738
+ * Remove a node from the tree by ID.
739
+ * Returns the removed node or null if not found.
740
+ */
741
+ declare function removeNodeFromTree(root: SchemaNode, nodeId: string): SchemaNode | null;
742
+ /**
743
+ * Insert a node into a parent's children array at a given index.
744
+ * If index is undefined, appends to the end.
745
+ */
746
+ declare function insertNodeIntoTree(parent: SchemaNode, node: SchemaNode, index?: number): void;
747
+ /**
748
+ * Walk root and its direct children (one level), calling visitor for each node.
749
+ * If visitor returns false, stop traversal.
750
+ *
751
+ * Note: This does NOT recurse into grandchildren — the schema is a flat
752
+ * two-level structure (root → widgets).
753
+ */
754
+ declare function walkFlatChildren(root: SchemaNode, visitor: (node: SchemaNode) => boolean | void): void;
755
+ //#endregion
756
+ //#region src/layout.d.ts
757
+ declare const DEFAULT_LAYOUT_REGION = "content";
758
+ declare const DEFAULT_SORT_SCOPE = "content";
759
+ declare const DEFAULT_LAYER = "float";
760
+ declare function resolveNodeLayout(node: SchemaNode, registry: RegistryInstance, schema?: DesignerSchema): ResolvedNodeLayout;
761
+ declare function createLayoutPlan(schema: DesignerSchema, registry: RegistryInstance): LayoutPlan;
762
+ declare function getLayoutRegionEntries(plan: LayoutPlan, region?: string): LayoutNodeEntry[];
763
+ declare function getSortScopeEntries(plan: LayoutPlan, sortScope?: string): LayoutNodeEntry[];
764
+ declare function getSortScopeNodes(plan: LayoutPlan, sortScope?: string): SchemaNode[];
765
+ declare function getSortableArrayIndexForInsert(scopeEntries: LayoutNodeEntry[], allChildren: SchemaNode[], visualIndex: number): number;
766
+ declare function resolveNodeSource(schema: DesignerSchema, indexed: SchemaIndexResult, nodeId: string): OwnerResolutionResult<ResolvedNodeSource>;
767
+ declare function resolveDestination(schema: DesignerSchema, registry: RegistryInstance, destination: NodeDestination): OwnerResolutionResult<ResolvedNodeDestination>;
768
+ declare function clampInsertIndex(index: number | undefined, length: number): number;
769
+ declare function stripPageLayout(node: SchemaNode): SchemaNode;
770
+ //#endregion
771
+ //#region src/registry.d.ts
772
+ declare function createRegistry(): RegistryInstance;
773
+ //#endregion
774
+ //#region src/schema-index.d.ts
775
+ declare function buildSchemaIndex(schema: DesignerSchema): SchemaIndexResult;
776
+ declare function findIndexedNode(result: SchemaIndexResult, nodeId: string): IndexedNodeLocation | undefined;
777
+ //#endregion
778
+ //#region src/schema-store.d.ts
779
+ declare function createDefaultSchema(): DesignerSchema;
780
+ //#endregion
781
+ //#region src/schema-validation.d.ts
782
+ declare function validateSchema(input: DesignerSchema, registry: RegistryInstance): SchemaValidationResult;
783
+ //#endregion
784
+ //#region src/sortable.d.ts
785
+ /**
786
+ * Collect the indices of all widgets whose `sortable` behavior resolves to false.
787
+ * These widgets must stay at their current array index (absolute index locking).
788
+ *
789
+ * @param _children - The flat widget list (`root.children`)
790
+ * @param registry - Widget meta registry for resolving behavior predicates
791
+ * @param schema - Current designer schema (for predicate evaluation context)
792
+ * @param sortScope - Sort scope to evaluate
793
+ */
794
+ declare function getLockedIndices(_children: SchemaNode[], registry: RegistryInstance, schema: DesignerSchema, sortScope?: string): Set<number>;
795
+ declare function getLockedIndicesFromNodes(nodes: readonly SchemaNode[], registry: RegistryInstance, schema: DesignerSchema): Set<number>;
796
+ /**
797
+ * Can a new node be inserted at `insertIndex` without shifting any locked widget?
798
+ *
799
+ * Inserting at index `i` shifts all widgets at index >= `i` rightward by 1.
800
+ * If any locked widget has index >= `insertIndex`, it would be displaced.
801
+ */
802
+ declare function isInsertAllowed(insertIndex: number, lockedIndices: Set<number>): boolean;
803
+ /**
804
+ * Can a node be moved from `srcIdx` to `targetIdx` (post-removal insertion index)
805
+ * without altering any locked widget's absolute position?
806
+ *
807
+ * Mathematical analysis for each locked widget at index L (L !== srcIdx):
808
+ * - srcIdx < L: Removal shifts L to L-1. Insertion must be at targetIdx <= L-1 to restore it.
809
+ * - srcIdx > L: L is unaffected by removal. Insertion must satisfy targetIdx > L to keep it at L.
810
+ */
811
+ declare function isMoveAllowed(srcIdx: number, targetIdx: number, lockedIndices: Set<number>): boolean;
812
+ /**
813
+ * Can a node at `removeIndex` be removed without shifting any locked widget?
814
+ *
815
+ * Removing at index `i` shifts all widgets at index > `i` leftward by 1.
816
+ * If any locked widget has index > `removeIndex`, it would be displaced.
817
+ */
818
+ declare function isRemoveAllowed(removeIndex: number, lockedIndices: Set<number>): boolean;
819
+ /**
820
+ * Compute the set of valid visual drop indices (0..children.length) for a drag operation.
821
+ *
822
+ * For new-widget drops (sourceNodeId === null): checks isInsertAllowed for each position.
823
+ * For existing-widget moves: converts visual gap index to post-removal target index
824
+ * and checks isMoveAllowed.
825
+ *
826
+ * @param children - The flat widget list
827
+ * @param lockedIndices - Pre-computed locked indices from getLockedIndices()
828
+ * @param sourceNodeId - ID of the dragged widget (null if dragging from material panel)
829
+ */
830
+ declare function getValidDropIndices(children: SchemaNode[] | LayoutNodeEntry[], lockedIndices: Set<number>, sourceNodeId: string | null): Set<number>;
831
+ /**
832
+ * Given a raw visual index and a set of valid indices, find the nearest valid one.
833
+ * Returns null if no valid index exists.
834
+ */
835
+ declare function findNearestValidIndex(rawIndex: number, validIndices: Set<number>): number | null;
836
+ //#endregion
837
+ //#region src/style.d.ts
838
+ declare function normalizeStyleValueMap(style: DeepReadonly<StyleValueMap> | undefined): StyleValueMap | undefined;
839
+ //#endregion
840
+ export { type AddNodePayload, type BehaviorPredicate, type ChangeContainerVariantPayload, type Command, type CommandBusInstance, type CommandContext, type CommandExecutionResult, type CommandHandler, type CommandResult, CommandType, type CommandTypeValue, type ContainerDefinition, type ContainerDefinitionValidationCode, type ContainerDefinitionValidationError, type ContainerDefinitionValidationResult, type ContainerInitContext, type ContainerPlacementContext, type ContainerPlan, type ContainerPlanRegion, type ContainerPlanResult, type ContainerRegionConstraints, type ContainerRegionDefinition, type ContainerRegionId, type ContainerState, type ContainerStateCreationResult, type ContainerVariantDefinition, type ContainerVariantId, type ContainerVariantMigrationContext, type ContainerVariantMigrationResult, type CoreWidgetActionConfig, type CoreWidgetMeta, type CreatableBehaviorPredicate, type CreatableBehaviorResult, type CreatableDecision, type CreateRegisteredNode, type CreationBlockReason, DEFAULT_LAYER, DEFAULT_LAYOUT_REGION, DEFAULT_MAX_HISTORY_SIZE, DEFAULT_SCHEMA_VERSION, DEFAULT_SORT_SCOPE, type DeepReadonly, type DesignerEngine, type DesignerSchema, type DragTarget, type DuplicateNodePayload, type EngineOptions, type EngineState, type EngineStore, EventHub, type EventListener, EventName, type EventNameValue, type FieldSchemaShape, type FormSchemaShape, type HistoryEntry, type HistoryManagerInstance, type HistoryState, type IndexedNodeLocation, type InstanceBehaviorContext, type LayoutAnchor, type LayoutAnchorSpec, type LayoutAvoidTarget, type LayoutChromePosition, type LayoutEdge, type LayoutInsetContribution, type LayoutInsetPlan, type LayoutLayerMode, type LayoutNodeEntry, type LayoutOffsets, type LayoutPlacementKind, type LayoutPlan, type LayoutReserveMode, type LayoutReserveSpec, type MoveNodePayload, type NodeDestination, type NodeLayout, type NodeOwner, type NodePlacement, type NodeStyle, type OwnerResolutionResult, type PlacementDecision, type RegistryInstance, type RemoveNodePayload, type ResolvePlacementContext, type ResolvedChromePlacement, type ResolvedFlowPlacement, type ResolvedLayerPlacement, type ResolvedLayoutReserveSpec, type ResolvedNodeDestination, type ResolvedNodeLayout, type ResolvedNodePlacement, type ResolvedNodeSource, type SchemaDiagnostic, type SchemaDiagnosticSeverity, type SchemaImportResult, type SchemaIndexResult, type SchemaMigration, type SchemaNode, type SchemaValidationResult, type SetGlobalConfigPayload, type StyleValueMap, type TypeBehaviorContext, type UpdatePropsPayload, type WidgetActionConfig, type WidgetMeta, addNodeHandler, buildSchemaIndex, changeContainerVariantHandler, clampInsertIndex, cloneNodeSubtree, collectSubtreeIds, commandFailure, createCommandBus, createContainerPlan, createContainerState, createDefaultSchema, createEngine, createEventHub, createHistoryManager, createLayoutPlan, createRegisteredNode, createRegistry, duplicateNodeHandler, findIndexedNode, findNearestValidIndex, findNodeById, findParentNode, getLayoutRegionEntries, getLockedIndices, getLockedIndicesFromNodes, getSortScopeEntries, getSortScopeNodes, getSortableArrayIndexForInsert, getValidDropIndices, insertNodeIntoTree, isInsertAllowed, isMoveAllowed, isRemoveAllowed, moveNodeHandler, normalizeStyleValueMap, removeNodeFromTree, removeNodeHandler, resolveBehavior, resolveCreatable, resolveDestination, resolveNodeLayout, resolveNodeSource, resolvePlacementDecision, setGlobalConfigHandler, stripPageLayout, updatePropsHandler, validateContainerDefinition, validateSchema, walkFlatChildren };