@echothink-ui/layout 0.2.0

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.
Files changed (50) hide show
  1. package/README.md +92 -0
  2. package/dist/index.cjs +1620 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.css +149 -0
  5. package/dist/index.css.map +1 -0
  6. package/dist/index.d.ts +24 -0
  7. package/dist/index.js +1546 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/layout-system/builders.d.ts +13 -0
  10. package/dist/layout-system/index.d.ts +24 -0
  11. package/dist/layout-system/inference/context.d.ts +33 -0
  12. package/dist/layout-system/inference/responsive.d.ts +21 -0
  13. package/dist/layout-system/inference/style.d.ts +15 -0
  14. package/dist/layout-system/page-layouts/index.d.ts +8 -0
  15. package/dist/layout-system/primitives/index.d.ts +6 -0
  16. package/dist/layout-system/regions/index.d.ts +4 -0
  17. package/dist/layout-system/registry/builtins.d.ts +8 -0
  18. package/dist/layout-system/registry/registry.d.ts +20 -0
  19. package/dist/layout-system/renderer/context.d.ts +41 -0
  20. package/dist/layout-system/renderer/region.d.ts +10 -0
  21. package/dist/layout-system/renderer/renderer.d.ts +13 -0
  22. package/dist/layout-system/renderer/root.d.ts +24 -0
  23. package/dist/layout-system/runtime/state.d.ts +17 -0
  24. package/dist/layout-system/runtime/viewport.d.ts +9 -0
  25. package/dist/layout-system/schema/types.d.ts +488 -0
  26. package/dist/layout-system/schema/validate.d.ts +15 -0
  27. package/dist/layout-system/tokens/preset-tokens.d.ts +11 -0
  28. package/package.json +47 -0
  29. package/src/index.tsx +42 -0
  30. package/src/layout-system/__tests__/layout-system.test.tsx +169 -0
  31. package/src/layout-system/builders.ts +46 -0
  32. package/src/layout-system/index.ts +87 -0
  33. package/src/layout-system/inference/context.ts +158 -0
  34. package/src/layout-system/inference/responsive.ts +147 -0
  35. package/src/layout-system/inference/style.ts +128 -0
  36. package/src/layout-system/page-layouts/index.tsx +405 -0
  37. package/src/layout-system/primitives/index.tsx +266 -0
  38. package/src/layout-system/regions/index.tsx +90 -0
  39. package/src/layout-system/registry/builtins.ts +19 -0
  40. package/src/layout-system/registry/registry.ts +47 -0
  41. package/src/layout-system/renderer/context.tsx +89 -0
  42. package/src/layout-system/renderer/region.tsx +34 -0
  43. package/src/layout-system/renderer/renderer.tsx +200 -0
  44. package/src/layout-system/renderer/root.tsx +95 -0
  45. package/src/layout-system/runtime/state.ts +80 -0
  46. package/src/layout-system/runtime/viewport.ts +71 -0
  47. package/src/layout-system/schema/types.ts +706 -0
  48. package/src/layout-system/schema/validate.ts +168 -0
  49. package/src/layout-system/tokens/preset-tokens.ts +77 -0
  50. package/src/styles.css +178 -0
