@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
package/dist/index.js ADDED
@@ -0,0 +1,1546 @@
1
+ // src/index.tsx
2
+ import {
3
+ StyleScope as StyleScope2,
4
+ defaultStylePreset,
5
+ ethDensityModes,
6
+ ethStylePresetDescriptions,
7
+ ethStylePresetLabels,
8
+ ethStylePresets,
9
+ ethTypeScales
10
+ } from "@echothink-ui/style";
11
+
12
+ // src/layout-system/schema/validate.ts
13
+ var DEFAULT_MAX_DEPTH = 8;
14
+ function acceptsContent(accepts, content) {
15
+ return accepts.some((spec) => {
16
+ switch (content.kind) {
17
+ case "component":
18
+ return spec.kind === "component" && (!spec.componentTypes || spec.componentTypes.includes(content.component));
19
+ case "layout":
20
+ return spec.kind === "layout" && (!spec.layoutTypes || spec.layoutTypes.includes(content.layout.type));
21
+ case "template":
22
+ return spec.kind === "template" && (!spec.templateTypes || spec.templateTypes.includes(content.template));
23
+ case "fragment":
24
+ return spec.kind === "fragment";
25
+ case "empty":
26
+ return spec.kind === "empty";
27
+ default:
28
+ return false;
29
+ }
30
+ });
31
+ }
32
+ function* childLayouts(content) {
33
+ if (content.kind === "layout") {
34
+ yield content.layout;
35
+ } else if (content.kind === "fragment") {
36
+ for (const item of content.items) yield* childLayouts(item);
37
+ }
38
+ }
39
+ function validateLayout(root, registry, opts = {}) {
40
+ const diagnostics = [];
41
+ const maxDepth = opts.maxDepth ?? DEFAULT_MAX_DEPTH;
42
+ const visiting = /* @__PURE__ */ new Set();
43
+ function walk(node, path, depth) {
44
+ const nodePath = [...path, node.id];
45
+ if (depth > maxDepth) {
46
+ diagnostics.push({
47
+ level: "error",
48
+ code: "MAX_DEPTH_EXCEEDED",
49
+ path: nodePath,
50
+ message: `Layout nesting exceeds maxDepth=${maxDepth}.`
51
+ });
52
+ return;
53
+ }
54
+ if (visiting.has(node.id)) {
55
+ diagnostics.push({
56
+ level: "error",
57
+ code: "CYCLE_DETECTED",
58
+ path: nodePath,
59
+ message: `Cycle detected at node "${node.id}".`
60
+ });
61
+ return;
62
+ }
63
+ visiting.add(node.id);
64
+ const item = registry.get(node.type);
65
+ if (!item) {
66
+ diagnostics.push({
67
+ level: "error",
68
+ code: "UNKNOWN_LAYOUT_TYPE",
69
+ path: nodePath,
70
+ message: `Unknown layout type "${node.type}".`,
71
+ suggestion: `Register "${node.type}" or use one of: ${registry.types().slice(0, 8).join(", ")}\u2026`
72
+ });
73
+ visiting.delete(node.id);
74
+ return;
75
+ }
76
+ const slotByName = new Map(
77
+ item.slots.map((s) => [s.name, s])
78
+ );
79
+ for (const slotName of Object.keys(node.slots)) {
80
+ if (!slotByName.has(slotName)) {
81
+ diagnostics.push({
82
+ level: "warning",
83
+ code: "UNKNOWN_SLOT",
84
+ path: [...nodePath, slotName],
85
+ message: `Slot "${slotName}" is not declared by "${node.type}".`
86
+ });
87
+ }
88
+ }
89
+ for (const slot of item.slots) {
90
+ const content = node.slots[slot.name];
91
+ if (content === void 0) {
92
+ if (slot.required && !slot.fallback?.empty) {
93
+ diagnostics.push({
94
+ level: "error",
95
+ code: "SLOT_REQUIRED",
96
+ path: [...nodePath, slot.name],
97
+ message: `Required slot "${slot.name}" of "${node.type}" has no content or fallback.`
98
+ });
99
+ }
100
+ continue;
101
+ }
102
+ if (!acceptsContent(slot.accepts, content)) {
103
+ diagnostics.push({
104
+ level: "error",
105
+ code: "SLOT_ACCEPTS_VIOLATION",
106
+ path: [...nodePath, slot.name],
107
+ message: `Slot "${slot.name}" of "${node.type}" does not accept ${content.kind} content.`
108
+ });
109
+ }
110
+ if (content.kind === "fragment" && slot.cardinality?.max && slot.cardinality.max !== "unbounded") {
111
+ if (content.items.length > slot.cardinality.max) {
112
+ diagnostics.push({
113
+ level: "error",
114
+ code: "SLOT_CARDINALITY_VIOLATION",
115
+ path: [...nodePath, slot.name],
116
+ message: `Slot "${slot.name}" allows at most ${slot.cardinality.max} items.`
117
+ });
118
+ }
119
+ }
120
+ for (const child of childLayouts(content)) {
121
+ walk(child, [...nodePath, slot.name], depth + 1);
122
+ }
123
+ }
124
+ visiting.delete(node.id);
125
+ }
126
+ walk(root, [], 0);
127
+ return diagnostics;
128
+ }
129
+ function hasErrors(diagnostics) {
130
+ return diagnostics.some((d) => d.level === "error");
131
+ }
132
+
133
+ // src/layout-system/tokens/preset-tokens.ts
134
+ var presetTokenMap = {
135
+ "carbon-like": {
136
+ surface: "var(--eth-surface, var(--eth-color-surface))",
137
+ surfaceMuted: "var(--eth-surface-muted, var(--eth-color-surface-muted))",
138
+ border: "var(--eth-border-subtle, var(--eth-color-border))",
139
+ text: "var(--eth-text-primary, var(--eth-color-text))",
140
+ textMuted: "var(--eth-text-secondary, var(--eth-color-text-secondary))",
141
+ accent: "var(--eth-accent, var(--eth-color-accent))",
142
+ radius: "var(--eth-radius-sm, 0.125rem)",
143
+ shadow: "var(--eth-shadow-none, none)"
144
+ },
145
+ "soft-card": {
146
+ surface: "var(--eth-surface, var(--eth-color-surface))",
147
+ surfaceMuted: "var(--eth-surface-muted, var(--eth-color-surface-muted))",
148
+ border: "var(--eth-border-soft, var(--eth-color-border))",
149
+ text: "var(--eth-text-primary, var(--eth-color-text))",
150
+ textMuted: "var(--eth-text-secondary, var(--eth-color-text-secondary))",
151
+ accent: "var(--eth-accent, var(--eth-color-accent))",
152
+ radius: "var(--eth-radius-lg, 0.75rem)",
153
+ shadow: "var(--eth-shadow-card, 0 1px 2px rgba(15,23,42,0.08))"
154
+ },
155
+ glass: {
156
+ surface: "var(--eth-surface-glass, var(--eth-color-surface))",
157
+ surfaceMuted: "var(--eth-surface-glass-muted, var(--eth-color-surface-muted))",
158
+ border: "var(--eth-border-glass, var(--eth-color-border))",
159
+ text: "var(--eth-text-on-glass, var(--eth-color-text))",
160
+ textMuted: "var(--eth-text-on-glass-muted, var(--eth-color-text-secondary))",
161
+ accent: "var(--eth-accent, var(--eth-color-accent))",
162
+ radius: "var(--eth-radius-xl, 1rem)",
163
+ shadow: "var(--eth-shadow-floating, 0 8px 32px rgba(15,23,42,0.18))",
164
+ backdrop: "blur(16px)"
165
+ },
166
+ bright: {
167
+ surface: "var(--eth-surface, var(--eth-color-surface))",
168
+ surfaceMuted: "var(--eth-surface-muted, var(--eth-color-surface-muted))",
169
+ border: "var(--eth-border-bright, var(--eth-color-border))",
170
+ text: "var(--eth-text-primary, var(--eth-color-text))",
171
+ textMuted: "var(--eth-text-secondary, var(--eth-color-text-secondary))",
172
+ accent: "var(--eth-accent, var(--eth-color-accent))",
173
+ radius: "var(--eth-radius-md, 0.5rem)",
174
+ shadow: "var(--eth-shadow-sm, 0 1px 2px rgba(15,23,42,0.06))"
175
+ },
176
+ "studio-dark": {
177
+ surface: "var(--eth-surface-studio, var(--eth-color-surface))",
178
+ surfaceMuted: "var(--eth-surface-studio-muted, var(--eth-color-surface-muted))",
179
+ border: "var(--eth-border-studio, var(--eth-color-border))",
180
+ text: "var(--eth-text-on-dark, var(--eth-color-text))",
181
+ textMuted: "var(--eth-text-on-dark-muted, var(--eth-color-text-secondary))",
182
+ accent: "var(--eth-accent, var(--eth-color-accent))",
183
+ radius: "var(--eth-radius-sm, 0.125rem)",
184
+ shadow: "var(--eth-shadow-none, none)"
185
+ }
186
+ };
187
+ var densityGap = {
188
+ compact: "var(--eth-space-sm, 0.5rem)",
189
+ standard: "var(--eth-space-md, 0.75rem)",
190
+ comfortable: "var(--eth-space-lg, 1rem)"
191
+ };
192
+ function resolveTokens(style) {
193
+ return { ...presetTokenMap[style.preset], gap: densityGap[style.density] };
194
+ }
195
+
196
+ // src/layout-system/inference/style.ts
197
+ var defaultComponentStyle = {
198
+ preset: "carbon-like",
199
+ palette: "default",
200
+ density: "standard",
201
+ typeScale: "standard"
202
+ };
203
+ function matches(value, candidates) {
204
+ if (!value) return false;
205
+ return candidates.some((c) => value === c || value.endsWith(`.${c}`) || value.includes(c));
206
+ }
207
+ function chooseHighestScore(scores, fallback) {
208
+ let best = fallback;
209
+ let bestScore = -Infinity;
210
+ const order = [
211
+ "carbon-like",
212
+ "soft-card",
213
+ "glass",
214
+ "bright",
215
+ "studio-dark"
216
+ ];
217
+ for (const key of order) {
218
+ if (scores[key] > bestScore) {
219
+ bestScore = scores[key];
220
+ best = key;
221
+ }
222
+ }
223
+ return bestScore <= 0 ? fallback : best;
224
+ }
225
+ function inferDensity(input) {
226
+ if (input.explicit?.density) return input.explicit.density;
227
+ const role = input.slotRole;
228
+ if (input.dataIntensity === "high" || input.dataIntensity === "realtime") return "compact";
229
+ if (matches(role, ["console", "timeline", "filter", "toolbar", "navigation"]) || matches(input.layoutType, ["DataGridInspector", "MonitoringOps"])) {
230
+ return "compact";
231
+ }
232
+ if (input.visualEmphasis === "expressive" || input.visualEmphasis === "immersive") {
233
+ return "comfortable";
234
+ }
235
+ if (matches(role, ["form", "inspector", "summary", "content", "preview"])) return "standard";
236
+ if (input.taskMode === "wizard" || input.taskMode === "copilot") return "comfortable";
237
+ if (input.breakpoint === "mobile" && input.parent?.density === "compact") return "standard";
238
+ return input.parent?.density ?? "standard";
239
+ }
240
+ function inferTypeScale(input) {
241
+ if (input.explicit?.typeScale) return input.explicit.typeScale;
242
+ const role = input.slotRole;
243
+ if (typeof input.containerWidth === "number" && input.containerWidth < 360) return "compact";
244
+ if (matches(role, ["toolbar", "navigation", "console", "timeline"])) return "compact";
245
+ if (matches(input.layoutType, ["DataGridInspector", "MonitoringOps"])) return "compact";
246
+ if (input.taskMode === "wizard" || matches(role, ["summary", "emptyState"])) return "large";
247
+ if (input.breakpoint === "mobile") return "standard";
248
+ if (matches(role, ["form", "inspector", "content", "preview"])) return "standard";
249
+ return input.parent?.typeScale ?? "standard";
250
+ }
251
+ function inferComponentStyle(input) {
252
+ const presetScores = {
253
+ "carbon-like": 0,
254
+ "soft-card": 0,
255
+ glass: 0,
256
+ bright: 0,
257
+ "studio-dark": 0
258
+ };
259
+ if (matches(input.layoutType, ["AdminShell", "DataGridInspector", "SettingsConsole"])) {
260
+ presetScores["carbon-like"] += 4;
261
+ }
262
+ if (matches(input.layoutType, ["DashboardBento", "SearchCatalog"])) {
263
+ presetScores["soft-card"] += 4;
264
+ }
265
+ if (matches(input.layoutType, ["SpatialHud"]) || input.surface === "floating") {
266
+ presetScores.glass += 4;
267
+ }
268
+ if (matches(input.layoutType, ["WizardComposer"]) || input.taskMode === "wizard") {
269
+ presetScores.bright += 4;
270
+ }
271
+ if (matches(input.layoutType, ["Workbench", "CanvasInspector", "MonitoringOps", "Playground"]) || matches(input.slotRole, ["console", "canvas", "timeline"])) {
272
+ presetScores["studio-dark"] += 4;
273
+ }
274
+ if (input.depth >= 2 && matches(input.slotRole, ["form", "filter", "inspector"])) {
275
+ presetScores["carbon-like"] += 2;
276
+ }
277
+ const preset = input.explicit?.preset ?? chooseHighestScore(presetScores, input.parent?.preset ?? defaultComponentStyle.preset);
278
+ return {
279
+ preset,
280
+ palette: input.explicit?.palette ?? input.parent?.palette ?? "default",
281
+ density: inferDensity(input),
282
+ typeScale: inferTypeScale(input)
283
+ };
284
+ }
285
+
286
+ // src/layout-system/inference/context.ts
287
+ var defaultContextPolicy = {
288
+ inheritStyle: true,
289
+ inheritDensity: true,
290
+ inheritTypeScale: true,
291
+ inheritSurface: false,
292
+ isolateState: false,
293
+ isolateScroll: true,
294
+ allowComponentOverride: true,
295
+ maxDepth: 8
296
+ };
297
+ var defaultViewport = {
298
+ breakpoint: "desktop",
299
+ width: 1440,
300
+ height: 900,
301
+ pointer: "fine",
302
+ colorScheme: "light"
303
+ };
304
+ function createRootContext(input = {}) {
305
+ const viewport = { ...defaultViewport, ...input.viewport };
306
+ const componentStyle = {
307
+ ...defaultComponentStyle,
308
+ ...input.style
309
+ };
310
+ return {
311
+ path: [],
312
+ depth: 0,
313
+ ancestry: [],
314
+ viewport,
315
+ container: { width: viewport.width, height: viewport.height },
316
+ route: input.route,
317
+ surface: input.surface ?? "app",
318
+ componentStyle,
319
+ tokens: resolveTokens(componentStyle),
320
+ stateScope: "session",
321
+ permissions: input.permissions ? { granted: new Set(input.permissions) } : void 0
322
+ };
323
+ }
324
+ function inferChildSurface(parent, node, slot) {
325
+ return slot.contextTransform?.surface ?? node.styleIntent?.surface ?? (parent.surface === "app" ? "page" : parent.surface);
326
+ }
327
+ function buildStyleInferenceInput(args) {
328
+ const { parent, node, slot, content } = args;
329
+ const surface = inferChildSurface(parent, node, slot);
330
+ const explicit = {
331
+ ...node.styleIntent,
332
+ ...slot.contextTransform?.styleIntent,
333
+ ...slot.contextTransform?.densityHint ? { density: slot.contextTransform.densityHint } : {},
334
+ ...slot.contextTransform?.typeScaleHint ? { typeScale: slot.contextTransform.typeScaleHint } : {},
335
+ ...content.kind === "component" ? content.styleOverride : void 0
336
+ };
337
+ return {
338
+ parent: parent.componentStyle,
339
+ explicit,
340
+ layoutType: node.type,
341
+ layoutTier: node.tier,
342
+ layoutVariant: node.variant,
343
+ surface,
344
+ slotRole: slot.role,
345
+ taskMode: node.styleIntent?.taskMode ?? parent.taskMode,
346
+ dataIntensity: node.styleIntent?.dataIntensity ?? parent.dataIntensity,
347
+ interactionMode: node.styleIntent?.interactionMode ?? parent.interactionMode,
348
+ visualEmphasis: node.styleIntent?.visualEmphasis ?? parent.visualEmphasis,
349
+ depth: parent.depth + 1,
350
+ breakpoint: parent.viewport.breakpoint,
351
+ containerWidth: parent.container.width
352
+ };
353
+ }
354
+ function composeLayoutContext(args) {
355
+ const { parent, node, slot, content } = args;
356
+ const styleInput = buildStyleInferenceInput(args);
357
+ const componentStyle = inferComponentStyle(styleInput);
358
+ const surface = styleInput.surface;
359
+ return {
360
+ ...parent,
361
+ path: [...parent.path, node.id, slot.name],
362
+ depth: parent.depth + 1,
363
+ ancestry: [
364
+ ...parent.ancestry,
365
+ {
366
+ id: node.id,
367
+ type: node.type,
368
+ tier: node.tier,
369
+ surface: node.styleIntent?.surface,
370
+ slotRole: slot.role
371
+ }
372
+ ],
373
+ slot: {
374
+ name: slot.name,
375
+ role: slot.role,
376
+ priority: slot.priority?.value,
377
+ surface: slot.contextTransform?.surface
378
+ },
379
+ surface,
380
+ taskMode: node.styleIntent?.taskMode ?? parent.taskMode,
381
+ dataIntensity: node.styleIntent?.dataIntensity ?? parent.dataIntensity,
382
+ interactionMode: node.styleIntent?.interactionMode ?? parent.interactionMode,
383
+ visualEmphasis: node.styleIntent?.visualEmphasis ?? parent.visualEmphasis,
384
+ componentStyle,
385
+ tokens: resolveTokens(componentStyle),
386
+ stateScope: node.state?.scope ?? parent.stateScope
387
+ };
388
+ }
389
+
390
+ // src/layout-system/inference/responsive.ts
391
+ var BREAKPOINT_ORDER = [
392
+ "mobile",
393
+ "tablet",
394
+ "laptop",
395
+ "desktop",
396
+ "wide",
397
+ "ultra"
398
+ ];
399
+ function breakpointRank(bp) {
400
+ return BREAKPOINT_ORDER.indexOf(bp);
401
+ }
402
+ function isNarrow(bp) {
403
+ return breakpointRank(bp) <= breakpointRank("tablet");
404
+ }
405
+ var PLACEMENT_BY_ROLE = {
406
+ navigation: "inline-start",
407
+ filter: "inline-start",
408
+ inspector: "inline-end",
409
+ secondary: "inline-end",
410
+ toolbar: "block-start",
411
+ search: "block-start",
412
+ status: "block-start",
413
+ console: "block-end",
414
+ footer: "block-end",
415
+ timeline: "block-end",
416
+ summary: "inline-end",
417
+ preview: "inline-end",
418
+ drawer: "overlay",
419
+ modal: "overlay",
420
+ overlay: "overlay"
421
+ };
422
+ function placementFor(slot) {
423
+ return PLACEMENT_BY_ROLE[slot.role] ?? "center";
424
+ }
425
+ function resolvePhysicalPlan(input) {
426
+ const { node, item, viewport, state } = input;
427
+ const narrow = isNarrow(viewport.breakpoint);
428
+ const bpRank = breakpointRank(viewport.breakpoint);
429
+ const regions = item.slots.map((slot) => {
430
+ const present = node.slots[slot.name] !== void 0;
431
+ const persisted = state?.panels?.[slot.name];
432
+ const placement = placementFor(slot);
433
+ let visible = present;
434
+ let collapsed = persisted?.collapsed ?? false;
435
+ let layer;
436
+ let regionPlacement = placement;
437
+ if (slot.responsive?.hideBelow && bpRank < breakpointRank(slot.responsive.hideBelow)) {
438
+ visible = false;
439
+ }
440
+ if (slot.responsive?.collapseBelow && bpRank < breakpointRank(slot.responsive.collapseBelow)) {
441
+ collapsed = true;
442
+ }
443
+ if (narrow && slot.priority) {
444
+ switch (slot.priority.behaviorOnNarrow) {
445
+ case "collapse":
446
+ collapsed = true;
447
+ break;
448
+ case "hide":
449
+ if (!slot.required) visible = false;
450
+ break;
451
+ case "move-to-drawer":
452
+ regionPlacement = "overlay";
453
+ layer = "drawer";
454
+ break;
455
+ case "move-to-bottom-sheet":
456
+ regionPlacement = "overlay";
457
+ layer = "sheet";
458
+ break;
459
+ case "route-stack":
460
+ case "tabs":
461
+ case "keep":
462
+ default:
463
+ break;
464
+ }
465
+ }
466
+ return {
467
+ slot: slot.name,
468
+ placement: regionPlacement,
469
+ visible,
470
+ collapsed,
471
+ mounted: present,
472
+ layer,
473
+ size: persisted?.width ? { width: persisted.width } : persisted?.height ? { height: persisted.height } : void 0
474
+ };
475
+ });
476
+ const mode = narrow ? regions.some((r) => r.layer === "drawer" || r.layer === "sheet") ? "drawer-assisted" : "single-pane" : "multi-pane";
477
+ return { layoutId: node.id, mode, regions };
478
+ }
479
+ function regionFor(plan, slot) {
480
+ return plan.regions.find((r) => r.slot === slot);
481
+ }
482
+
483
+ // src/layout-system/registry/registry.ts
484
+ var LayoutRegistry = class {
485
+ items = /* @__PURE__ */ new Map();
486
+ register(item) {
487
+ this.items.set(item.type, item);
488
+ return this;
489
+ }
490
+ registerAll(items) {
491
+ for (const item of items) this.register(item);
492
+ return this;
493
+ }
494
+ get(type) {
495
+ return this.items.get(type);
496
+ }
497
+ has(type) {
498
+ return this.items.has(type);
499
+ }
500
+ list() {
501
+ return [...this.items.values()];
502
+ }
503
+ types() {
504
+ return [...this.items.keys()];
505
+ }
506
+ byTier(tier) {
507
+ return this.list().filter((i) => i.tier === tier);
508
+ }
509
+ byCategory(category) {
510
+ return this.list().filter((i) => i.category === category);
511
+ }
512
+ };
513
+
514
+ // src/layout-system/primitives/index.tsx
515
+ import * as React3 from "react";
516
+
517
+ // src/layout-system/renderer/renderer.tsx
518
+ import * as React2 from "react";
519
+ import { StyleScope } from "@echothink-ui/style";
520
+
521
+ // src/layout-system/renderer/context.tsx
522
+ import * as React from "react";
523
+
524
+ // src/layout-system/runtime/state.ts
525
+ function emptyState(layoutId) {
526
+ return {
527
+ schemaVersion: 2,
528
+ layoutId,
529
+ panels: {},
530
+ updatedAt: ""
531
+ };
532
+ }
533
+ function createMemoryLayoutStateStore(opts) {
534
+ const cache = /* @__PURE__ */ new Map();
535
+ const persistKey = opts?.persistKey;
536
+ function load() {
537
+ if (!persistKey || typeof localStorage === "undefined") return;
538
+ try {
539
+ const raw = localStorage.getItem(persistKey);
540
+ if (!raw) return;
541
+ const parsed = JSON.parse(raw);
542
+ for (const [id, st] of Object.entries(parsed)) cache.set(id, st);
543
+ } catch {
544
+ }
545
+ }
546
+ function flush() {
547
+ if (!persistKey || typeof localStorage === "undefined") return;
548
+ try {
549
+ localStorage.setItem(persistKey, JSON.stringify(Object.fromEntries(cache)));
550
+ } catch {
551
+ }
552
+ }
553
+ load();
554
+ return {
555
+ get(layoutId) {
556
+ return cache.get(layoutId);
557
+ },
558
+ set(layoutId, state) {
559
+ cache.set(layoutId, state);
560
+ flush();
561
+ },
562
+ patchPanel(layoutId, slot, patch) {
563
+ const current = cache.get(layoutId) ?? emptyState(layoutId);
564
+ const next = {
565
+ ...current,
566
+ panels: { ...current.panels, [slot]: { ...current.panels[slot], ...patch } },
567
+ // `updatedAt` is set by the caller environment; left as a marker here.
568
+ updatedAt: current.updatedAt || "pending"
569
+ };
570
+ cache.set(layoutId, next);
571
+ flush();
572
+ return next;
573
+ }
574
+ };
575
+ }
576
+
577
+ // src/layout-system/renderer/context.tsx
578
+ import { jsx } from "react/jsx-runtime";
579
+ var RuntimeContext = React.createContext(createRootContext());
580
+ var EngineContext = React.createContext(null);
581
+ function LayoutContextProvider({
582
+ value,
583
+ children
584
+ }) {
585
+ return /* @__PURE__ */ jsx(RuntimeContext.Provider, { value, children });
586
+ }
587
+ function useLayoutContext() {
588
+ return React.useContext(RuntimeContext);
589
+ }
590
+ function LayoutEngineProvider({
591
+ value,
592
+ children
593
+ }) {
594
+ return /* @__PURE__ */ jsx(EngineContext.Provider, { value, children });
595
+ }
596
+ function useLayoutEngine() {
597
+ const engine = React.useContext(EngineContext);
598
+ if (!engine) {
599
+ throw new Error(
600
+ "useLayoutEngine must be used within <LayoutRoot> / <LayoutEngineProvider>."
601
+ );
602
+ }
603
+ return engine;
604
+ }
605
+ function buildEngine(input) {
606
+ return {
607
+ registry: input.registry,
608
+ componentResolver: input.componentResolver,
609
+ templateResolver: input.templateResolver,
610
+ stateStore: input.stateStore ?? createMemoryLayoutStateStore(),
611
+ onDiagnostics: input.onDiagnostics
612
+ };
613
+ }
614
+
615
+ // src/layout-system/renderer/renderer.tsx
616
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
617
+ function UnknownLayout({ type }) {
618
+ return /* @__PURE__ */ jsxs("div", { "data-ls-unknown-layout": type, className: "eth-ls-unknown", role: "note", children: [
619
+ "Unknown layout type: ",
620
+ /* @__PURE__ */ jsx2("code", { children: type })
621
+ ] });
622
+ }
623
+ function UnknownComponent({ name }) {
624
+ return /* @__PURE__ */ jsxs("div", { "data-ls-unknown-component": name, className: "eth-ls-unknown", role: "note", children: [
625
+ "Unknown component: ",
626
+ /* @__PURE__ */ jsx2("code", { children: name })
627
+ ] });
628
+ }
629
+ function EmptySlot({ reason }) {
630
+ return /* @__PURE__ */ jsx2("div", { "data-ls-empty": reason ?? "not-configured", className: "eth-ls-empty", "aria-hidden": "true" });
631
+ }
632
+ function ComponentRenderer({
633
+ content,
634
+ context
635
+ }) {
636
+ const engine = useLayoutEngine();
637
+ const Component = engine.componentResolver(content.component);
638
+ if (!Component) return /* @__PURE__ */ jsx2(UnknownComponent, { name: content.component });
639
+ const s = context.componentStyle;
640
+ return /* @__PURE__ */ jsx2(
641
+ StyleScope,
642
+ {
643
+ preset: s.preset,
644
+ palette: s.palette,
645
+ density: s.density,
646
+ typeScale: s.typeScale,
647
+ global: false,
648
+ className: "eth-ls-component",
649
+ "data-ls-component": content.component,
650
+ children: /* @__PURE__ */ jsx2(Component, { ...content.props ?? {} })
651
+ }
652
+ );
653
+ }
654
+ function renderContent(content, context, engine) {
655
+ switch (content.kind) {
656
+ case "component":
657
+ return /* @__PURE__ */ jsx2(ComponentRenderer, { content, context });
658
+ case "layout":
659
+ return /* @__PURE__ */ jsx2(LayoutRenderer, { node: content.layout });
660
+ case "template": {
661
+ const resolved = engine.templateResolver?.(content.template);
662
+ if (!resolved) return /* @__PURE__ */ jsx2(UnknownComponent, { name: `template:${content.template}` });
663
+ return /* @__PURE__ */ jsx2(LayoutRenderer, { node: resolved });
664
+ }
665
+ case "fragment": {
666
+ const direction = content.composition?.op === "stack" && content.composition.direction === "horizontal" ? "row" : "column";
667
+ return /* @__PURE__ */ jsx2("div", { className: "eth-ls-fragment", style: { display: "flex", flexDirection: direction, gap: context.tokens.gap }, children: content.items.map((item, i) => /* @__PURE__ */ jsx2(React2.Fragment, { children: renderContent(item, context, engine) }, i)) });
668
+ }
669
+ case "empty":
670
+ return /* @__PURE__ */ jsx2(EmptySlot, { reason: content.reason });
671
+ default:
672
+ return null;
673
+ }
674
+ }
675
+ function SlotContentRenderer({
676
+ node,
677
+ slot,
678
+ content,
679
+ parentContext
680
+ }) {
681
+ const engine = useLayoutEngine();
682
+ const childContext = React2.useMemo(
683
+ () => composeLayoutContext({ parent: parentContext, node, slot, content }),
684
+ [parentContext, node, slot, content]
685
+ );
686
+ return /* @__PURE__ */ jsx2(LayoutContextProvider, { value: childContext, children: renderContent(content, childContext, engine) });
687
+ }
688
+ function LayoutRenderer({ node }) {
689
+ const parentContext = useLayoutContext();
690
+ const engine = useLayoutEngine();
691
+ const item = engine.registry.get(node.type);
692
+ if (!item) return /* @__PURE__ */ jsx2(UnknownLayout, { type: node.type });
693
+ const slots = {};
694
+ for (const slotDef of item.slots) {
695
+ const content = node.slots[slotDef.name] ?? slotDef.fallback?.empty;
696
+ if (content === void 0) continue;
697
+ slots[slotDef.name] = /* @__PURE__ */ jsx2(
698
+ SlotContentRenderer,
699
+ {
700
+ node,
701
+ slot: slotDef,
702
+ content,
703
+ parentContext
704
+ }
705
+ );
706
+ }
707
+ const plan = resolvePhysicalPlan({
708
+ node,
709
+ item,
710
+ viewport: parentContext.viewport,
711
+ state: engine.stateStore.get(node.id)
712
+ });
713
+ const Renderer = item.renderer;
714
+ return /* @__PURE__ */ jsx2(
715
+ Renderer,
716
+ {
717
+ node,
718
+ variant: node.variant,
719
+ props: { ...item.defaultProps, ...node.props },
720
+ slots,
721
+ context: parentContext,
722
+ composition: node.composition ?? item.defaultComposition,
723
+ plan
724
+ }
725
+ );
726
+ }
727
+
728
+ // src/layout-system/renderer/region.tsx
729
+ var ANY_ACCEPTS = [
730
+ { kind: "component" },
731
+ { kind: "layout" },
732
+ { kind: "template" },
733
+ { kind: "fragment" },
734
+ { kind: "empty" }
735
+ ];
736
+ function inlineSlot(name, role = "content") {
737
+ return { name, role, accepts: ANY_ACCEPTS };
738
+ }
739
+ function isVisible(plan, slot) {
740
+ const region = regionFor(plan, slot);
741
+ return region ? region.visible : true;
742
+ }
743
+ function isCollapsed(plan, slot) {
744
+ return regionFor(plan, slot)?.collapsed ?? false;
745
+ }
746
+
747
+ // src/layout-system/primitives/index.tsx
748
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
749
+ function PanelRenderer({ slots, context }) {
750
+ return /* @__PURE__ */ jsxs2(
751
+ "section",
752
+ {
753
+ className: "eth-ls-panel",
754
+ style: {
755
+ background: context.tokens.surface,
756
+ border: `1px solid ${context.tokens.border}`,
757
+ borderRadius: context.tokens.radius,
758
+ boxShadow: context.tokens.shadow,
759
+ color: context.tokens.text
760
+ },
761
+ children: [
762
+ slots.header ? /* @__PURE__ */ jsx3("header", { className: "eth-ls-panel__header", children: slots.header }) : null,
763
+ /* @__PURE__ */ jsx3("div", { className: "eth-ls-panel__body", children: slots.body }),
764
+ slots.footer ? /* @__PURE__ */ jsx3("footer", { className: "eth-ls-panel__footer", children: slots.footer }) : null
765
+ ]
766
+ }
767
+ );
768
+ }
769
+ var PanelLayout = {
770
+ type: "Primitive.Panel",
771
+ displayName: "Panel",
772
+ tier: "primitive",
773
+ category: "primitive",
774
+ description: "A bounded surface with optional header/body/footer.",
775
+ slots: [
776
+ { name: "header", role: "toolbar", accepts: [{ kind: "component" }, { kind: "fragment" }] },
777
+ {
778
+ name: "body",
779
+ role: "content",
780
+ required: true,
781
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }]
782
+ },
783
+ { name: "footer", role: "footer", accepts: [{ kind: "component" }, { kind: "fragment" }] }
784
+ ],
785
+ defaultStyleIntent: { surface: "panel" },
786
+ renderer: PanelRenderer
787
+ };
788
+ function SplitPaneRenderer({ slots, context, composition, variant }) {
789
+ const orientation = variant === "vertical" ? "vertical" : "horizontal";
790
+ const parallel = composition?.op === "parallel" ? composition : void 0;
791
+ const primaryBasis = parallel?.sizing?.primary?.basis;
792
+ return /* @__PURE__ */ jsxs2(
793
+ "div",
794
+ {
795
+ className: `eth-ls-split eth-ls-split--${orientation}`,
796
+ style: {
797
+ display: "grid",
798
+ gap: parallel?.gapToken ? `var(${parallel.gapToken})` : context.tokens.gap,
799
+ gridTemplateColumns: orientation === "horizontal" ? `${primaryBasis ? cssSize(primaryBasis) : "1fr"} 1fr` : void 0,
800
+ gridTemplateRows: orientation === "vertical" ? `${primaryBasis ? cssSize(primaryBasis) : "1fr"} 1fr` : void 0,
801
+ height: "100%",
802
+ minHeight: 0
803
+ },
804
+ children: [
805
+ /* @__PURE__ */ jsx3("div", { className: "eth-ls-split__pane eth-ls-split__pane--primary", style: { minWidth: 0, minHeight: 0 }, children: slots.primary }),
806
+ /* @__PURE__ */ jsx3(
807
+ "div",
808
+ {
809
+ className: "eth-ls-split__pane eth-ls-split__pane--secondary",
810
+ role: "separator",
811
+ "aria-orientation": orientation,
812
+ style: { minWidth: 0, minHeight: 0 },
813
+ children: slots.secondary
814
+ }
815
+ )
816
+ ]
817
+ }
818
+ );
819
+ }
820
+ function cssSize(value) {
821
+ return typeof value === "number" ? `${value}px` : value;
822
+ }
823
+ var SplitPaneLayout = {
824
+ type: "Primitive.SplitPane",
825
+ displayName: "Split Pane",
826
+ tier: "primitive",
827
+ category: "primitive",
828
+ description: "Two resizable panes split horizontally or vertically.",
829
+ variants: [
830
+ { id: "horizontal", label: "Left / Right" },
831
+ { id: "vertical", label: "Top / Bottom" }
832
+ ],
833
+ slots: [
834
+ {
835
+ name: "primary",
836
+ role: "primary",
837
+ required: true,
838
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "template" }, { kind: "fragment" }]
839
+ },
840
+ {
841
+ name: "secondary",
842
+ role: "secondary",
843
+ required: true,
844
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "template" }, { kind: "fragment" }]
845
+ }
846
+ ],
847
+ defaultComposition: {
848
+ op: "parallel",
849
+ axis: "x",
850
+ slots: ["primary", "secondary"],
851
+ sizing: { primary: { basis: "1fr", resizable: true }, secondary: { grow: 1 } }
852
+ },
853
+ renderer: SplitPaneRenderer
854
+ };
855
+ function TabsRenderer({ node, context, composition }) {
856
+ const switchSpec = composition?.op === "switch" ? composition : void 0;
857
+ const items = switchSpec?.items ?? [];
858
+ const initial = switchSpec?.activeKey ?? items[0]?.key;
859
+ const [active, setActive] = React3.useState(initial);
860
+ const activeItem = items.find((i) => i.key === active) ?? items[0];
861
+ return /* @__PURE__ */ jsxs2("div", { className: "eth-ls-tabs", style: { color: context.tokens.text }, children: [
862
+ /* @__PURE__ */ jsx3("div", { className: "eth-ls-tabs__list", role: "tablist", children: items.map((item) => {
863
+ const selected = item.key === activeItem?.key;
864
+ return /* @__PURE__ */ jsx3(
865
+ "button",
866
+ {
867
+ type: "button",
868
+ role: "tab",
869
+ id: `tab-${node.id}-${item.key}`,
870
+ "aria-selected": selected,
871
+ "aria-controls": `tabpanel-${node.id}-${item.key}`,
872
+ tabIndex: selected ? 0 : -1,
873
+ className: `eth-ls-tabs__tab${selected ? " eth-ls-tabs__tab--active" : ""}`,
874
+ onClick: () => setActive(item.key),
875
+ children: item.label
876
+ },
877
+ item.key
878
+ );
879
+ }) }),
880
+ activeItem ? /* @__PURE__ */ jsx3(
881
+ "div",
882
+ {
883
+ className: "eth-ls-tabs__panel",
884
+ role: "tabpanel",
885
+ id: `tabpanel-${node.id}-${activeItem.key}`,
886
+ "aria-labelledby": `tab-${node.id}-${activeItem.key}`,
887
+ children: /* @__PURE__ */ jsx3(
888
+ SlotContentRenderer,
889
+ {
890
+ node,
891
+ slot: inlineSlot(`tab:${activeItem.key}`, "content"),
892
+ content: activeItem.content,
893
+ parentContext: context
894
+ }
895
+ )
896
+ }
897
+ ) : null
898
+ ] });
899
+ }
900
+ var TabsLayout = {
901
+ type: "Primitive.Tabs",
902
+ displayName: "Tabs",
903
+ tier: "primitive",
904
+ category: "primitive",
905
+ description: "Switch composition rendering one panel at a time with tablist semantics.",
906
+ slots: [],
907
+ defaultComposition: { op: "switch", mode: "tabs", items: [] },
908
+ renderer: TabsRenderer
909
+ };
910
+ function StepperRenderer({ node, context, composition }) {
911
+ const stepper = composition?.op === "stepper" ? composition : void 0;
912
+ const steps = stepper?.steps ?? [];
913
+ const firstActive = steps.findIndex((s) => s.status === "active");
914
+ const [activeIndex, setActiveIndex] = React3.useState(firstActive >= 0 ? firstActive : 0);
915
+ const orientation = stepper?.orientation ?? "horizontal";
916
+ const activeStep = steps[activeIndex];
917
+ return /* @__PURE__ */ jsxs2(
918
+ "div",
919
+ {
920
+ className: `eth-ls-stepper eth-ls-stepper--${orientation}`,
921
+ style: { display: "flex", flexDirection: orientation === "vertical" ? "row" : "column", gap: context.tokens.gap, color: context.tokens.text },
922
+ children: [
923
+ /* @__PURE__ */ jsx3("ol", { className: "eth-ls-stepper__steps", "aria-label": "Steps", children: steps.map((step, index) => {
924
+ const current = index === activeIndex;
925
+ return /* @__PURE__ */ jsx3(
926
+ "li",
927
+ {
928
+ className: `eth-ls-stepper__step eth-ls-stepper__step--${step.status ?? "pending"}${current ? " eth-ls-stepper__step--current" : ""}`,
929
+ "aria-current": current ? "step" : void 0,
930
+ children: /* @__PURE__ */ jsxs2("button", { type: "button", className: "eth-ls-stepper__step-button", onClick: () => setActiveIndex(index), children: [
931
+ /* @__PURE__ */ jsx3("span", { className: "eth-ls-stepper__index", children: index + 1 }),
932
+ /* @__PURE__ */ jsx3("span", { className: "eth-ls-stepper__label", children: step.label })
933
+ ] })
934
+ },
935
+ step.id
936
+ );
937
+ }) }),
938
+ activeStep ? /* @__PURE__ */ jsx3("div", { className: "eth-ls-stepper__content", children: /* @__PURE__ */ jsx3(
939
+ SlotContentRenderer,
940
+ {
941
+ node,
942
+ slot: inlineSlot(`step:${activeStep.id}`, "form"),
943
+ content: activeStep.content,
944
+ parentContext: context
945
+ }
946
+ ) }) : null
947
+ ]
948
+ }
949
+ );
950
+ }
951
+ var StepperLayout = {
952
+ type: "Primitive.Stepper",
953
+ displayName: "Stepper",
954
+ tier: "primitive",
955
+ category: "primitive",
956
+ description: "Flow-oriented step container with validation/navigation semantics.",
957
+ variants: [{ id: "horizontal" }, { id: "vertical" }, { id: "with-preview" }],
958
+ slots: [],
959
+ defaultComposition: {
960
+ op: "stepper",
961
+ orientation: "horizontal",
962
+ stepSlot: "stepper",
963
+ contentSlot: "content",
964
+ steps: []
965
+ },
966
+ defaultStyleIntent: { taskMode: "wizard", visualEmphasis: "balanced" },
967
+ renderer: StepperRenderer
968
+ };
969
+ var primitiveLayouts = [
970
+ PanelLayout,
971
+ SplitPaneLayout,
972
+ TabsLayout,
973
+ StepperLayout
974
+ ];
975
+
976
+ // src/layout-system/regions/index.tsx
977
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
978
+ function ToolbarRenderer({ slots, context }) {
979
+ return /* @__PURE__ */ jsxs3(
980
+ "div",
981
+ {
982
+ className: "eth-ls-toolbar",
983
+ role: "toolbar",
984
+ style: {
985
+ display: "flex",
986
+ alignItems: "center",
987
+ gap: context.tokens.gap,
988
+ background: context.tokens.surfaceMuted,
989
+ borderBottom: `1px solid ${context.tokens.border}`,
990
+ color: context.tokens.text
991
+ },
992
+ children: [
993
+ slots.start ? /* @__PURE__ */ jsx4("div", { className: "eth-ls-toolbar__start", children: slots.start }) : null,
994
+ slots.center ? /* @__PURE__ */ jsx4("div", { className: "eth-ls-toolbar__center", style: { flex: 1, minWidth: 0 }, children: slots.center }) : /* @__PURE__ */ jsx4("div", { style: { flex: 1 } }),
995
+ slots.end ? /* @__PURE__ */ jsx4("div", { className: "eth-ls-toolbar__end", children: slots.end }) : null
996
+ ]
997
+ }
998
+ );
999
+ }
1000
+ var ToolbarLayout = {
1001
+ type: "Region.Toolbar",
1002
+ displayName: "Toolbar",
1003
+ tier: "region",
1004
+ category: "region",
1005
+ description: "Horizontal toolbar with start/center/end groups and roving focus.",
1006
+ slots: [
1007
+ { name: "start", role: "toolbar", accepts: [{ kind: "component" }, { kind: "fragment" }] },
1008
+ { name: "center", role: "toolbar", accepts: [{ kind: "component" }, { kind: "fragment" }] },
1009
+ { name: "end", role: "toolbar", accepts: [{ kind: "component" }, { kind: "fragment" }] }
1010
+ ],
1011
+ defaultStyleIntent: { surface: "region" },
1012
+ renderer: ToolbarRenderer
1013
+ };
1014
+ function InspectorRenderer({ slots, context }) {
1015
+ return /* @__PURE__ */ jsxs3(
1016
+ "aside",
1017
+ {
1018
+ className: "eth-ls-inspector",
1019
+ "aria-label": "Inspector",
1020
+ style: {
1021
+ display: "flex",
1022
+ flexDirection: "column",
1023
+ minHeight: 0,
1024
+ background: context.tokens.surface,
1025
+ borderInlineStart: `1px solid ${context.tokens.border}`,
1026
+ color: context.tokens.text
1027
+ },
1028
+ children: [
1029
+ slots.header ? /* @__PURE__ */ jsx4("header", { className: "eth-ls-inspector__header", children: slots.header }) : null,
1030
+ /* @__PURE__ */ jsx4("div", { className: "eth-ls-inspector__body", style: { overflow: "auto", flex: 1, minHeight: 0 }, children: slots.body }),
1031
+ slots.footer ? /* @__PURE__ */ jsx4("footer", { className: "eth-ls-inspector__footer", children: slots.footer }) : null
1032
+ ]
1033
+ }
1034
+ );
1035
+ }
1036
+ var InspectorLayout = {
1037
+ type: "Region.Inspector",
1038
+ displayName: "Inspector",
1039
+ tier: "region",
1040
+ category: "region",
1041
+ description: "An inline-end inspector panel with header/body/footer.",
1042
+ slots: [
1043
+ { name: "header", role: "toolbar", accepts: [{ kind: "component" }, { kind: "fragment" }] },
1044
+ {
1045
+ name: "body",
1046
+ role: "inspector",
1047
+ required: true,
1048
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }]
1049
+ },
1050
+ { name: "footer", role: "footer", accepts: [{ kind: "component" }, { kind: "fragment" }] }
1051
+ ],
1052
+ defaultStyleIntent: { surface: "panel", interactionMode: "edit" },
1053
+ renderer: InspectorRenderer
1054
+ };
1055
+ var regionLayouts = [ToolbarLayout, InspectorLayout];
1056
+
1057
+ // src/layout-system/page-layouts/index.tsx
1058
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1059
+ function frameStyle(context) {
1060
+ return {
1061
+ display: "grid",
1062
+ height: "100%",
1063
+ minHeight: 0,
1064
+ background: context.tokens.surfaceMuted,
1065
+ color: context.tokens.text
1066
+ };
1067
+ }
1068
+ function AdminShellRenderer({ slots, context, plan }) {
1069
+ const navVisible = slots.navigation && isVisible(plan, "navigation");
1070
+ const navCollapsed = isCollapsed(plan, "navigation");
1071
+ return /* @__PURE__ */ jsxs4(
1072
+ "div",
1073
+ {
1074
+ className: "eth-ls-admin-shell",
1075
+ style: {
1076
+ ...frameStyle(context),
1077
+ gridTemplateRows: "auto 1fr auto",
1078
+ gridTemplateColumns: navVisible ? `${navCollapsed ? "3.5rem" : "16rem"} 1fr` : "1fr",
1079
+ gridTemplateAreas: navVisible ? `"topbar topbar" "nav content" "footer footer"` : `"topbar" "content" "footer"`
1080
+ },
1081
+ children: [
1082
+ slots.topbar ? /* @__PURE__ */ jsx5("header", { className: "eth-ls-admin-shell__topbar", style: { gridArea: "topbar" }, children: slots.topbar }) : null,
1083
+ navVisible ? /* @__PURE__ */ jsx5("nav", { className: "eth-ls-admin-shell__nav", style: { gridArea: "nav", overflow: "auto", borderInlineEnd: `1px solid ${context.tokens.border}` }, children: slots.navigation }) : null,
1084
+ /* @__PURE__ */ jsx5("main", { className: "eth-ls-admin-shell__content", style: { gridArea: "content", overflow: "auto", minWidth: 0 }, children: slots.content }),
1085
+ slots.footer ? /* @__PURE__ */ jsx5("footer", { className: "eth-ls-admin-shell__footer", style: { gridArea: "footer", borderBlockStart: `1px solid ${context.tokens.border}` }, children: slots.footer }) : null
1086
+ ]
1087
+ }
1088
+ );
1089
+ }
1090
+ var AdminShellLayout = {
1091
+ type: "PageLayout.AdminShell",
1092
+ displayName: "Admin Shell",
1093
+ tier: "page",
1094
+ category: "app-shell",
1095
+ description: "Enterprise CRUD shell: topbar, navigation rail, content, footer.",
1096
+ slots: [
1097
+ { name: "topbar", role: "toolbar", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1098
+ {
1099
+ name: "navigation",
1100
+ role: "navigation",
1101
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1102
+ priority: { value: 40, behaviorOnNarrow: "move-to-drawer" },
1103
+ responsive: { collapseBelow: "laptop" }
1104
+ },
1105
+ {
1106
+ name: "content",
1107
+ role: "content",
1108
+ required: true,
1109
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "template" }, { kind: "fragment" }],
1110
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1111
+ },
1112
+ { name: "footer", role: "footer", accepts: [{ kind: "component" }, { kind: "fragment" }] }
1113
+ ],
1114
+ defaultStyleIntent: { surface: "app", taskMode: "crud" },
1115
+ renderer: AdminShellRenderer
1116
+ };
1117
+ function DataGridInspectorRenderer({ slots, context, plan }) {
1118
+ const inspectorVisible = slots.inspector && isVisible(plan, "inspector");
1119
+ return /* @__PURE__ */ jsxs4(
1120
+ "div",
1121
+ {
1122
+ className: "eth-ls-datagrid",
1123
+ style: {
1124
+ ...frameStyle(context),
1125
+ gridTemplateRows: "auto 1fr",
1126
+ gridTemplateColumns: inspectorVisible ? "1fr minmax(18rem, 24rem)" : "1fr",
1127
+ gridTemplateAreas: inspectorVisible ? `"toolbar toolbar" "grid inspector"` : `"toolbar" "grid"`
1128
+ },
1129
+ children: [
1130
+ slots.toolbar ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-datagrid__toolbar", style: { gridArea: "toolbar" }, children: slots.toolbar }) : null,
1131
+ /* @__PURE__ */ jsx5("div", { className: "eth-ls-datagrid__grid", style: { gridArea: "grid", overflow: "auto", minWidth: 0 }, children: slots.grid }),
1132
+ inspectorVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-datagrid__inspector", style: { gridArea: "inspector", overflow: "auto", borderInlineStart: `1px solid ${context.tokens.border}` }, children: slots.inspector }) : null
1133
+ ]
1134
+ }
1135
+ );
1136
+ }
1137
+ var DataGridInspectorLayout = {
1138
+ type: "PageLayout.DataGridInspector",
1139
+ displayName: "Data Grid + Inspector",
1140
+ tier: "page",
1141
+ category: "page",
1142
+ description: "High-density grid with a toolbar and an inline-end inspector.",
1143
+ slots: [
1144
+ { name: "toolbar", role: "toolbar", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1145
+ {
1146
+ name: "grid",
1147
+ role: "content",
1148
+ required: true,
1149
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1150
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1151
+ },
1152
+ {
1153
+ name: "inspector",
1154
+ role: "inspector",
1155
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1156
+ priority: { value: 50, behaviorOnNarrow: "move-to-drawer" }
1157
+ }
1158
+ ],
1159
+ defaultStyleIntent: { surface: "page", taskMode: "crud", dataIntensity: "high" },
1160
+ renderer: DataGridInspectorRenderer
1161
+ };
1162
+ function WorkbenchRenderer({ slots, context, plan }) {
1163
+ const explorerVisible = slots.explorer && isVisible(plan, "explorer");
1164
+ const inspectorVisible = slots.inspector && isVisible(plan, "inspector");
1165
+ const consoleVisible = slots.console && isVisible(plan, "console");
1166
+ const cols = [explorerVisible ? "16rem" : null, "1fr", inspectorVisible ? "minmax(18rem, 22rem)" : null].filter(Boolean).join(" ");
1167
+ const areaMid = [explorerVisible ? "explorer" : null, "primary", inspectorVisible ? "inspector" : null].filter(Boolean).join(" ");
1168
+ return /* @__PURE__ */ jsxs4(
1169
+ "div",
1170
+ {
1171
+ className: "eth-ls-workbench",
1172
+ style: {
1173
+ ...frameStyle(context),
1174
+ gridTemplateColumns: cols,
1175
+ gridTemplateRows: `auto 1fr ${consoleVisible ? "minmax(8rem, 16rem)" : "0"}`,
1176
+ gridTemplateAreas: `"${new Array(cols.split(" ").length).fill("toolbar").join(" ")}" "${areaMid}" "${new Array(cols.split(" ").length).fill("console").join(" ")}"`
1177
+ },
1178
+ children: [
1179
+ slots.toolbar ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-workbench__toolbar", style: { gridArea: "toolbar" }, children: slots.toolbar }) : null,
1180
+ explorerVisible ? /* @__PURE__ */ jsx5("nav", { className: "eth-ls-workbench__explorer", style: { gridArea: "explorer", overflow: "auto", borderInlineEnd: `1px solid ${context.tokens.border}` }, children: slots.explorer }) : null,
1181
+ /* @__PURE__ */ jsx5("main", { className: "eth-ls-workbench__primary", style: { gridArea: "primary", overflow: "auto", minWidth: 0, minHeight: 0 }, children: slots.primary }),
1182
+ inspectorVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-workbench__inspector", style: { gridArea: "inspector", overflow: "auto", borderInlineStart: `1px solid ${context.tokens.border}` }, children: slots.inspector }) : null,
1183
+ consoleVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-workbench__console", style: { gridArea: "console", overflow: "auto", borderBlockStart: `1px solid ${context.tokens.border}` }, children: slots.console }) : null
1184
+ ]
1185
+ }
1186
+ );
1187
+ }
1188
+ var WorkbenchLayout = {
1189
+ type: "PageLayout.Workbench",
1190
+ displayName: "Workbench",
1191
+ tier: "page",
1192
+ category: "page",
1193
+ description: "Authoring workbench: toolbar, explorer, primary canvas, inspector, console.",
1194
+ variants: [{ id: "canvas-primary" }, { id: "split-primary" }],
1195
+ slots: [
1196
+ { name: "toolbar", role: "toolbar", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1197
+ {
1198
+ name: "explorer",
1199
+ role: "navigation",
1200
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1201
+ priority: { value: 40, behaviorOnNarrow: "move-to-drawer" },
1202
+ responsive: { collapseBelow: "laptop" }
1203
+ },
1204
+ {
1205
+ name: "primary",
1206
+ role: "primary",
1207
+ required: true,
1208
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "template" }, { kind: "fragment" }],
1209
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1210
+ },
1211
+ {
1212
+ name: "inspector",
1213
+ role: "inspector",
1214
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1215
+ priority: { value: 50, behaviorOnNarrow: "move-to-drawer" }
1216
+ },
1217
+ {
1218
+ name: "console",
1219
+ role: "console",
1220
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1221
+ priority: { value: 30, behaviorOnNarrow: "move-to-bottom-sheet" }
1222
+ }
1223
+ ],
1224
+ defaultStyleIntent: { surface: "workspace", taskMode: "authoring", visualEmphasis: "immersive" },
1225
+ renderer: WorkbenchRenderer
1226
+ };
1227
+ function WizardComposerRenderer({ slots, context, plan }) {
1228
+ const previewVisible = slots.preview && isVisible(plan, "preview");
1229
+ return /* @__PURE__ */ jsxs4(
1230
+ "div",
1231
+ {
1232
+ className: "eth-ls-wizard",
1233
+ style: {
1234
+ ...frameStyle(context),
1235
+ gridTemplateColumns: previewVisible ? "1fr minmax(20rem, 28rem)" : "1fr",
1236
+ gridTemplateRows: "auto 1fr auto",
1237
+ gridTemplateAreas: previewVisible ? `"stepper stepper" "content preview" "footer footer"` : `"stepper" "content" "footer"`,
1238
+ background: context.tokens.surface
1239
+ },
1240
+ children: [
1241
+ slots.stepper ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-wizard__stepper", style: { gridArea: "stepper" }, children: slots.stepper }) : null,
1242
+ /* @__PURE__ */ jsx5("main", { className: "eth-ls-wizard__content", style: { gridArea: "content", overflow: "auto", minWidth: 0 }, children: slots.content }),
1243
+ previewVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-wizard__preview", style: { gridArea: "preview", overflow: "auto", borderInlineStart: `1px solid ${context.tokens.border}` }, children: slots.preview }) : null,
1244
+ slots.footerActions ? /* @__PURE__ */ jsx5("footer", { className: "eth-ls-wizard__footer", style: { gridArea: "footer", borderBlockStart: `1px solid ${context.tokens.border}` }, children: slots.footerActions }) : null
1245
+ ]
1246
+ }
1247
+ );
1248
+ }
1249
+ var WizardComposerLayout = {
1250
+ type: "PageLayout.WizardComposer",
1251
+ displayName: "Wizard Composer",
1252
+ tier: "page",
1253
+ category: "page",
1254
+ description: "Guided multi-step creation flow with optional live preview.",
1255
+ slots: [
1256
+ { name: "stepper", role: "stepper", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1257
+ {
1258
+ name: "content",
1259
+ role: "form",
1260
+ required: true,
1261
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "template" }, { kind: "fragment" }],
1262
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1263
+ },
1264
+ {
1265
+ name: "preview",
1266
+ role: "preview",
1267
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1268
+ priority: { value: 40, behaviorOnNarrow: "hide" }
1269
+ },
1270
+ { name: "footerActions", role: "footer", accepts: [{ kind: "component" }, { kind: "fragment" }] }
1271
+ ],
1272
+ defaultStyleIntent: { surface: "page", taskMode: "wizard", visualEmphasis: "balanced" },
1273
+ renderer: WizardComposerRenderer
1274
+ };
1275
+ function MonitoringOpsRenderer({ slots, context, plan }) {
1276
+ const consoleVisible = slots.console && isVisible(plan, "console");
1277
+ return /* @__PURE__ */ jsxs4(
1278
+ "div",
1279
+ {
1280
+ className: "eth-ls-monitoring",
1281
+ style: {
1282
+ ...frameStyle(context),
1283
+ gridTemplateRows: `auto auto 1fr ${consoleVisible ? "minmax(8rem, 14rem)" : "0"}`,
1284
+ gridTemplateAreas: `"toolbar" "metrics" "primary" "console"`
1285
+ },
1286
+ children: [
1287
+ slots.toolbar ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-monitoring__toolbar", style: { gridArea: "toolbar" }, children: slots.toolbar }) : null,
1288
+ slots.metrics ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-monitoring__metrics", style: { gridArea: "metrics" }, children: slots.metrics }) : null,
1289
+ /* @__PURE__ */ jsx5("main", { className: "eth-ls-monitoring__primary", style: { gridArea: "primary", overflow: "auto", minHeight: 0 }, children: slots.primary }),
1290
+ consoleVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-monitoring__console", style: { gridArea: "console", overflow: "auto", borderBlockStart: `1px solid ${context.tokens.border}` }, children: slots.console }) : null
1291
+ ]
1292
+ }
1293
+ );
1294
+ }
1295
+ var MonitoringOpsLayout = {
1296
+ type: "PageLayout.MonitoringOps",
1297
+ displayName: "Monitoring Ops",
1298
+ tier: "page",
1299
+ category: "page",
1300
+ description: "Realtime ops dashboard: toolbar, metric strip, primary view, console.",
1301
+ slots: [
1302
+ { name: "toolbar", role: "toolbar", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1303
+ { name: "metrics", role: "summary", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1304
+ {
1305
+ name: "primary",
1306
+ role: "content",
1307
+ required: true,
1308
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1309
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1310
+ },
1311
+ {
1312
+ name: "console",
1313
+ role: "console",
1314
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1315
+ priority: { value: 30, behaviorOnNarrow: "move-to-bottom-sheet" }
1316
+ }
1317
+ ],
1318
+ defaultStyleIntent: { surface: "page", taskMode: "monitoring", dataIntensity: "realtime" },
1319
+ renderer: MonitoringOpsRenderer
1320
+ };
1321
+ function CanvasInspectorRenderer({ slots, context, plan }) {
1322
+ const inspectorVisible = slots.inspector && isVisible(plan, "inspector");
1323
+ return /* @__PURE__ */ jsxs4(
1324
+ "div",
1325
+ {
1326
+ className: "eth-ls-canvas",
1327
+ style: {
1328
+ ...frameStyle(context),
1329
+ gridTemplateRows: "auto 1fr",
1330
+ gridTemplateColumns: inspectorVisible ? "1fr minmax(18rem, 24rem)" : "1fr",
1331
+ gridTemplateAreas: inspectorVisible ? `"toolbar toolbar" "canvas inspector"` : `"toolbar" "canvas"`
1332
+ },
1333
+ children: [
1334
+ slots.toolbar ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-canvas__toolbar", style: { gridArea: "toolbar" }, children: slots.toolbar }) : null,
1335
+ /* @__PURE__ */ jsx5("div", { className: "eth-ls-canvas__stage", style: { gridArea: "canvas", overflow: "hidden", minWidth: 0, minHeight: 0 }, children: slots.canvas }),
1336
+ inspectorVisible ? /* @__PURE__ */ jsx5("div", { className: "eth-ls-canvas__inspector", style: { gridArea: "inspector", overflow: "auto", borderInlineStart: `1px solid ${context.tokens.border}` }, children: slots.inspector }) : null
1337
+ ]
1338
+ }
1339
+ );
1340
+ }
1341
+ var CanvasInspectorLayout = {
1342
+ type: "PageLayout.CanvasInspector",
1343
+ displayName: "Canvas + Inspector",
1344
+ tier: "page",
1345
+ category: "page",
1346
+ description: "Full-bleed canvas with a toolbar and inline-end inspector.",
1347
+ slots: [
1348
+ { name: "toolbar", role: "toolbar", accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }] },
1349
+ {
1350
+ name: "canvas",
1351
+ role: "canvas",
1352
+ required: true,
1353
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1354
+ priority: { value: 100, behaviorOnNarrow: "keep" }
1355
+ },
1356
+ {
1357
+ name: "inspector",
1358
+ role: "inspector",
1359
+ accepts: [{ kind: "component" }, { kind: "layout" }, { kind: "fragment" }],
1360
+ priority: { value: 50, behaviorOnNarrow: "move-to-drawer" }
1361
+ }
1362
+ ],
1363
+ defaultStyleIntent: { surface: "canvas", taskMode: "authoring", visualEmphasis: "immersive" },
1364
+ renderer: CanvasInspectorRenderer
1365
+ };
1366
+ var pageLayouts = [
1367
+ AdminShellLayout,
1368
+ DataGridInspectorLayout,
1369
+ WorkbenchLayout,
1370
+ WizardComposerLayout,
1371
+ MonitoringOpsLayout,
1372
+ CanvasInspectorLayout
1373
+ ];
1374
+
1375
+ // src/layout-system/registry/builtins.ts
1376
+ var builtinLayouts = [
1377
+ ...primitiveLayouts,
1378
+ ...regionLayouts,
1379
+ ...pageLayouts
1380
+ ];
1381
+ function createDefaultLayoutRegistry() {
1382
+ return new LayoutRegistry().registerAll(builtinLayouts);
1383
+ }
1384
+
1385
+ // src/layout-system/runtime/viewport.ts
1386
+ import * as React4 from "react";
1387
+ var BREAKPOINTS = [
1388
+ { bp: "ultra", minWidth: 1920 },
1389
+ { bp: "wide", minWidth: 1536 },
1390
+ { bp: "desktop", minWidth: 1280 },
1391
+ { bp: "laptop", minWidth: 1024 },
1392
+ { bp: "tablet", minWidth: 768 },
1393
+ { bp: "mobile", minWidth: 0 }
1394
+ ];
1395
+ function breakpointForWidth(width) {
1396
+ for (const { bp, minWidth } of BREAKPOINTS) {
1397
+ if (width >= minWidth) return bp;
1398
+ }
1399
+ return "mobile";
1400
+ }
1401
+ function readViewport() {
1402
+ if (typeof window === "undefined") return defaultViewport;
1403
+ const width = window.innerWidth;
1404
+ const height = window.innerHeight;
1405
+ const pointer = typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches ? "coarse" : "fine";
1406
+ const colorScheme = typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1407
+ return { breakpoint: breakpointForWidth(width), width, height, pointer, colorScheme };
1408
+ }
1409
+ function useViewport(opts = {}) {
1410
+ const { initial = defaultViewport, debounceMs = 120 } = opts;
1411
+ const [viewport, setViewport] = React4.useState(initial);
1412
+ React4.useEffect(() => {
1413
+ if (typeof window === "undefined") return void 0;
1414
+ setViewport(readViewport());
1415
+ let timer;
1416
+ const onResize = () => {
1417
+ if (timer) clearTimeout(timer);
1418
+ timer = setTimeout(() => setViewport(readViewport()), debounceMs);
1419
+ };
1420
+ window.addEventListener("resize", onResize);
1421
+ return () => {
1422
+ if (timer) clearTimeout(timer);
1423
+ window.removeEventListener("resize", onResize);
1424
+ };
1425
+ }, [debounceMs]);
1426
+ return viewport;
1427
+ }
1428
+
1429
+ // src/layout-system/renderer/root.tsx
1430
+ import * as React5 from "react";
1431
+ import { jsx as jsx6 } from "react/jsx-runtime";
1432
+ function LayoutRoot({
1433
+ node,
1434
+ componentResolver,
1435
+ templateResolver,
1436
+ registry,
1437
+ context,
1438
+ stateStore,
1439
+ observeViewport = true,
1440
+ onDiagnostics
1441
+ }) {
1442
+ const resolvedRegistry = React5.useMemo(
1443
+ () => registry ?? createDefaultLayoutRegistry(),
1444
+ [registry]
1445
+ );
1446
+ const observed = useViewport({ initial: context?.viewport });
1447
+ const viewport = observeViewport ? observed : context?.viewport;
1448
+ const rootContext = React5.useMemo(
1449
+ () => createRootContext({ ...context, viewport }),
1450
+ [context, viewport]
1451
+ );
1452
+ const engine = React5.useMemo(
1453
+ () => buildEngine({
1454
+ registry: resolvedRegistry,
1455
+ componentResolver,
1456
+ templateResolver,
1457
+ stateStore,
1458
+ onDiagnostics
1459
+ }),
1460
+ [resolvedRegistry, componentResolver, templateResolver, stateStore, onDiagnostics]
1461
+ );
1462
+ React5.useEffect(() => {
1463
+ if (!onDiagnostics) return;
1464
+ onDiagnostics(validateLayout(node, resolvedRegistry));
1465
+ }, [node, resolvedRegistry, onDiagnostics]);
1466
+ return /* @__PURE__ */ jsx6(LayoutEngineProvider, { value: engine, children: /* @__PURE__ */ jsx6(LayoutContextProvider, { value: rootContext, children: /* @__PURE__ */ jsx6(LayoutRenderer, { node }) }) });
1467
+ }
1468
+
1469
+ // src/layout-system/builders.ts
1470
+ function layout(input) {
1471
+ return { kind: "layout", ...input };
1472
+ }
1473
+ function componentSlot(component, props, styleOverride) {
1474
+ return { kind: "component", component, props, styleOverride };
1475
+ }
1476
+ function layoutSlot(node) {
1477
+ return { kind: "layout", layout: node };
1478
+ }
1479
+ function templateSlot(template, props) {
1480
+ return { kind: "template", template, props };
1481
+ }
1482
+ function fragmentSlot(items, composition) {
1483
+ return { kind: "fragment", items, composition };
1484
+ }
1485
+ function emptySlot(reason) {
1486
+ return { kind: "empty", reason };
1487
+ }
1488
+
1489
+ // src/index.tsx
1490
+ var ethBreakpoints = {
1491
+ sm: "36rem",
1492
+ md: "48rem",
1493
+ lg: "64rem",
1494
+ xl: "80rem"
1495
+ };
1496
+ export {
1497
+ LayoutContextProvider,
1498
+ LayoutEngineProvider,
1499
+ LayoutRegistry,
1500
+ LayoutRenderer,
1501
+ LayoutRoot,
1502
+ SlotContentRenderer,
1503
+ StyleScope2 as StyleScope,
1504
+ breakpointForWidth,
1505
+ breakpointRank,
1506
+ buildEngine,
1507
+ buildStyleInferenceInput,
1508
+ builtinLayouts,
1509
+ componentSlot,
1510
+ composeLayoutContext,
1511
+ createDefaultLayoutRegistry,
1512
+ createMemoryLayoutStateStore,
1513
+ createRootContext,
1514
+ defaultComponentStyle,
1515
+ defaultContextPolicy,
1516
+ defaultStylePreset,
1517
+ defaultViewport,
1518
+ emptySlot,
1519
+ ethBreakpoints,
1520
+ ethDensityModes,
1521
+ ethStylePresetDescriptions,
1522
+ ethStylePresetLabels,
1523
+ ethStylePresets,
1524
+ ethTypeScales,
1525
+ fragmentSlot,
1526
+ hasErrors,
1527
+ inferComponentStyle,
1528
+ inferDensity,
1529
+ inferTypeScale,
1530
+ isNarrow,
1531
+ layout,
1532
+ layoutSlot,
1533
+ pageLayouts,
1534
+ presetTokenMap,
1535
+ primitiveLayouts,
1536
+ regionFor,
1537
+ regionLayouts,
1538
+ resolvePhysicalPlan,
1539
+ resolveTokens,
1540
+ templateSlot,
1541
+ useLayoutContext,
1542
+ useLayoutEngine,
1543
+ useViewport,
1544
+ validateLayout
1545
+ };
1546
+ //# sourceMappingURL=index.js.map