@@ -0,0 +1,706 @@
1
+ /**
2
+ * Typed Layout AST — the type system from `layout-spec.md` §4–§6, §9, §10, §16.
3
+ *
4
+ * A layout is a TYPED STRUCTURE, not a CSS fragment. These types describe the
5
+ * logical layout tree (the AST); the physical plan (placement/visibility per
6
+ * viewport) is derived separately by the responsive resolver. Component style
7
+ * params are INFERRED from the runtime context, never hard-coded by components.
8
+ */
9
+ import type {
10
+ EthDensityMode,
11
+ EthPalette,
12
+ EthStylePreset,
13
+ EthTypeScale,
14
+ } from "@echothink-ui/style";
15
+
16
+ /* ------------------------------------------------------------------ *
17
+ * 4.1 Base enums
18
+ * ------------------------------------------------------------------ */
19
+
20
+ export type LayoutTier = "primitive" | "region" | "page" | "workspace" | "route";
21
+
22
+ export type NodeKind = "layout" | "component" | "template" | "fragment" | "empty";
23
+
24
+ export type CompositionKind =
25
+ | "parallel"
26
+ | "stack"
27
+ | "embed"
28
+ | "overlay"
29
+ | "switch"
30
+ | "stepper"
31
+ | "portal"
32
+ | "conditional"
33
+ | "responsive";
34
+
35
+ export type Axis = "x" | "y" | "z";
36
+
37
+ export type SurfaceRole =
38
+ | "app"
39
+ | "page"
40
+ | "workspace"
41
+ | "region"
42
+ | "panel"
43
+ | "card"
44
+ | "sheet"
45
+ | "drawer"
46
+ | "modal"
47
+ | "popover"
48
+ | "floating"
49
+ | "console"
50
+ | "canvas";
51
+
52
+ export type SlotRole =
53
+ | "navigation"
54
+ | "primary"
55
+ | "secondary"
56
+ | "inspector"
57
+ | "toolbar"
58
+ | "filter"
59
+ | "search"
60
+ | "content"
61
+ | "canvas"
62
+ | "console"
63
+ | "timeline"
64
+ | "stepper"
65
+ | "form"
66
+ | "preview"
67
+ | "summary"
68
+ | "drawer"
69
+ | "modal"
70
+ | "overlay"
71
+ | "footer"
72
+ | "status"
73
+ | "emptyState";
74
+
75
+ /* ------------------------------------------------------------------ *
76
+ * 4.2 Component style params (the layout system's final output protocol).
77
+ * Re-uses the real `@echothink-ui/style` axes verbatim.
78
+ * ------------------------------------------------------------------ */
79
+
80
+ export type ComponentStylePreset = EthStylePreset;
81
+ export type ComponentPalette = EthPalette;
82
+ export type ComponentDensity = EthDensityMode;
83
+ export type ComponentTypeScale = EthTypeScale;
84
+
85
+ export interface ComponentStyleParams {
86
+ preset: ComponentStylePreset;
87
+ palette: ComponentPalette;
88
+ density: ComponentDensity;
89
+ typeScale: ComponentTypeScale;
90
+ }
91
+
92
+ /* ------------------------------------------------------------------ *
93
+ * Layout style intent + task semantics
94
+ * ------------------------------------------------------------------ */
95
+
96
+ export type LayoutTaskMode =
97
+ | "navigation"
98
+ | "crud"
99
+ | "analytics"
100
+ | "monitoring"
101
+ | "authoring"
102
+ | "review"
103
+ | "wizard"
104
+ | "playground"
105
+ | "copilot"
106
+ | "spatial";
107
+
108
+ export type DataIntensity = "low" | "medium" | "high" | "realtime";
109
+ export type InteractionMode = "read" | "edit" | "operate" | "debug" | "review";
110
+ export type VisualEmphasis = "quiet" | "balanced" | "expressive" | "immersive";
111
+
112
+ export interface LayoutStyleIntent {
113
+ preset?: ComponentStylePreset;
114
+ density?: ComponentDensity;
115
+ typeScale?: ComponentTypeScale;
116
+ surface?: SurfaceRole;
117
+ taskMode?: LayoutTaskMode;
118
+ dataIntensity?: DataIntensity;
119
+ interactionMode?: InteractionMode;
120
+ visualEmphasis?: VisualEmphasis;
121
+ }
122
+
123
+ /* ------------------------------------------------------------------ *
124
+ * 4.3 LayoutNode
125
+ * ------------------------------------------------------------------ */
126
+
127
+ export interface LayoutNode {
128
+ kind: "layout";
129
+ id: string;
130
+ type: string;
131
+ tier?: LayoutTier;
132
+ variant?: string;
133
+
134
+ slots: Record<string, SlotContent>;
135
+ props?: Record<string, unknown>;
136
+
137
+ composition?: CompositionSpec;
138
+ constraints?: LayoutConstraints;
139
+ responsive?: ResponsiveRule[];
140
+ state?: LayoutStateSpec;
141
+ permissions?: PermissionSpec;
142
+
143
+ /** Explicit style intent; NOT the final `ComponentStyleParams`. */
144
+ styleIntent?: Partial<LayoutStyleIntent>;
145
+ /** Context-propagation policy toward children. */
146
+ contextPolicy?: Partial<ContextPolicy>;
147
+ /** Debug / observability / AI-generation explanation. */
148
+ meta?: LayoutNodeMeta;
149
+ }
150
+
151
+ export interface LayoutNodeMeta {
152
+ label?: string;
153
+ description?: string;
154
+ generatedBy?: "human" | "ai" | "template";
155
+ notes?: string;
156
+ [key: string]: unknown;
157
+ }
158
+
159
+ export interface LayoutConstraints {
160
+ minWidth?: number | string;
161
+ maxWidth?: number | string;
162
+ minHeight?: number | string;
163
+ maxHeight?: number | string;
164
+ maxDepth?: number;
165
+ }
166
+
167
+ export interface LayoutStateSpec {
168
+ scope?: LayoutStateScope;
169
+ key?: string;
170
+ }
171
+
172
+ export interface PermissionSpec {
173
+ require?: string[];
174
+ anyOf?: string[];
175
+ }
176
+
177
+ /* ------------------------------------------------------------------ *
178
+ * 4.4 SlotDefinition
179
+ * ------------------------------------------------------------------ */
180
+
181
+ export interface SlotDefinition {
182
+ name: string;
183
+ role: SlotRole;
184
+
185
+ accepts: NodeAcceptSpec[];
186
+ cardinality?: { min?: number; max?: number | "unbounded" };
187
+
188
+ required?: boolean;
189
+ multiple?: boolean;
190
+
191
+ constraints?: SlotConstraints;
192
+ priority?: SlotPriority;
193
+ responsive?: SlotResponsivePolicy;
194
+
195
+ /** How this slot transforms the child component style context. */
196
+ contextTransform?: SlotContextTransform;
197
+ /** Empty/loading/error fallback. */
198
+ fallback?: SlotFallbackSpec;
199
+ }
200
+
201
+ export type NodeAcceptSpec =
202
+ | { kind: "component"; componentTypes?: string[]; componentTags?: string[] }
203
+ | {
204
+ kind: "layout";
205
+ layoutTypes?: string[];
206
+ tiers?: LayoutTier[];
207
+ surfaces?: SurfaceRole[];
208
+ }
209
+ | { kind: "template"; templateTypes?: string[] }
210
+ | { kind: "fragment" }
211
+ | { kind: "empty" };
212
+
213
+ export interface SlotConstraints {
214
+ minWidth?: number | string;
215
+ maxWidth?: number | string;
216
+ minHeight?: number | string;
217
+ }
218
+
219
+ export interface SlotContextTransform {
220
+ surface?: SurfaceRole;
221
+ styleIntent?: Partial<LayoutStyleIntent>;
222
+ densityHint?: ComponentDensity;
223
+ typeScaleHint?: ComponentTypeScale;
224
+ }
225
+
226
+ export interface SlotFallbackSpec {
227
+ empty?: SlotContent;
228
+ loading?: SlotContent;
229
+ error?: SlotContent;
230
+ }
231
+
232
+ export interface SlotPriority {
233
+ value: number;
234
+ behaviorOnNarrow:
235
+ | "keep"
236
+ | "collapse"
237
+ | "move-to-drawer"
238
+ | "move-to-bottom-sheet"
239
+ | "hide"
240
+ | "route-stack"
241
+ | "tabs";
242
+ }
243
+
244
+ export interface SlotResponsivePolicy {
245
+ collapseBelow?: Breakpoint;
246
+ hideBelow?: Breakpoint;
247
+ }
248
+
249
+ /* ------------------------------------------------------------------ *
250
+ * 4.5 SlotContent
251
+ * ------------------------------------------------------------------ */
252
+
253
+ export type SlotContent =
254
+ | ComponentSlotContent
255
+ | LayoutSlotContent
256
+ | TemplateSlotContent
257
+ | FragmentSlotContent
258
+ | EmptySlotContent;
259
+
260
+ export interface ComponentSlotContent {
261
+ kind: "component";
262
+ component: string;
263
+ props?: Record<string, unknown>;
264
+ /** Discouraged long-term; special cases only. */
265
+ styleOverride?: Partial<ComponentStyleParams>;
266
+ }
267
+
268
+ export interface LayoutSlotContent {
269
+ kind: "layout";
270
+ layout: LayoutNode;
271
+ }
272
+
273
+ export interface TemplateSlotContent {
274
+ kind: "template";
275
+ template: string;
276
+ props?: Record<string, unknown>;
277
+ }
278
+
279
+ export interface FragmentSlotContent {
280
+ kind: "fragment";
281
+ items: SlotContent[];
282
+ composition?: CompositionSpec;
283
+ }
284
+
285
+ export interface EmptySlotContent {
286
+ kind: "empty";
287
+ reason?: "not-configured" | "no-data" | "permission-denied" | "loading" | "error";
288
+ }
289
+
290
+ /* ------------------------------------------------------------------ *
291
+ * 5. Container composition algebra
292
+ * ------------------------------------------------------------------ */
293
+
294
+ export type CompositionSpec =
295
+ | ParallelComposition
296
+ | StackComposition
297
+ | EmbedComposition
298
+ | OverlayComposition
299
+ | SwitchComposition
300
+ | StepperComposition
301
+ | PortalComposition
302
+ | ConditionalComposition
303
+ | ResponsiveComposition;
304
+
305
+ export interface ParallelSizing {
306
+ basis?: number | string;
307
+ min?: number | string;
308
+ max?: number | string;
309
+ grow?: number;
310
+ shrink?: number;
311
+ resizable?: boolean;
312
+ collapsible?: boolean;
313
+ }
314
+
315
+ export interface ParallelComposition {
316
+ op: "parallel";
317
+ axis: "x" | "y";
318
+ slots: string[];
319
+ sizing?: Record<string, ParallelSizing>;
320
+ gapToken?: string;
321
+ align?: "start" | "center" | "end" | "stretch";
322
+ }
323
+
324
+ export interface StackComposition {
325
+ op: "stack";
326
+ direction: "vertical" | "horizontal";
327
+ slots: string[];
328
+ gapToken?: string;
329
+ divider?: boolean;
330
+ sticky?: Partial<Record<string, "start" | "end">>;
331
+ }
332
+
333
+ export interface EmbedComposition {
334
+ op: "embed";
335
+ host: { layoutId: string; slot: string };
336
+ child: { nodeId: string };
337
+ adapter?: EmbedAdapter;
338
+ }
339
+
340
+ export interface EmbedAdapter {
341
+ surfaceDemotion?: { from: SurfaceRole; to: SurfaceRole };
342
+ inheritContext?: boolean;
343
+ isolateScroll?: boolean;
344
+ isolateState?: boolean;
345
+ densityAdjustment?: "inherit" | "compact-one-step" | "comfortable-one-step";
346
+ }
347
+
348
+ export interface OverlayComposition {
349
+ op: "overlay";
350
+ baseSlot: string;
351
+ overlays: Array<{
352
+ slot: string;
353
+ layer: "floating" | "popover" | "drawer" | "modal" | "toast";
354
+ anchor?: string;
355
+ placement?: "top" | "right" | "bottom" | "left" | "center";
356
+ dismissible?: boolean;
357
+ modal?: boolean;
358
+ }>;
359
+ }
360
+
361
+ export interface SwitchComposition {
362
+ op: "switch";
363
+ mode: "tabs" | "segmented" | "route-stack" | "accordion";
364
+ activeKey?: string;
365
+ items: Array<{
366
+ key: string;
367
+ label: string;
368
+ content: SlotContent;
369
+ lazy?: boolean;
370
+ keepMounted?: boolean;
371
+ }>;
372
+ }
373
+
374
+ export interface StepperComposition {
375
+ op: "stepper";
376
+ orientation: "horizontal" | "vertical";
377
+ stepSlot: string;
378
+ contentSlot: string;
379
+ steps: Array<{
380
+ id: string;
381
+ label: string;
382
+ optional?: boolean;
383
+ status?: "pending" | "active" | "valid" | "invalid" | "complete";
384
+ content: SlotContent;
385
+ validation?: string;
386
+ }>;
387
+ navigation?: {
388
+ allowSkip?: boolean;
389
+ allowBack?: boolean;
390
+ requireValidBeforeNext?: boolean;
391
+ };
392
+ }
393
+
394
+ export interface PortalComposition {
395
+ op: "portal";
396
+ slot: string;
397
+ target?: string;
398
+ }
399
+
400
+ export interface ConditionalComposition {
401
+ op: "conditional";
402
+ branches: Array<{ when: ConditionExpr; content: SlotContent }>;
403
+ fallback?: SlotContent;
404
+ }
405
+
406
+ export type ConditionExpr =
407
+ | { kind: "permission"; require: string[] }
408
+ | { kind: "featureFlag"; flag: string }
409
+ | { kind: "dataState"; state: "loading" | "error" | "empty" | "ready" }
410
+ | { kind: "viewport"; atLeast?: Breakpoint; atMost?: Breakpoint };
411
+
412
+ export interface ResponsiveComposition {
413
+ op: "responsive";
414
+ rules: ResponsiveRule[];
415
+ }
416
+
417
+ export interface ResponsiveRule {
418
+ when: ResponsiveCondition;
419
+ transform: ResponsiveTransform;
420
+ }
421
+
422
+ export interface ResponsiveCondition {
423
+ breakpoint?: Breakpoint;
424
+ maxBreakpoint?: Breakpoint;
425
+ containerMaxWidth?: number;
426
+ pointer?: "coarse" | "fine";
427
+ }
428
+
429
+ export interface ResponsiveTransform {
430
+ mode?: PhysicalLayoutPlan["mode"];
431
+ collapse?: string[];
432
+ moveToDrawer?: string[];
433
+ hide?: string[];
434
+ stack?: boolean;
435
+ }
436
+
437
+ /* ------------------------------------------------------------------ *
438
+ * 6. Layout context
439
+ * ------------------------------------------------------------------ */
440
+
441
+ export type Breakpoint =
442
+ | "mobile"
443
+ | "tablet"
444
+ | "laptop"
445
+ | "desktop"
446
+ | "wide"
447
+ | "ultra";
448
+
449
+ export interface LayoutViewport {
450
+ breakpoint: Breakpoint;
451
+ width: number;
452
+ height: number;
453
+ pointer: "coarse" | "fine";
454
+ colorScheme: "light" | "dark";
455
+ }
456
+
457
+ export interface LayoutRuntimeContext {
458
+ path: string[];
459
+ depth: number;
460
+ ancestry: Array<{
461
+ id: string;
462
+ type: string;
463
+ tier?: LayoutTier;
464
+ surface?: SurfaceRole;
465
+ slotRole?: SlotRole;
466
+ }>;
467
+
468
+ viewport: LayoutViewport;
469
+ container: { width?: number; height?: number; queryName?: string };
470
+ route?: {
471
+ routeId?: string;
472
+ routePattern?: string;
473
+ workspaceId?: string;
474
+ entityType?: string;
475
+ entityId?: string;
476
+ };
477
+
478
+ slot?: {
479
+ name: string;
480
+ role: SlotRole;
481
+ priority?: number;
482
+ surface?: SurfaceRole;
483
+ };
484
+
485
+ surface: SurfaceRole;
486
+ taskMode?: LayoutTaskMode;
487
+ dataIntensity?: DataIntensity;
488
+ interactionMode?: InteractionMode;
489
+ visualEmphasis?: VisualEmphasis;
490
+
491
+ /** Final style params handed to components. */
492
+ componentStyle: ComponentStyleParams;
493
+ tokens: ResolvedLayoutTokens;
494
+ stateScope: LayoutStateScope;
495
+ permissions?: ResolvedPermissionContext;
496
+ telemetry?: Record<string, unknown>;
497
+ }
498
+
499
+ export interface ContextPolicy {
500
+ inheritStyle: boolean;
501
+ inheritDensity: boolean;
502
+ inheritTypeScale: boolean;
503
+ inheritSurface: boolean;
504
+ isolateState: boolean;
505
+ isolateScroll: boolean;
506
+ allowComponentOverride: boolean;
507
+ maxDepth: number;
508
+ }
509
+
510
+ export interface ResolvedPermissionContext {
511
+ granted: Set<string>;
512
+ }
513
+
514
+ /* ------------------------------------------------------------------ *
515
+ * 7.1 Style inference input
516
+ * ------------------------------------------------------------------ */
517
+
518
+ export interface StyleInferenceInput {
519
+ parent?: ComponentStyleParams;
520
+ explicit?: Partial<ComponentStyleParams>;
521
+ layoutType: string;
522
+ layoutTier?: LayoutTier;
523
+ layoutVariant?: string;
524
+ surface: SurfaceRole;
525
+ slotRole?: SlotRole;
526
+ taskMode?: LayoutTaskMode;
527
+ dataIntensity?: DataIntensity;
528
+ interactionMode?: InteractionMode;
529
+ visualEmphasis?: VisualEmphasis;
530
+ depth: number;
531
+ breakpoint: Breakpoint;
532
+ containerWidth?: number;
533
+ userPreference?: Partial<ComponentStyleParams>;
534
+ productDefault?: Partial<ComponentStyleParams>;
535
+ }
536
+
537
+ /* ------------------------------------------------------------------ *
538
+ * 13. Tokens
539
+ * ------------------------------------------------------------------ */
540
+
541
+ export interface PresetTokenSet {
542
+ surface: string;
543
+ surfaceMuted: string;
544
+ border: string;
545
+ text: string;
546
+ textMuted: string;
547
+ accent: string;
548
+ radius: string;
549
+ shadow: string;
550
+ backdrop?: string;
551
+ }
552
+
553
+ export type StylePresetTokenMap = Record<ComponentStylePreset, PresetTokenSet>;
554
+
555
+ export interface ResolvedLayoutTokens extends PresetTokenSet {
556
+ gap: string;
557
+ }
558
+
559
+ /* ------------------------------------------------------------------ *
560
+ * 10. Physical layout plan
561
+ * ------------------------------------------------------------------ */
562
+
563
+ export interface PhysicalLayoutPlan {
564
+ layoutId: string;
565
+ mode:
566
+ | "multi-pane"
567
+ | "single-pane"
568
+ | "drawer-assisted"
569
+ | "sheet-assisted"
570
+ | "route-stack";
571
+ regions: PhysicalRegion[];
572
+ }
573
+
574
+ export interface PhysicalRegion {
575
+ slot: string;
576
+ placement:
577
+ | "inline-start"
578
+ | "inline-end"
579
+ | "block-start"
580
+ | "block-end"
581
+ | "center"
582
+ | "overlay";
583
+ visible: boolean;
584
+ collapsed?: boolean;
585
+ mounted?: boolean;
586
+ layer?: SurfaceRole;
587
+ size?: { width?: number | string; height?: number | string };
588
+ }
589
+
590
+ /* ------------------------------------------------------------------ *
591
+ * 11. State persistence
592
+ * ------------------------------------------------------------------ */
593
+
594
+ export type LayoutStateScope =
595
+ | "global"
596
+ | "workspace"
597
+ | "project"
598
+ | "route"
599
+ | "user"
600
+ | "session";
601
+
602
+ export interface PersistedLayoutState {
603
+ schemaVersion: 2;
604
+ layoutId: string;
605
+ userId?: string;
606
+ workspaceId?: string;
607
+ projectId?: string;
608
+ routePattern?: string;
609
+ panels: Record<
610
+ string,
611
+ {
612
+ width?: number;
613
+ height?: number;
614
+ collapsed?: boolean;
615
+ pinned?: boolean;
616
+ activeTab?: string;
617
+ splitRatio?: number;
618
+ }
619
+ >;
620
+ stylePreference?: Partial<ComponentStyleParams>;
621
+ updatedAt: string;
622
+ }
623
+
624
+ /* ------------------------------------------------------------------ *
625
+ * 16. Validation diagnostics
626
+ * ------------------------------------------------------------------ */
627
+
628
+ export type LayoutDiagnosticCode =
629
+ | "UNKNOWN_LAYOUT_TYPE"
630
+ | "UNKNOWN_SLOT"
631
+ | "SLOT_REQUIRED"
632
+ | "SLOT_CARDINALITY_VIOLATION"
633
+ | "SLOT_ACCEPTS_VIOLATION"
634
+ | "SURFACE_INCOMPATIBLE"
635
+ | "CYCLE_DETECTED"
636
+ | "MAX_DEPTH_EXCEEDED"
637
+ | "RESPONSIVE_FALLBACK_MISSING"
638
+ | "A11Y_SEMANTICS_MISSING"
639
+ | "STYLE_OVERRIDE_REJECTED";
640
+
641
+ export interface LayoutDiagnostic {
642
+ level: "error" | "warning" | "info";
643
+ code: LayoutDiagnosticCode;
644
+ path: string[];
645
+ message: string;
646
+ suggestion?: string;
647
+ }
648
+
649
+ /* ------------------------------------------------------------------ *
650
+ * 9. Registry item
651
+ * ------------------------------------------------------------------ */
652
+
653
+ export interface LayoutVariant {
654
+ id: string;
655
+ label?: string;
656
+ description?: string;
657
+ }
658
+
659
+ export interface LayoutAcceptRule {
660
+ layoutTypes?: string[];
661
+ tiers?: LayoutTier[];
662
+ surfaces?: SurfaceRole[];
663
+ }
664
+
665
+ export type LayoutCategory =
666
+ | "app-shell"
667
+ | "page"
668
+ | "workspace"
669
+ | "region"
670
+ | "primitive";
671
+
672
+ /**
673
+ * Props handed to a registry item's `renderer`. The engine has already resolved
674
+ * each slot's `SlotContent` into a React node (wrapped in the child context),
675
+ * so renderers just arrange the pre-rendered `slots` map into boxes.
676
+ */
677
+ export interface LayoutRendererProps {
678
+ node: LayoutNode;
679
+ variant?: string;
680
+ props?: Record<string, unknown>;
681
+ /** Pre-rendered slot content, keyed by slot name. */
682
+ slots: Record<string, import("react").ReactNode>;
683
+ /** This node's own runtime context (parent of the slots' contexts). */
684
+ context: LayoutRuntimeContext;
685
+ composition?: CompositionSpec;
686
+ /** Physical plan for this node (placement/visibility per slot). */
687
+ plan: PhysicalLayoutPlan;
688
+ }
689
+
690
+ export interface LayoutRegistryItem {
691
+ type: string;
692
+ displayName: string;
693
+ tier: LayoutTier;
694
+ category: LayoutCategory;
695
+ description: string;
696
+ slots: SlotDefinition[];
697
+ variants?: LayoutVariant[];
698
+ defaultProps?: Record<string, unknown>;
699
+ defaultComposition?: CompositionSpec;
700
+ defaultStyleIntent?: Partial<LayoutStyleIntent>;
701
+ contextPolicy?: Partial<ContextPolicy>;
702
+ allowedParents?: LayoutAcceptRule[];
703
+ allowedChildren?: LayoutAcceptRule[];
704
+ /** The React renderer for this layout type. */
705
+ renderer: import("react").ComponentType<LayoutRendererProps>;
706
+ }