machinalayout 0.1.0 → 0.3.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 (61) hide show
  1. package/README.md +295 -49
  2. package/dist/chunk-2ZQ2RFFI.js +400 -0
  3. package/dist/chunk-33CKBEJH.js +186 -0
  4. package/dist/chunk-BJOQRPPX.js +382 -0
  5. package/dist/chunk-KYWOCAHK.js +205 -0
  6. package/dist/chunk-RJYRJ3LD.js +0 -0
  7. package/dist/chunk-SVWYWI7I.js +59 -0
  8. package/dist/chunk-VREK57S3.js +13 -0
  9. package/dist/chunk-ZVDE7PX4.js +222 -0
  10. package/dist/debugOverlay-pJpj0n5H.d.ts +125 -0
  11. package/dist/deus/index.d.ts +14 -0
  12. package/dist/deus/index.js +26 -0
  13. package/dist/dispatch/index.d.ts +49 -0
  14. package/dist/dispatch/index.js +217 -0
  15. package/dist/handoff/index.d.ts +44 -0
  16. package/dist/handoff/index.js +83 -0
  17. package/dist/index.d.ts +54 -236
  18. package/dist/index.js +753 -583
  19. package/dist/inspect/index.d.ts +8 -0
  20. package/dist/inspect/index.js +97 -0
  21. package/dist/react/index.d.ts +41 -0
  22. package/dist/react/index.js +9 -0
  23. package/dist/react-native/index.d.ts +30 -0
  24. package/dist/react-native/index.js +84 -0
  25. package/dist/screenCatalog-ZjonGiOi.d.ts +46 -0
  26. package/dist/text/index.d.ts +10 -0
  27. package/dist/text/index.js +9 -0
  28. package/dist/text/react/index.d.ts +14 -0
  29. package/dist/text/react/index.js +7 -0
  30. package/dist/text/react-native/index.d.ts +16 -0
  31. package/dist/text/react-native/index.js +155 -0
  32. package/dist/text/vue/index.d.ts +113 -0
  33. package/dist/text/vue/index.js +202 -0
  34. package/dist/types-B90jb3RW.d.ts +184 -0
  35. package/dist/types-C4poVJpR.d.ts +74 -0
  36. package/dist/types-DLYAhNXw.d.ts +32 -0
  37. package/dist/vue/index.d.ts +173 -0
  38. package/dist/vue/index.js +112 -0
  39. package/docs/adapter-packaging-a0-plan.md +352 -0
  40. package/docs/adapters.md +19 -0
  41. package/docs/api-coherence-m8-audit.md +397 -0
  42. package/docs/deusmachina.md +108 -0
  43. package/docs/error-codes.md +95 -0
  44. package/docs/grid-arrange-m5a-contract.md +480 -0
  45. package/docs/grid-arrange.md +51 -0
  46. package/docs/inspection-and-handoff.md +126 -0
  47. package/docs/layout-interpolation.md +52 -0
  48. package/docs/machina-dispatch-d0-contract.md +496 -0
  49. package/docs/machina-dispatch.md +143 -0
  50. package/docs/named-layers.md +40 -0
  51. package/docs/react-adapter.md +63 -58
  52. package/docs/react-native-adapter.md +56 -0
  53. package/docs/react-native-text-renderer.md +50 -0
  54. package/docs/reference-alignment-m7a-contract.md +384 -0
  55. package/docs/reference-alignment.md +44 -0
  56. package/docs/responsive-variants.md +54 -0
  57. package/docs/screen-catalog-and-viewports.md +124 -0
  58. package/docs/stack-geometry-helpers.md +115 -0
  59. package/docs/vue-adapter.md +55 -0
  60. package/docs/vue-text-renderer.md +55 -0
  61. package/package.json +127 -60
package/dist/index.js CHANGED
@@ -1,12 +1,29 @@
1
- // src/errors.ts
2
- var MachinaLayoutError = class extends Error {
3
- code;
4
- constructor(code, message) {
5
- super(message);
6
- this.name = "MachinaLayoutError";
7
- this.code = code;
8
- }
9
- };
1
+ import {
2
+ createViewportMatrix,
3
+ defineMachinaScreens,
4
+ defineMachinaViewports,
5
+ expandScreenViewportTasks,
6
+ getMachinaViewport,
7
+ slugMachinaArtifactName
8
+ } from "./chunk-33CKBEJH.js";
9
+ import {
10
+ MachinaReactView
11
+ } from "./chunk-ZVDE7PX4.js";
12
+ import "./chunk-2ZQ2RFFI.js";
13
+ import "./chunk-RJYRJ3LD.js";
14
+ import {
15
+ MachinaTextView
16
+ } from "./chunk-KYWOCAHK.js";
17
+ import {
18
+ parseMachinaText,
19
+ parseMachinaTextInline
20
+ } from "./chunk-BJOQRPPX.js";
21
+ import {
22
+ toResolvedTree
23
+ } from "./chunk-SVWYWI7I.js";
24
+ import {
25
+ MachinaLayoutError
26
+ } from "./chunk-VREK57S3.js";
10
27
 
11
28
  // src/validation.ts
12
29
  function assertFiniteNumber(value, fieldName) {
@@ -83,7 +100,10 @@ function resolveUiLength(length, axisSize, fieldName = "length") {
83
100
  if (unit === "ui") {
84
101
  return value * axisSize;
85
102
  }
86
- throw new MachinaLayoutError("InvalidLengthUnit", `Invalid UiLength unit for ${fieldName}: ${String(unit)}.`);
103
+ throw new MachinaLayoutError(
104
+ "InvalidLengthUnit",
105
+ `Invalid UiLength unit for ${fieldName}: ${String(unit)}.`
106
+ );
87
107
  }
88
108
 
89
109
  // src/offset.ts
@@ -118,12 +138,18 @@ function compileLayoutRows(rows) {
118
138
  assertFiniteNumber(row.order, `rows[${rowIndex}].order`);
119
139
  }
120
140
  if (row.frame.kind === "root" && row.parent !== void 0) {
121
- throw new MachinaLayoutError("RootFrameNotRoot", `row ${row.id} uses RootFrame but is not a root row.`);
141
+ throw new MachinaLayoutError(
142
+ "RootFrameNotRoot",
143
+ `row ${row.id} uses RootFrame but is not a root row.`
144
+ );
122
145
  }
123
146
  if (row.z !== void 0) {
124
147
  assertFiniteNumber(row.z, `rows[${rowIndex}].z`);
125
148
  if (!Number.isInteger(row.z) || row.z < -5 || row.z > 5) {
126
- throw new MachinaLayoutError("InvalidZ", `rows[${rowIndex}].z must be an integer in range -5..5`);
149
+ throw new MachinaLayoutError(
150
+ "InvalidZ",
151
+ `rows[${rowIndex}].z must be an integer in range -5..5`
152
+ );
127
153
  }
128
154
  }
129
155
  rowById.set(row.id, row);
@@ -135,6 +161,7 @@ function compileLayoutRows(rows) {
135
161
  view: row.view,
136
162
  slot: row.slot,
137
163
  debugLabel: row.debugLabel,
164
+ layer: row.layer,
138
165
  offset: row.offset
139
166
  };
140
167
  if (row.parent === void 0) {
@@ -149,10 +176,16 @@ function compileLayoutRows(rows) {
149
176
  }
150
177
  const rootId = rootCandidates[0];
151
178
  if (nodes[rootId].frame.kind === "fill") {
152
- throw new MachinaLayoutError("FillFrameWithoutArranger", "FillFrame cannot be used as the root frame.");
179
+ throw new MachinaLayoutError(
180
+ "FillFrameWithoutArranger",
181
+ "FillFrame cannot be used as the root frame."
182
+ );
153
183
  }
154
184
  if (nodes[rootId].frame.kind === "fixed") {
155
- throw new MachinaLayoutError("FixedFrameWithoutArranger", "FixedFrame cannot be used as the root frame.");
185
+ throw new MachinaLayoutError(
186
+ "FixedFrameWithoutArranger",
187
+ "FixedFrame cannot be used as the root frame."
188
+ );
156
189
  }
157
190
  const childrenEntries = /* @__PURE__ */ new Map();
158
191
  for (let rowIndex = 0; rowIndex < rows.length; rowIndex += 1) {
@@ -164,7 +197,10 @@ function compileLayoutRows(rows) {
164
197
  throw new MachinaLayoutError("SelfParent", `node ${row.id} cannot parent itself.`);
165
198
  }
166
199
  if (!rowById.has(row.parent) || row.parent.trim().length === 0) {
167
- throw new MachinaLayoutError("UnknownParent", `node ${row.id} references unknown parent: ${row.parent}`);
200
+ throw new MachinaLayoutError(
201
+ "UnknownParent",
202
+ `node ${row.id} references unknown parent: ${row.parent}`
203
+ );
168
204
  }
169
205
  const entry = {
170
206
  childId: row.id,
@@ -233,6 +269,101 @@ function compileLayoutRows(rows) {
233
269
  return { rootId, nodes, children };
234
270
  }
235
271
 
272
+ // src/selectLayoutRowsForRoot.ts
273
+ function validateRootRect(rootRect) {
274
+ assertFiniteNumber(rootRect.x, "rootRect.x");
275
+ assertFiniteNumber(rootRect.y, "rootRect.y");
276
+ assertNonNegativeSize(rootRect.width, "rootRect.width");
277
+ assertNonNegativeSize(rootRect.height, "rootRect.height");
278
+ }
279
+ function validateCondition(condition, rowIndex, variantIndex) {
280
+ if (condition.minWidth !== void 0) {
281
+ assertFiniteNumber(
282
+ condition.minWidth,
283
+ `rows[${rowIndex}].variants[${variantIndex}].when.minWidth`
284
+ );
285
+ }
286
+ if (condition.maxWidth !== void 0) {
287
+ assertFiniteNumber(
288
+ condition.maxWidth,
289
+ `rows[${rowIndex}].variants[${variantIndex}].when.maxWidth`
290
+ );
291
+ }
292
+ if (condition.minHeight !== void 0) {
293
+ assertFiniteNumber(
294
+ condition.minHeight,
295
+ `rows[${rowIndex}].variants[${variantIndex}].when.minHeight`
296
+ );
297
+ }
298
+ if (condition.maxHeight !== void 0) {
299
+ assertFiniteNumber(
300
+ condition.maxHeight,
301
+ `rows[${rowIndex}].variants[${variantIndex}].when.maxHeight`
302
+ );
303
+ }
304
+ if (condition.minWidth !== void 0 && condition.maxWidth !== void 0 && condition.minWidth > condition.maxWidth) {
305
+ throw new MachinaLayoutError(
306
+ "InvalidVariantCondition",
307
+ `rows[${rowIndex}].variants[${variantIndex}].when has minWidth > maxWidth`
308
+ );
309
+ }
310
+ if (condition.minHeight !== void 0 && condition.maxHeight !== void 0 && condition.minHeight > condition.maxHeight) {
311
+ throw new MachinaLayoutError(
312
+ "InvalidVariantCondition",
313
+ `rows[${rowIndex}].variants[${variantIndex}].when has minHeight > maxHeight`
314
+ );
315
+ }
316
+ }
317
+ function conditionMatches(condition, rootRect) {
318
+ if (condition.minWidth !== void 0 && rootRect.width < condition.minWidth) return false;
319
+ if (condition.maxWidth !== void 0 && rootRect.width > condition.maxWidth) return false;
320
+ if (condition.minHeight !== void 0 && rootRect.height < condition.minHeight) return false;
321
+ if (condition.maxHeight !== void 0 && rootRect.height > condition.maxHeight) return false;
322
+ return true;
323
+ }
324
+ function validateVariantZ(variant, rowIndex, variantIndex) {
325
+ if (variant.z === void 0) return;
326
+ assertFiniteNumber(variant.z, `rows[${rowIndex}].variants[${variantIndex}].z`);
327
+ if (!Number.isInteger(variant.z) || variant.z < -5 || variant.z > 5) {
328
+ throw new MachinaLayoutError(
329
+ "InvalidZ",
330
+ `rows[${rowIndex}].variants[${variantIndex}].z must be an integer in range -5..5`
331
+ );
332
+ }
333
+ }
334
+ function selectLayoutRowsForRoot(rows, rootRect) {
335
+ validateRootRect(rootRect);
336
+ return rows.map((row, rowIndex) => {
337
+ const baseRow = { ...row };
338
+ delete baseRow.variants;
339
+ const variants = row.variants;
340
+ if (!variants || variants.length === 0) {
341
+ return baseRow;
342
+ }
343
+ for (let variantIndex = 0; variantIndex < variants.length; variantIndex += 1) {
344
+ const variant = variants[variantIndex];
345
+ validateCondition(variant.when, rowIndex, variantIndex);
346
+ validateVariantZ(variant, rowIndex, variantIndex);
347
+ if (!conditionMatches(variant.when, rootRect)) {
348
+ continue;
349
+ }
350
+ const selected = {
351
+ ...baseRow,
352
+ frame: variant.frame ?? baseRow.frame,
353
+ arrange: variant.arrange ?? baseRow.arrange,
354
+ offset: variant.offset ?? baseRow.offset,
355
+ z: variant.z ?? baseRow.z,
356
+ view: variant.view ?? baseRow.view,
357
+ slot: variant.slot ?? baseRow.slot,
358
+ debugLabel: variant.debugLabel ?? baseRow.debugLabel,
359
+ layer: variant.layer ?? baseRow.layer
360
+ };
361
+ return selected;
362
+ }
363
+ return baseRow;
364
+ });
365
+ }
366
+
236
367
  // src/resolveFrame.ts
237
368
  function validateParentRect(parent) {
238
369
  assertFiniteNumber(parent.x, "parent.x");
@@ -260,11 +391,17 @@ function resolveAnchor(parent, frame) {
260
391
  if (hasHeight) assertNonNegativeSize(explicitHeight, "frame.height");
261
392
  const horizontalCount = Number(hasLeft) + Number(hasRight) + Number(hasWidth);
262
393
  if (horizontalCount !== 2) {
263
- throw new MachinaLayoutError("InvalidAnchorHorizontal", "Anchor frame must specify exactly two horizontal constraints: left, right, width.");
394
+ throw new MachinaLayoutError(
395
+ "InvalidAnchorHorizontal",
396
+ "Anchor frame must specify exactly two horizontal constraints: left, right, width."
397
+ );
264
398
  }
265
399
  const verticalCount = Number(hasTop) + Number(hasBottom) + Number(hasHeight);
266
400
  if (verticalCount !== 2) {
267
- throw new MachinaLayoutError("InvalidAnchorVertical", "Anchor frame must specify exactly two vertical constraints: top, bottom, height.");
401
+ throw new MachinaLayoutError(
402
+ "InvalidAnchorVertical",
403
+ "Anchor frame must specify exactly two vertical constraints: top, bottom, height."
404
+ );
268
405
  }
269
406
  let x;
270
407
  let width;
@@ -306,29 +443,159 @@ function resolveFrame(parent, frame) {
306
443
  assertFiniteNumber(frame.y, "frame.y");
307
444
  assertNonNegativeSize(frame.width, "frame.width");
308
445
  assertNonNegativeSize(frame.height, "frame.height");
309
- return { x: parent.x + frame.x, y: parent.y + frame.y, width: frame.width, height: frame.height };
446
+ return {
447
+ x: parent.x + frame.x,
448
+ y: parent.y + frame.y,
449
+ width: frame.width,
450
+ height: frame.height
451
+ };
310
452
  }
311
453
  case "anchor":
312
454
  return resolveAnchor(parent, frame);
313
455
  case "root":
314
- throw new MachinaLayoutError("RootFrameWithoutRoot", "RootFrame can only be declared on the root row.");
456
+ throw new MachinaLayoutError(
457
+ "RootFrameWithoutRoot",
458
+ "RootFrame can only be declared on the root row."
459
+ );
315
460
  case "fixed": {
316
461
  assertNonNegativeSize(frame.width, "frame.width");
317
462
  assertNonNegativeSize(frame.height, "frame.height");
318
- throw new MachinaLayoutError("FixedFrameWithoutArranger", "Fixed frames require an arranger to determine placement.");
463
+ throw new MachinaLayoutError(
464
+ "FixedFrameWithoutArranger",
465
+ "Fixed frames require an arranger to determine placement."
466
+ );
319
467
  }
320
468
  case "fill":
321
- throw new MachinaLayoutError("FillFrameWithoutArranger", "Fill frames require a stack arranger to determine placement.");
469
+ throw new MachinaLayoutError(
470
+ "FillFrameWithoutArranger",
471
+ "Fill frames require a stack arranger to determine placement."
472
+ );
473
+ case "cell":
474
+ throw new MachinaLayoutError(
475
+ "CellFrameWithoutGrid",
476
+ "Cell frames require a grid arranger to determine placement."
477
+ );
478
+ case "guide":
479
+ throw new MachinaLayoutError(
480
+ "GuideTargetUnresolved",
481
+ "Guide frames require document-level dependency resolution and cannot be resolved directly."
482
+ );
322
483
  }
323
484
  }
324
485
 
325
486
  // src/resolveLayoutDocument.ts
326
- function validateRootRect(rootRect) {
487
+ function validateRootRect2(rootRect) {
327
488
  assertFiniteNumber(rootRect.x, "rootRect.x");
328
489
  assertFiniteNumber(rootRect.y, "rootRect.y");
329
490
  assertNonNegativeSize(rootRect.width, "rootRect.width");
330
491
  assertNonNegativeSize(rootRect.height, "rootRect.height");
331
492
  }
493
+ var H_EDGES = /* @__PURE__ */ new Set(["left", "right", "centerX"]);
494
+ var V_EDGES = /* @__PURE__ */ new Set(["top", "bottom", "centerY"]);
495
+ var ALL_EDGES = /* @__PURE__ */ new Set(["left", "right", "top", "bottom", "centerX", "centerY"]);
496
+ var isEdgeRef = (value) => Boolean(value && typeof value === "object" && "ref" in value && "edge" in value);
497
+ function getRectEdgeValue(rect, edge) {
498
+ switch (edge) {
499
+ case "left":
500
+ return rect.x;
501
+ case "right":
502
+ return rect.x + rect.width;
503
+ case "centerX":
504
+ return rect.x + rect.width / 2;
505
+ case "top":
506
+ return rect.y;
507
+ case "bottom":
508
+ return rect.y + rect.height;
509
+ case "centerY":
510
+ return rect.y + rect.height / 2;
511
+ default:
512
+ throw new MachinaLayoutError("InvalidGuideFrame", `unknown guide edge: ${String(edge)}`);
513
+ }
514
+ }
515
+ function validateGuideFrame(nodeId, frame, document) {
516
+ const hCount = Number(frame.left !== void 0) + Number(frame.right !== void 0) + Number(frame.width !== void 0);
517
+ const vCount = Number(frame.top !== void 0) + Number(frame.bottom !== void 0) + Number(frame.height !== void 0);
518
+ if (hCount !== 2 || vCount !== 2)
519
+ throw new MachinaLayoutError(
520
+ "InvalidGuideFrame",
521
+ `guide frame must provide exactly two constraints per axis: ${nodeId}`
522
+ );
523
+ const hRefs = [frame.left, frame.right].filter(isEdgeRef);
524
+ const vRefs = [frame.top, frame.bottom].filter(isEdgeRef);
525
+ if (hRefs.length > 1 || vRefs.length > 1)
526
+ throw new MachinaLayoutError(
527
+ "GuideTooManyReferencesPerAxis",
528
+ `guide has too many refs on one axis: ${nodeId}`
529
+ );
530
+ for (const ref of [...hRefs, ...vRefs]) {
531
+ if (ref.ref === nodeId)
532
+ throw new MachinaLayoutError(
533
+ "GuideSelfReference",
534
+ `guide cannot reference itself: ${nodeId}`
535
+ );
536
+ if (!document.nodes[ref.ref])
537
+ throw new MachinaLayoutError("GuideTargetNotFound", `guide target not found: ${ref.ref}`);
538
+ if (!ALL_EDGES.has(ref.edge))
539
+ throw new MachinaLayoutError("InvalidGuideFrame", `unknown edge: ${String(ref.edge)}`);
540
+ }
541
+ for (const ref of hRefs)
542
+ if (!H_EDGES.has(ref.edge))
543
+ throw new MachinaLayoutError(
544
+ "GuideInvalidEdgeForAxis",
545
+ `horizontal guide ref must use horizontal edge: ${nodeId}`
546
+ );
547
+ for (const ref of vRefs)
548
+ if (!V_EDGES.has(ref.edge))
549
+ throw new MachinaLayoutError(
550
+ "GuideInvalidEdgeForAxis",
551
+ `vertical guide ref must use vertical edge: ${nodeId}`
552
+ );
553
+ }
554
+ function resolveGuidePosition(parentRect, side, value, resolvedNodes) {
555
+ if (isEdgeRef(value)) {
556
+ const target = resolvedNodes[value.ref];
557
+ if (!target)
558
+ throw new MachinaLayoutError(
559
+ "GuideTargetUnresolved",
560
+ `guide target unresolved: ${value.ref}`
561
+ );
562
+ const axisSize2 = side === "left" || side === "right" ? parentRect.width : parentRect.height;
563
+ const offset = value.offset === void 0 ? 0 : resolveUiLength(value.offset, axisSize2, `frame.${side}.offset`);
564
+ return getRectEdgeValue(target.rect, value.edge) + offset;
565
+ }
566
+ const axisSize = side === "left" || side === "right" ? parentRect.width : parentRect.height;
567
+ const scalar = resolveUiLength(value, axisSize, `frame.${side}`);
568
+ if (side === "left") return parentRect.x + scalar;
569
+ if (side === "right") return parentRect.x + parentRect.width - scalar;
570
+ if (side === "top") return parentRect.y + scalar;
571
+ return parentRect.y + parentRect.height - scalar;
572
+ }
573
+ function resolveGuideFrame(parentRect, frame, resolvedNodes) {
574
+ const hasLeft = frame.left !== void 0;
575
+ const hasRight = frame.right !== void 0;
576
+ const hasWidth = frame.width !== void 0;
577
+ const hasTop = frame.top !== void 0;
578
+ const hasBottom = frame.bottom !== void 0;
579
+ const hasHeight = frame.height !== void 0;
580
+ const left = hasLeft ? resolveGuidePosition(parentRect, "left", frame.left, resolvedNodes) : void 0;
581
+ const right = hasRight ? resolveGuidePosition(parentRect, "right", frame.right, resolvedNodes) : void 0;
582
+ const top = hasTop ? resolveGuidePosition(parentRect, "top", frame.top, resolvedNodes) : void 0;
583
+ const bottom = hasBottom ? resolveGuidePosition(parentRect, "bottom", frame.bottom, resolvedNodes) : void 0;
584
+ const explicitWidth = hasWidth ? resolveUiLength(frame.width, parentRect.width, "frame.width") : void 0;
585
+ const explicitHeight = hasHeight ? resolveUiLength(frame.height, parentRect.height, "frame.height") : void 0;
586
+ if (hasWidth) assertNonNegativeSize(explicitWidth, "frame.width");
587
+ if (hasHeight) assertNonNegativeSize(explicitHeight, "frame.height");
588
+ const x = hasLeft && hasWidth ? left : hasRight && hasWidth ? right - explicitWidth : left;
589
+ const width = hasWidth ? explicitWidth : right - left;
590
+ const y = hasTop && hasHeight ? top : hasBottom && hasHeight ? bottom - explicitHeight : top;
591
+ const height = hasHeight ? explicitHeight : bottom - top;
592
+ if (width < 0 || height < 0)
593
+ throw new MachinaLayoutError(
594
+ "NegativeResolvedSize",
595
+ `Resolved guide frame size must be non-negative. Received width=${width}, height=${height}.`
596
+ );
597
+ return { x, y, width, height };
598
+ }
332
599
  function resolveStackChildRects(parentRect, arrange, childIds, document) {
333
600
  const gap = arrange.gap ?? 0;
334
601
  const justify = arrange.justify ?? "start";
@@ -341,9 +608,11 @@ function resolveStackChildRects(parentRect, arrange, childIds, document) {
341
608
  width: parentRect.width - padding.left - padding.right,
342
609
  height: parentRect.height - padding.top - padding.bottom
343
610
  };
344
- if (content.width < 0 || content.height < 0) {
345
- throw new MachinaLayoutError("StackContentNegative", "stack content size cannot be negative after applying padding");
346
- }
611
+ if (content.width < 0 || content.height < 0)
612
+ throw new MachinaLayoutError(
613
+ "StackContentNegative",
614
+ "stack content size cannot be negative after applying padding"
615
+ );
347
616
  const isHorizontal = arrange.axis === "horizontal";
348
617
  const contentMain = isHorizontal ? content.width : content.height;
349
618
  const contentCross = isHorizontal ? content.height : content.width;
@@ -352,9 +621,11 @@ function resolveStackChildRects(parentRect, arrange, childIds, document) {
352
621
  const fillWeights = [];
353
622
  for (const childId of childIds) {
354
623
  const childNode = document.nodes[childId];
355
- if (!childNode) {
356
- throw new MachinaLayoutError("UnknownParent", `child id ${childId} referenced by arranged parent is missing`);
357
- }
624
+ if (!childNode)
625
+ throw new MachinaLayoutError(
626
+ "UnknownParent",
627
+ `child id ${childId} referenced by arranged parent is missing`
628
+ );
358
629
  if (childNode.frame.kind === "fixed") {
359
630
  assertNonNegativeSize(childNode.frame.width, `${childId}.frame.width`);
360
631
  assertNonNegativeSize(childNode.frame.height, `${childId}.frame.height`);
@@ -363,14 +634,18 @@ function resolveStackChildRects(parentRect, arrange, childIds, document) {
363
634
  fillWeights.push(0);
364
635
  continue;
365
636
  }
366
- if (childNode.frame.kind !== "fill") {
367
- throw new MachinaLayoutError("StackChildMustBeFixed", `stack child must use fixed or fill frame: ${childId}`);
368
- }
637
+ if (childNode.frame.kind !== "fill")
638
+ throw new MachinaLayoutError(
639
+ "StackChildMustBeFixed",
640
+ `stack child must use fixed or fill frame: ${childId}`
641
+ );
369
642
  const weight = childNode.frame.weight ?? 1;
370
643
  assertFiniteNumber(weight, `${childId}.frame.weight`);
371
- if (weight <= 0) {
372
- throw new MachinaLayoutError("InvalidFillWeight", `${childId}.frame.weight must be greater than 0`);
373
- }
644
+ if (weight <= 0)
645
+ throw new MachinaLayoutError(
646
+ "InvalidFillWeight",
647
+ `${childId}.frame.weight must be greater than 0`
648
+ );
374
649
  const cross = childNode.frame.cross ?? "fill";
375
650
  let childCross = contentCross;
376
651
  if (cross !== "fill") {
@@ -381,49 +656,31 @@ function resolveStackChildRects(parentRect, arrange, childIds, document) {
381
656
  childCrossSizes.push(childCross);
382
657
  fillWeights.push(weight);
383
658
  }
384
- const fixedMainTotal = childIds.reduce((sum, _id, i) => sum + (fillWeights[i] === 0 ? childMainSizes[i] : 0), 0);
659
+ const fixedMainTotal = childIds.reduce(
660
+ (sum, _id, i) => sum + (fillWeights[i] === 0 ? childMainSizes[i] : 0),
661
+ 0
662
+ );
385
663
  const totalGap = gap * Math.max(0, childIds.length - 1);
386
664
  const remainingMain = contentMain - fixedMainTotal - totalGap;
387
- if (remainingMain < 0) {
388
- throw new MachinaLayoutError("StackOverflow", "stack main axis overflow");
389
- }
665
+ if (remainingMain < 0) throw new MachinaLayoutError("StackOverflow", "stack main axis overflow");
390
666
  const totalFillWeight = fillWeights.reduce((sum, w) => sum + w, 0);
391
667
  if (totalFillWeight > 0) {
392
- for (let i = 0; i < childMainSizes.length; i += 1) {
393
- if (fillWeights[i] > 0) {
668
+ for (let i = 0; i < childMainSizes.length; i += 1)
669
+ if (fillWeights[i] > 0)
394
670
  childMainSizes[i] = remainingMain * fillWeights[i] / totalFillWeight;
395
- }
396
- }
397
671
  }
398
- for (const childCross of childCrossSizes) {
399
- if (childCross > contentCross) {
672
+ for (const childCross of childCrossSizes)
673
+ if (childCross > contentCross)
400
674
  throw new MachinaLayoutError("StackOverflow", "stack cross axis overflow");
401
- }
402
- }
403
675
  const occupiedMain = childMainSizes.reduce((sum, size) => sum + size, 0) + totalGap;
404
676
  const remainingMainAfterFill = contentMain - occupiedMain;
405
677
  let startOffset = 0;
406
678
  let actualGap = gap;
407
679
  if (totalFillWeight === 0) {
408
- switch (justify) {
409
- case "start":
410
- break;
411
- case "center":
412
- startOffset = remainingMainAfterFill / 2;
413
- break;
414
- case "end":
415
- startOffset = remainingMainAfterFill;
416
- break;
417
- case "space-between":
418
- if (childIds.length <= 1) {
419
- actualGap = 0;
420
- } else {
421
- actualGap = gap + remainingMainAfterFill / (childIds.length - 1);
422
- }
423
- break;
424
- default:
425
- throw new Error(`Unsupported stack justify: ${String(justify)}`);
426
- }
680
+ if (justify === "center") startOffset = remainingMainAfterFill / 2;
681
+ else if (justify === "end") startOffset = remainingMainAfterFill;
682
+ else if (justify === "space-between")
683
+ actualGap = childIds.length <= 1 ? 0 : gap + remainingMainAfterFill / (childIds.length - 1);
427
684
  }
428
685
  const rects = {};
429
686
  let currentMain = startOffset;
@@ -431,134 +688,247 @@ function resolveStackChildRects(parentRect, arrange, childIds, document) {
431
688
  const childMain = childMainSizes[index];
432
689
  const childCross = childCrossSizes[index];
433
690
  let crossOffset = 0;
434
- switch (align) {
435
- case "start":
436
- break;
437
- case "center":
438
- crossOffset = (contentCross - childCross) / 2;
439
- break;
440
- case "end":
441
- crossOffset = contentCross - childCross;
442
- break;
443
- default:
444
- throw new Error(`Unsupported stack align: ${String(align)}`);
445
- }
446
- rects[childId] = isHorizontal ? { x: content.x + currentMain, y: content.y + crossOffset, width: childMain, height: childCross } : { x: content.x + crossOffset, y: content.y + currentMain, width: childCross, height: childMain };
691
+ if (align === "center") crossOffset = (contentCross - childCross) / 2;
692
+ else if (align === "end") crossOffset = contentCross - childCross;
693
+ rects[childId] = isHorizontal ? {
694
+ x: content.x + currentMain,
695
+ y: content.y + crossOffset,
696
+ width: childMain,
697
+ height: childCross
698
+ } : {
699
+ x: content.x + crossOffset,
700
+ y: content.y + currentMain,
701
+ width: childCross,
702
+ height: childMain
703
+ };
447
704
  currentMain += childMain + actualGap;
448
705
  });
449
706
  return rects;
450
707
  }
708
+ function validateGridTrack(track, axis, index) {
709
+ if (track.kind === "fixed") {
710
+ if (!Number.isFinite(track.size) || track.size < 0)
711
+ throw new MachinaLayoutError(
712
+ "InvalidGridTrack",
713
+ `${axis}[${index}].size must be finite and non-negative`
714
+ );
715
+ return;
716
+ }
717
+ if (track.kind === "fill") {
718
+ const weight = track.weight ?? 1;
719
+ if (!Number.isFinite(weight) || weight <= 0)
720
+ throw new MachinaLayoutError(
721
+ "InvalidGridTrack",
722
+ `${axis}[${index}].weight must be finite and greater than 0`
723
+ );
724
+ return;
725
+ }
726
+ throw new MachinaLayoutError("InvalidGridTrack", `${axis}[${index}] has unknown track kind`);
727
+ }
728
+ function resolveGridTracks(contentAxisSize, tracks, gap, axis) {
729
+ if (!Number.isFinite(gap) || gap < 0 || tracks.length === 0)
730
+ throw new MachinaLayoutError("InvalidGridTrack", `invalid ${axis} configuration`);
731
+ tracks.forEach((t, i) => {
732
+ validateGridTrack(t, axis, i);
733
+ });
734
+ const fixedTotal = tracks.reduce((s, t) => s + (t.kind === "fixed" ? t.size : 0), 0);
735
+ const gapTotal = gap * Math.max(0, tracks.length - 1);
736
+ const remaining = contentAxisSize - fixedTotal - gapTotal;
737
+ if (remaining < 0) throw new MachinaLayoutError("GridOverflow", `grid ${axis} overflow`);
738
+ const totalWeight = tracks.reduce((s, t) => s + (t.kind === "fill" ? t.weight ?? 1 : 0), 0);
739
+ const sizes = tracks.map(
740
+ (t) => t.kind === "fixed" ? t.size : totalWeight <= 0 ? 0 : remaining * (t.weight ?? 1) / totalWeight
741
+ );
742
+ let current = 0;
743
+ return sizes.map((size) => {
744
+ const r = { start: current, size };
745
+ current += size + gap;
746
+ return r;
747
+ });
748
+ }
749
+ function resolveGridChildRect(childNode, columns, rows, columnGap, rowGap, content) {
750
+ if (childNode.frame.kind !== "cell")
751
+ throw new MachinaLayoutError(
752
+ "GridChildMustBeCell",
753
+ `grid child must use cell frame: ${childNode.id}`
754
+ );
755
+ const { row, col } = childNode.frame;
756
+ const rowSpan = childNode.frame.rowSpan ?? 1;
757
+ const colSpan = childNode.frame.colSpan ?? 1;
758
+ if (!Number.isInteger(row) || row < 0 || !Number.isInteger(col) || col < 0 || !Number.isInteger(rowSpan) || rowSpan <= 0 || !Number.isInteger(colSpan) || colSpan <= 0)
759
+ throw new MachinaLayoutError(
760
+ "InvalidGridCell",
761
+ `invalid cell coordinates/spans for node ${childNode.id}`
762
+ );
763
+ if (row + rowSpan > rows.length || col + colSpan > columns.length)
764
+ throw new MachinaLayoutError(
765
+ "InvalidGridCell",
766
+ `cell exceeds grid bounds for node ${childNode.id}`
767
+ );
768
+ const x = content.x + columns[col].start;
769
+ const y = content.y + rows[row].start;
770
+ let width = columnGap * (colSpan - 1);
771
+ for (let i = col; i < col + colSpan; i += 1) width += columns[i].size;
772
+ let height = rowGap * (rowSpan - 1);
773
+ for (let i = row; i < row + rowSpan; i += 1) height += rows[i].size;
774
+ return { x, y, width, height };
775
+ }
451
776
  function resolveLayoutDocument(document, rootRect) {
452
- validateRootRect(rootRect);
777
+ validateRootRect2(rootRect);
453
778
  const rootNode = document.nodes[document.rootId];
454
- if (!rootNode) {
779
+ if (!rootNode)
455
780
  throw new MachinaLayoutError("MissingRoot", `root node not found for id: ${document.rootId}`);
456
- }
457
781
  const resolvedNodes = {};
458
782
  const resolvedChildren = {};
459
783
  const visitState = /* @__PURE__ */ new Map();
460
- let visitedCount = 0;
784
+ const pendingGuides = /* @__PURE__ */ new Map();
461
785
  const resolveNode = (nodeId, rect) => {
462
786
  const state = visitState.get(nodeId) ?? 0;
463
- if (state === 1) {
464
- throw new MachinaLayoutError("Cycle", `cycle detected at node ${nodeId}`);
465
- }
466
- if (state === 2) {
467
- return;
468
- }
787
+ if (state === 1) throw new MachinaLayoutError("Cycle", `cycle detected at node ${nodeId}`);
788
+ if (state === 2) return;
469
789
  const node = document.nodes[nodeId];
470
- if (!node) {
471
- throw new MachinaLayoutError("UnknownParent", `node referenced in children but missing from nodes: ${nodeId}`);
472
- }
790
+ if (!node)
791
+ throw new MachinaLayoutError(
792
+ "UnknownParent",
793
+ `node referenced in children but missing from nodes: ${nodeId}`
794
+ );
473
795
  visitState.set(nodeId, 1);
474
- visitedCount += 1;
475
796
  resolvedNodes[nodeId] = {
476
797
  id: node.id,
477
798
  z: node.z,
478
- rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
799
+ rect: { ...rect },
479
800
  frame: node.frame,
480
801
  arrange: node.arrange,
481
802
  view: node.view,
482
803
  slot: node.slot,
483
804
  debugLabel: node.debugLabel,
805
+ layer: node.layer,
484
806
  offset: node.offset
485
807
  };
486
808
  const childIds = document.children[nodeId] ?? [];
487
809
  resolvedChildren[nodeId] = [...childIds];
488
- const childRects = node.arrange?.kind === "stack" ? resolveStackChildRects(rect, node.arrange, childIds, document) : void 0;
810
+ let childRects;
811
+ if (node.arrange?.kind === "stack")
812
+ childRects = resolveStackChildRects(rect, node.arrange, childIds, document);
813
+ else if (node.arrange?.kind === "grid") {
814
+ const columnGap = node.arrange.columnGap ?? 0;
815
+ const rowGap = node.arrange.rowGap ?? 0;
816
+ const padding = normalizePadding(node.arrange.padding);
817
+ const content = {
818
+ x: rect.x + padding.left,
819
+ y: rect.y + padding.top,
820
+ width: rect.width - padding.left - padding.right,
821
+ height: rect.height - padding.top - padding.bottom
822
+ };
823
+ if (content.width < 0 || content.height < 0)
824
+ throw new MachinaLayoutError(
825
+ "GridContentNegative",
826
+ "grid content size cannot be negative after applying padding"
827
+ );
828
+ const columns = resolveGridTracks(content.width, node.arrange.columns, columnGap, "columns");
829
+ const rows = resolveGridTracks(content.height, node.arrange.rows, rowGap, "rows");
830
+ childRects = {};
831
+ for (const childId of childIds) {
832
+ const childNode = document.nodes[childId];
833
+ if (!childNode)
834
+ throw new MachinaLayoutError(
835
+ "UnknownParent",
836
+ `child id ${childId} referenced by ${nodeId} is missing`
837
+ );
838
+ childRects[childId] = resolveGridChildRect(
839
+ childNode,
840
+ columns,
841
+ rows,
842
+ columnGap,
843
+ rowGap,
844
+ content
845
+ );
846
+ }
847
+ }
489
848
  for (const childId of childIds) {
490
849
  const childNode = document.nodes[childId];
491
- if (!childNode) {
492
- throw new MachinaLayoutError("UnknownParent", `child id ${childId} referenced by ${nodeId} is missing`);
850
+ if (!childNode)
851
+ throw new MachinaLayoutError(
852
+ "UnknownParent",
853
+ `child id ${childId} referenced by ${nodeId} is missing`
854
+ );
855
+ if (childNode.frame.kind === "guide" && !childRects) {
856
+ validateGuideFrame(childId, childNode.frame, document);
857
+ pendingGuides.set(childId, { nodeId: childId, parentId: nodeId });
858
+ continue;
493
859
  }
494
860
  const normalChildRect = childRects?.[childId] ?? resolveFrame(rect, childNode.frame);
495
- const childRect = applyOffset(normalChildRect, rect, childNode.offset);
496
- resolveNode(childId, childRect);
861
+ resolveNode(childId, applyOffset(normalChildRect, rect, childNode.offset));
497
862
  }
498
863
  visitState.set(nodeId, 2);
499
864
  };
500
- resolveNode(document.rootId, { x: rootRect.x, y: rootRect.y, width: rootRect.width, height: rootRect.height });
501
- if (visitedCount !== Object.keys(document.nodes).length) {
502
- throw new MachinaLayoutError("UnreachableNode", "one or more nodes are unreachable from the root.");
503
- }
504
- return {
505
- rootId: document.rootId,
506
- nodes: resolvedNodes,
507
- children: resolvedChildren
865
+ const processPending = () => {
866
+ while (pendingGuides.size > 0) {
867
+ let progressed = false;
868
+ for (const [id, pending] of [...pendingGuides.entries()]) {
869
+ const parentResolved = resolvedNodes[pending.parentId];
870
+ const node = document.nodes[id];
871
+ if (!parentResolved || !node || node.frame.kind !== "guide") continue;
872
+ const refs = [node.frame.left, node.frame.right, node.frame.top, node.frame.bottom].filter(
873
+ isEdgeRef
874
+ );
875
+ if (refs.some((r) => !resolvedNodes[r.ref])) continue;
876
+ const rect = resolveGuideFrame(parentResolved.rect, node.frame, resolvedNodes);
877
+ resolveNode(id, applyOffset(rect, parentResolved.rect, node.offset));
878
+ pendingGuides.delete(id);
879
+ progressed = true;
880
+ }
881
+ if (pendingGuides.size === 0) return;
882
+ if (progressed) continue;
883
+ const remaining = /* @__PURE__ */ new Set([...pendingGuides.keys()]);
884
+ const visiting = /* @__PURE__ */ new Set();
885
+ const visited = /* @__PURE__ */ new Set();
886
+ const hasCycle = (id) => {
887
+ if (visiting.has(id)) return true;
888
+ if (visited.has(id)) return false;
889
+ visiting.add(id);
890
+ const node = document.nodes[id];
891
+ if (node?.frame.kind === "guide") {
892
+ for (const ref of [
893
+ node.frame.left,
894
+ node.frame.right,
895
+ node.frame.top,
896
+ node.frame.bottom
897
+ ].filter(isEdgeRef)) {
898
+ if (remaining.has(ref.ref) && hasCycle(ref.ref)) return true;
899
+ }
900
+ }
901
+ visiting.delete(id);
902
+ visited.add(id);
903
+ return false;
904
+ };
905
+ for (const id of remaining) {
906
+ if (hasCycle(id))
907
+ throw new MachinaLayoutError("GuideReferenceCycle", "guide reference cycle detected");
908
+ }
909
+ throw new MachinaLayoutError(
910
+ "GuideTargetUnresolved",
911
+ "one or more guide targets could not be resolved"
912
+ );
913
+ }
508
914
  };
915
+ resolveNode(document.rootId, { ...rootRect });
916
+ processPending();
917
+ if (Object.keys(resolvedNodes).length !== Object.keys(document.nodes).length)
918
+ throw new MachinaLayoutError(
919
+ "UnreachableNode",
920
+ "one or more nodes are unreachable from the root."
921
+ );
922
+ return { rootId: document.rootId, nodes: resolvedNodes, children: resolvedChildren };
509
923
  }
510
924
 
511
925
  // src/resolveLayoutRows.ts
512
926
  function resolveLayoutRows(rows, rootRect) {
513
- const document = compileLayoutRows(rows);
927
+ const selectedRows = selectLayoutRowsForRoot(rows, rootRect);
928
+ const document = compileLayoutRows(selectedRows);
514
929
  return resolveLayoutDocument(document, rootRect);
515
930
  }
516
931
 
517
- // src/toResolvedTree.ts
518
- function toResolvedTree(document) {
519
- const root = document.nodes[document.rootId];
520
- if (!root) {
521
- throw new MachinaLayoutError("MissingRoot", `root node '${document.rootId}' is missing`);
522
- }
523
- const visiting = /* @__PURE__ */ new Set();
524
- const visited = /* @__PURE__ */ new Set();
525
- const build = (node) => {
526
- if (visiting.has(node.id)) {
527
- throw new MachinaLayoutError("Cycle", `cycle detected at '${node.id}'`);
528
- }
529
- visiting.add(node.id);
530
- visited.add(node.id);
531
- const childIds = document.children[node.id] ?? [];
532
- const children = childIds.map((childId) => {
533
- const child = document.nodes[childId];
534
- if (!child) {
535
- throw new MachinaLayoutError("UnknownParent", `missing child node '${childId}' referenced by '${node.id}'`);
536
- }
537
- return build(child);
538
- });
539
- visiting.delete(node.id);
540
- return {
541
- id: node.id,
542
- z: node.z,
543
- rect: { ...node.rect },
544
- frame: node.frame,
545
- arrange: node.arrange,
546
- view: node.view,
547
- slot: node.slot,
548
- debugLabel: node.debugLabel,
549
- offset: node.offset,
550
- children
551
- };
552
- };
553
- const tree = build(root);
554
- for (const nodeId of Object.keys(document.nodes)) {
555
- if (!visited.has(nodeId)) {
556
- throw new MachinaLayoutError("UnreachableNode", `node '${nodeId}' is unreachable from root '${document.rootId}'`);
557
- }
558
- }
559
- return tree;
560
- }
561
-
562
932
  // src/flattenResolvedTree.ts
563
933
  function flattenResolvedTree(tree) {
564
934
  const out = [];
@@ -572,6 +942,7 @@ function flattenResolvedTree(tree) {
572
942
  view: node.view,
573
943
  slot: node.slot,
574
944
  debugLabel: node.debugLabel,
945
+ layer: node.layer,
575
946
  offset: node.offset
576
947
  });
577
948
  for (const child of node.children) {
@@ -587,464 +958,248 @@ function formatRect(rect) {
587
958
  return `x=${rect.x} y=${rect.y} w=${rect.width} h=${rect.height}`;
588
959
  }
589
960
 
590
- // src/react/MachinaReactView.tsx
591
- import React from "react";
592
- import { jsx, jsxs } from "react/jsx-runtime";
593
- function renderNode(node, parentRect, views, viewData, nodeData, nodeClassName, debug, nodeContainment, nodeContentVisibility, nodeContainIntrinsicSize, nodesById) {
594
- const viewKey = node.view ?? node.slot;
595
- const View = viewKey ? views[viewKey] : void 0;
596
- const selectedViewData = viewKey ? viewData?.[viewKey] : void 0;
597
- const selectedNodeData = nodeData?.[node.id];
598
- const left = node.rect.x - parentRect.x;
599
- const top = node.rect.y - parentRect.y;
600
- const style = {
601
- position: "absolute",
602
- left,
603
- top,
604
- width: node.rect.width,
605
- height: node.rect.height,
606
- boxSizing: "border-box",
607
- zIndex: node.z ?? 0,
608
- ...nodeContainment === "layout-paint" ? { contain: "layout paint" } : null,
609
- ...nodeContainment === "strict" ? { contain: "strict" } : null,
610
- ...nodeContentVisibility === "auto" ? { contentVisibility: "auto" } : null,
611
- ...nodeContainIntrinsicSize !== void 0 ? { containIntrinsicSize: nodeContainIntrinsicSize } : null,
612
- ...debug ? { outline: "1px dashed rgba(59, 130, 246, 0.9)" } : null
613
- };
614
- const renderedSlot = View && nodesById[node.id] ? React.createElement(View, {
615
- id: node.id,
616
- rect: { ...node.rect },
617
- debugLabel: node.debugLabel,
618
- node: { ...nodesById[node.id], rect: { ...nodesById[node.id].rect } },
619
- viewKey,
620
- viewData: selectedViewData,
621
- nodeData: selectedNodeData
622
- }) : null;
623
- return /* @__PURE__ */ jsxs(
624
- "div",
625
- {
626
- "data-testid": `machina-node-${node.id}`,
627
- className: nodeClassName,
628
- style,
629
- "data-machina-node-id": node.id,
630
- "data-machina-slot": node.slot,
631
- "data-machina-view": viewKey,
632
- "data-machina-debug-label": node.debugLabel,
633
- children: [
634
- debug ? /* @__PURE__ */ jsx("small", { children: node.debugLabel ?? node.id }) : null,
635
- renderedSlot,
636
- [...node.children].map((child, index) => ({ child, index })).sort((a, b) => (a.child.z ?? 0) - (b.child.z ?? 0) || a.index - b.index).map(
637
- ({ child }) => renderNode(
638
- child,
639
- node.rect,
640
- views,
641
- viewData,
642
- nodeData,
643
- nodeClassName,
644
- debug,
645
- nodeContainment,
646
- nodeContentVisibility,
647
- nodeContainIntrinsicSize,
648
- nodesById
649
- )
650
- )
651
- ]
652
- },
653
- node.id
654
- );
961
+ // src/stackGeometry.ts
962
+ function copyRect(rect) {
963
+ return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
655
964
  }
656
- function MachinaReactView(props) {
657
- const {
658
- layout,
659
- views = {},
660
- viewData,
661
- nodeData,
662
- className,
663
- style,
664
- nodeClassName,
665
- debug,
666
- nodeContainment = "layout-paint",
667
- nodeContentVisibility = "none",
668
- nodeContainIntrinsicSize
669
- } = props;
670
- const tree = toResolvedTree(layout);
671
- const wrapperStyle = {
672
- position: "relative",
673
- width: tree.rect.width,
674
- height: tree.rect.height,
675
- ...style
965
+ function applyPadding(parentRect, padding) {
966
+ return {
967
+ x: parentRect.x + padding.left,
968
+ y: parentRect.y + padding.top,
969
+ width: parentRect.width - padding.left - padding.right,
970
+ height: parentRect.height - padding.top - padding.bottom
676
971
  };
677
- return /* @__PURE__ */ jsx("div", { className, style: wrapperStyle, "data-machina-root-id": tree.id, children: renderNode(
678
- tree,
679
- tree.rect,
680
- views,
681
- viewData,
682
- nodeData,
683
- nodeClassName,
684
- debug,
685
- nodeContainment,
686
- nodeContentVisibility,
687
- nodeContainIntrinsicSize,
688
- layout.nodes
689
- ) });
690
972
  }
691
-
692
- // src/text/parseMachinaText.ts
693
- function makeDiagnostic(code, message, index, length, line, column) {
694
- return { code, message, index, length, line, column, level: "error" };
695
- }
696
- function toLines(source) {
697
- const lines = [];
698
- let i = 0;
699
- let line = 1;
700
- while (i <= source.length) {
701
- const start = i;
702
- while (i < source.length && source[i] !== "\n" && source[i] !== "\r") i += 1;
703
- const text = source.slice(start, i);
704
- lines.push({ text, index: start, line });
705
- if (i >= source.length) break;
706
- if (source[i] === "\r" && source[i + 1] === "\n") i += 2;
707
- else i += 1;
708
- line += 1;
973
+ function assertContentNonNegative(contentRect, code) {
974
+ if (contentRect.width < 0 || contentRect.height < 0) {
975
+ throw new MachinaLayoutError(
976
+ code,
977
+ `${code === "StackContentNegative" ? "stack" : "grid"} content size cannot be negative after applying padding`
978
+ );
709
979
  }
710
- return lines;
711
980
  }
712
- function parseInline(text, lineIndex, line) {
713
- const diagnostics = [];
714
- const inline = [];
715
- let cursor = 0;
716
- const pushText = (t) => {
717
- if (!t) return;
718
- const prev = inline[inline.length - 1];
719
- if (prev?.kind === "text") prev.text += t;
720
- else inline.push({ kind: "text", text: t });
721
- };
722
- const allowedEscapes = /* @__PURE__ */ new Set(["\\", "*", "`", "[", "]", "(", ")", "-"]);
723
- const consumeEscape = () => {
724
- if (text[cursor] !== "\\") return false;
725
- if (cursor === text.length - 1) {
726
- diagnostics.push(makeDiagnostic("invalid_escape", "Dangling escape sequence.", lineIndex + cursor, 1, line, cursor + 1));
727
- pushText("\\");
728
- cursor += 1;
729
- return true;
730
- }
731
- const escaped = text[cursor + 1];
732
- if (allowedEscapes.has(escaped)) {
733
- pushText(escaped);
734
- cursor += 2;
735
- return true;
736
- }
737
- diagnostics.push(makeDiagnostic("invalid_escape", `Unsupported escape sequence: \\${escaped}`, lineIndex + cursor, 2, line, cursor + 1));
738
- pushText(escaped);
739
- cursor += 2;
740
- return true;
741
- };
742
- while (cursor < text.length) {
743
- if (consumeEscape()) continue;
744
- if (text.startsWith("![", cursor)) {
745
- diagnostics.push(makeDiagnostic("unsupported_syntax", "Images are not supported.", lineIndex + cursor, 2, line, cursor + 1));
746
- pushText("![");
747
- cursor += 2;
748
- continue;
749
- }
750
- if (text[cursor] === "`") {
751
- const close = text.indexOf("`", cursor + 1);
752
- if (close < 0) {
753
- diagnostics.push(makeDiagnostic("unclosed_inline", "Unclosed inline code marker.", lineIndex + cursor, text.length - cursor, line, cursor + 1));
754
- pushText(text.slice(cursor));
755
- break;
756
- }
757
- inline.push({ kind: "code", text: text.slice(cursor + 1, close) });
758
- cursor = close + 1;
759
- continue;
760
- }
761
- if (text.startsWith("**", cursor)) {
762
- const close = text.indexOf("**", cursor + 2);
763
- if (close < 0) {
764
- diagnostics.push(makeDiagnostic("unclosed_inline", "Unclosed strong marker.", lineIndex + cursor, text.length - cursor, line, cursor + 1));
765
- pushText(text.slice(cursor));
766
- break;
767
- }
768
- const children = parseInline(text.slice(cursor + 2, close), lineIndex + cursor + 2, line);
769
- diagnostics.push(...children.diagnostics);
770
- inline.push({ kind: "strong", children: children.inline });
771
- cursor = close + 2;
772
- continue;
773
- }
774
- if (text[cursor] === "*") {
775
- const close = text.indexOf("*", cursor + 1);
776
- if (close < 0) {
777
- diagnostics.push(makeDiagnostic("unclosed_inline", "Unclosed emphasis marker.", lineIndex + cursor, text.length - cursor, line, cursor + 1));
778
- pushText(text.slice(cursor));
779
- break;
780
- }
781
- const children = parseInline(text.slice(cursor + 1, close), lineIndex + cursor + 1, line);
782
- diagnostics.push(...children.diagnostics);
783
- inline.push({ kind: "emphasis", children: children.inline });
784
- cursor = close + 1;
785
- continue;
786
- }
787
- if (text[cursor] === "[") {
788
- const closeBracket = text.indexOf("]", cursor + 1);
789
- if (closeBracket < 0 || text[closeBracket + 1] !== "(") {
790
- diagnostics.push(makeDiagnostic("malformed_link", "Malformed link syntax.", lineIndex + cursor, Math.max(1, text.length - cursor), line, cursor + 1));
791
- pushText("[");
792
- cursor += 1;
793
- continue;
794
- }
795
- const closeParen = text.indexOf(")", closeBracket + 2);
796
- if (closeParen < 0) {
797
- diagnostics.push(makeDiagnostic("malformed_link", "Malformed link syntax.", lineIndex + cursor, text.length - cursor, line, cursor + 1));
798
- pushText(text.slice(cursor));
799
- break;
800
- }
801
- const label = text.slice(cursor + 1, closeBracket);
802
- const href = text.slice(closeBracket + 2, closeParen);
803
- if (label.length === 0) {
804
- diagnostics.push(makeDiagnostic("malformed_link", "Link label cannot be empty.", lineIndex + cursor, closeParen - cursor + 1, line, cursor + 1));
805
- pushText(text.slice(cursor, closeParen + 1));
806
- cursor = closeParen + 1;
807
- continue;
808
- }
809
- const labelInline = parseInline(label, lineIndex + cursor + 1, line);
810
- diagnostics.push(...labelInline.diagnostics);
811
- inline.push({ kind: "link", href, children: labelInline.inline });
812
- cursor = closeParen + 1;
813
- continue;
814
- }
815
- const specials = ["![", "`", "**", "*", "[", "\\"];
816
- let next = text.length;
817
- for (const special of specials) {
818
- const p = text.indexOf(special, cursor);
819
- if (p >= 0 && p < next) next = p;
820
- }
821
- if (next === cursor) {
822
- pushText(text[cursor]);
823
- cursor += 1;
824
- continue;
825
- }
826
- pushText(text.slice(cursor, next));
827
- cursor = next;
828
- }
829
- return { inline, diagnostics };
981
+ function requireNode(layout, nodeId) {
982
+ const node = layout.nodes[nodeId];
983
+ if (!node) throw new MachinaLayoutError("InvalidId", `node id not found: ${nodeId}`);
984
+ return node;
830
985
  }
831
- function classifyForbiddenBlock(line) {
832
- if (/^#{1,6}\s+/.test(line)) return "heading_forbidden";
833
- if (/^\d+\.\s+/.test(line)) return "unsupported_syntax";
834
- if (/^\s*-\s+\[[ xX]\]\s+/.test(line)) return "unsupported_syntax";
835
- if (/^>\s+/.test(line)) return "unsupported_syntax";
836
- if (/^```/.test(line)) return "unsupported_syntax";
837
- if (/^\s*<\/?[a-zA-Z][^>]*>/.test(line)) return "unsupported_syntax";
838
- if (/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(line)) return "unsupported_syntax";
839
- return void 0;
986
+ function requireStackArrange(layout, parentId) {
987
+ const parent = requireNode(layout, parentId);
988
+ if (parent.arrange?.kind !== "stack") {
989
+ throw new MachinaLayoutError(
990
+ "ExpectedStackArrange",
991
+ `expected stack arrange for node: ${parentId}`
992
+ );
993
+ }
994
+ return parent.arrange;
840
995
  }
841
- function parseBulletLine(line) {
842
- if (line.startsWith("\\- ")) return void 0;
843
- if (line.startsWith("- ")) return { depth: 1, text: line.slice(2) };
844
- if (line.startsWith(" - ")) return { depth: 2, text: line.slice(4) };
845
- if (line.startsWith(" - ")) return { depth: 3, text: line.slice(6) };
846
- return void 0;
996
+ function getArrangeContentRect(parentRect, arrange) {
997
+ if (!arrange) return copyRect(parentRect);
998
+ if (arrange.kind === "stack") {
999
+ const contentRect = applyPadding(parentRect, normalizePadding(arrange.padding));
1000
+ assertContentNonNegative(contentRect, "StackContentNegative");
1001
+ return contentRect;
1002
+ }
1003
+ if (arrange.kind === "grid") {
1004
+ const contentRect = applyPadding(parentRect, normalizePadding(arrange.padding));
1005
+ assertContentNonNegative(contentRect, "GridContentNegative");
1006
+ return contentRect;
1007
+ }
1008
+ return copyRect(parentRect);
847
1009
  }
848
- function parseMachinaTextInline(text) {
849
- return parseInline(text, 0, 1);
1010
+ function getStackContentRect(layout, parentId) {
1011
+ const parent = requireNode(layout, parentId);
1012
+ const arrange = requireStackArrange(layout, parentId);
1013
+ return getArrangeContentRect(parent.rect, arrange);
850
1014
  }
851
- function parseMachinaText(source) {
852
- const src = typeof source === "string" ? { kind: "machina-text", text: source } : source;
853
- if (src?.kind !== "plain" && src?.kind !== "machina-text") {
854
- const diagnostic = makeDiagnostic("unsupported_syntax", "Unsupported MachinaText source kind.", 0, 0, 1, 1);
855
- return { ok: false, document: { blocks: [] }, diagnostics: [diagnostic] };
856
- }
857
- if (src.kind === "plain") {
1015
+ function getStackMainAxisMetrics(layout, parentId) {
1016
+ const parent = requireNode(layout, parentId);
1017
+ const arrange = requireStackArrange(layout, parentId);
1018
+ const contentRect = getArrangeContentRect(parent.rect, arrange);
1019
+ const isHorizontal = arrange.axis === "horizontal";
1020
+ const childIds = [...layout.children[parentId] ?? []];
1021
+ const childMetrics = childIds.map((id) => {
1022
+ const child = requireNode(layout, id);
1023
+ const rect = copyRect(child.rect);
1024
+ const mainStart = isHorizontal ? rect.x - contentRect.x : rect.y - contentRect.y;
1025
+ const mainSize = isHorizontal ? rect.width : rect.height;
1026
+ const crossStart = isHorizontal ? rect.y - contentRect.y : rect.x - contentRect.x;
1027
+ const crossSize = isHorizontal ? rect.height : rect.width;
858
1028
  return {
859
- ok: true,
860
- document: { blocks: [{ kind: "paragraph", inline: [{ kind: "text", text: src.text }] }] },
861
- diagnostics: []
1029
+ id,
1030
+ rect,
1031
+ mainStart,
1032
+ mainEnd: mainStart + mainSize,
1033
+ mainSize,
1034
+ crossStart,
1035
+ crossEnd: crossStart + crossSize,
1036
+ crossSize,
1037
+ frameKind: child.frame.kind,
1038
+ z: child.z,
1039
+ layer: child.layer
862
1040
  };
1041
+ });
1042
+ const contentMainSize = isHorizontal ? contentRect.width : contentRect.height;
1043
+ const contentCrossSize = isHorizontal ? contentRect.height : contentRect.width;
1044
+ const totalChildMainSize = childMetrics.reduce((sum, metric) => sum + metric.mainSize, 0);
1045
+ const totalGapSize = (arrange.gap ?? 0) * Math.max(0, childMetrics.length - 1);
1046
+ const usedMainSize = totalChildMainSize + totalGapSize;
1047
+ return {
1048
+ parentId,
1049
+ axis: arrange.axis,
1050
+ parentRect: copyRect(parent.rect),
1051
+ contentRect,
1052
+ padding: normalizePadding(arrange.padding),
1053
+ gap: arrange.gap ?? 0,
1054
+ childIds,
1055
+ childMetrics,
1056
+ contentMainSize,
1057
+ contentCrossSize,
1058
+ totalChildMainSize,
1059
+ totalGapSize,
1060
+ usedMainSize,
1061
+ unusedMainSize: contentMainSize - usedMainSize
1062
+ };
1063
+ }
1064
+ function getStackChildRects(layout, parentId) {
1065
+ requireStackArrange(layout, parentId);
1066
+ const rects = {};
1067
+ for (const childId of layout.children[parentId] ?? []) {
1068
+ rects[childId] = copyRect(requireNode(layout, childId).rect);
863
1069
  }
864
- const blocks = [];
865
- const diagnostics = [];
866
- const lines = toLines(src.text);
867
- let i = 0;
868
- while (i < lines.length) {
869
- const lineInfo = lines[i];
870
- const trimmed = lineInfo.text.trim();
871
- if (trimmed.length === 0) {
872
- i += 1;
873
- continue;
874
- }
875
- const forbiddenCode = classifyForbiddenBlock(lineInfo.text);
876
- if (forbiddenCode) {
877
- const code = forbiddenCode;
878
- diagnostics.push(makeDiagnostic(code, "Unsupported block syntax.", lineInfo.index, lineInfo.text.length || 1, lineInfo.line, 1));
879
- blocks.push({ kind: "paragraph", inline: [{ kind: "text", text: lineInfo.text }] });
880
- i += 1;
881
- continue;
882
- }
883
- const bullet = parseBulletLine(lineInfo.text);
884
- if (bullet) {
885
- const items = [];
886
- let lastTop;
887
- while (i < lines.length) {
888
- const current = lines[i];
889
- if (current.text.trim().length === 0) break;
890
- const currentBullet = parseBulletLine(current.text);
891
- if (!currentBullet) break;
892
- if (/^\s*-\s+\[[ xX]\]\s+/.test(current.text)) {
893
- diagnostics.push(makeDiagnostic("unsupported_syntax", "Task lists are not supported.", current.index, current.text.length || 1, current.line, 1));
894
- }
895
- if (currentBullet.depth > 2) {
896
- diagnostics.push(makeDiagnostic("max_list_depth_exceeded", "Maximum bullet depth is 2.", current.index, current.text.length || 1, current.line, 1));
897
- const parsed3 = parseInline(current.text.trim(), current.index + (current.text.length - current.text.trimStart().length), current.line);
898
- diagnostics.push(...parsed3.diagnostics);
899
- blocks.push({ kind: "paragraph", inline: parsed3.inline.length ? parsed3.inline : [{ kind: "text", text: current.text }] });
900
- i += 1;
901
- continue;
902
- }
903
- const parsed2 = parseInline(currentBullet.text, current.index + (currentBullet.depth === 1 ? 2 : 4), current.line);
904
- diagnostics.push(...parsed2.diagnostics);
905
- const item = { inline: parsed2.inline };
906
- if (currentBullet.depth === 1) {
907
- items.push(item);
908
- lastTop = item;
909
- } else if (lastTop) {
910
- if (!lastTop.children) lastTop.children = [];
911
- lastTop.children.push(item);
912
- } else {
913
- diagnostics.push(makeDiagnostic("unsupported_syntax", "Nested bullet requires a parent bullet.", current.index, current.text.length || 1, current.line, 1));
914
- blocks.push({ kind: "paragraph", inline: [{ kind: "text", text: current.text }] });
915
- }
916
- i += 1;
917
- }
918
- blocks.push({ kind: "bulletList", items });
919
- continue;
920
- }
921
- const paragraphLines = [];
922
- while (i < lines.length && lines[i].text.trim().length > 0 && !parseBulletLine(lines[i].text) && !classifyForbiddenBlock(lines[i].text)) {
923
- paragraphLines.push(lines[i]);
924
- i += 1;
925
- }
926
- const paragraphText = paragraphLines.map((line) => line.text).join("\n");
927
- const first = paragraphLines[0];
928
- const parsed = parseInline(paragraphText, first?.index ?? 0, first?.line ?? 1);
929
- diagnostics.push(...parsed.diagnostics);
930
- blocks.push({ kind: "paragraph", inline: parsed.inline });
1070
+ return rects;
1071
+ }
1072
+ function getRemainingStackRect(layout, options) {
1073
+ const metrics = getStackMainAxisMetrics(layout, options.parentId);
1074
+ const byId = new Map(metrics.childMetrics.map((metric) => [metric.id, metric]));
1075
+ const after = options.afterChildren ?? [];
1076
+ const before = options.beforeChildren ?? [];
1077
+ const start = after.length === 0 ? 0 : Math.max(...after.map((id) => requireStackMetric(byId, id).mainEnd));
1078
+ const end = before.length === 0 ? metrics.contentMainSize : Math.min(...before.map((id) => requireStackMetric(byId, id).mainStart));
1079
+ const size = end - start;
1080
+ if (size < 0) {
1081
+ throw new MachinaLayoutError(
1082
+ "StackQueryInvalidRange",
1083
+ `remaining stack interval is negative for parent: ${options.parentId}`
1084
+ );
931
1085
  }
932
- return { ok: diagnostics.every((d) => d.level !== "error"), document: { blocks }, diagnostics };
1086
+ return metrics.axis === "horizontal" ? {
1087
+ x: metrics.contentRect.x + start,
1088
+ y: metrics.contentRect.y,
1089
+ width: size,
1090
+ height: metrics.contentRect.height
1091
+ } : {
1092
+ x: metrics.contentRect.x,
1093
+ y: metrics.contentRect.y + start,
1094
+ width: metrics.contentRect.width,
1095
+ height: size
1096
+ };
1097
+ }
1098
+ function requireStackMetric(metrics, childId) {
1099
+ const metric = metrics.get(childId);
1100
+ if (!metric) throw new MachinaLayoutError("InvalidId", `stack child id not found: ${childId}`);
1101
+ return metric;
933
1102
  }
934
1103
 
935
- // src/text/react/MachinaTextView.tsx
936
- import React2 from "react";
937
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
938
- var DEFAULT_POLICY = { variant: "body", wrap: "word", overflow: "clip", align: "start", leading: "normal", blockGap: 8, listGap: 2, valign: "top" };
939
- var INLINE_CODE_FONT = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
940
- var VARIANT_STYLE = {
941
- body: { fontSize: "14px", fontWeight: 400, lineHeight: 1.4 },
942
- label: { fontSize: "12px", fontWeight: 500, lineHeight: 1.3 },
943
- caption: { fontSize: "11px", fontWeight: 400, lineHeight: 1.25, opacity: 0.8 },
944
- title: { fontSize: "18px", fontWeight: 700, lineHeight: 1.25 },
945
- mono: { fontSize: "12px", lineHeight: 1.35, fontFamily: INLINE_CODE_FONT }
946
- };
947
- function isMachinaTextDocument(value) {
948
- return typeof value === "object" && value !== null && "blocks" in value;
1104
+ // src/lerp.ts
1105
+ function assertFiniteNumber2(value) {
1106
+ if (!Number.isFinite(value)) {
1107
+ throw new MachinaLayoutError(
1108
+ "NonFiniteNumber",
1109
+ `Expected finite number, got ${String(value)}.`
1110
+ );
1111
+ }
949
1112
  }
950
- function isMachinaTextSpec(value) {
951
- return typeof value === "object" && value !== null && "kind" in value && value.kind === "text";
1113
+ function sameStringArray(a, b) {
1114
+ if (a.length !== b.length) {
1115
+ return false;
1116
+ }
1117
+ for (let index = 0; index < a.length; index += 1) {
1118
+ if (a[index] !== b[index]) {
1119
+ return false;
1120
+ }
1121
+ }
1122
+ return true;
952
1123
  }
953
- function normalizePositive(value, fallback) {
954
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
1124
+ function assertCompatibleResolvedLayouts(a, b) {
1125
+ if (a.rootId !== b.rootId) {
1126
+ throw new MachinaLayoutError(
1127
+ "IncompatibleLayouts",
1128
+ `Layout roots differ: ${a.rootId} !== ${b.rootId}.`
1129
+ );
1130
+ }
1131
+ if (!(a.rootId in a.nodes) || !(b.rootId in b.nodes)) {
1132
+ throw new MachinaLayoutError(
1133
+ "IncompatibleLayouts",
1134
+ `Root id ${a.rootId} must exist in both node maps.`
1135
+ );
1136
+ }
1137
+ const aNodeIds = Object.keys(a.nodes).sort();
1138
+ const bNodeIds = Object.keys(b.nodes).sort();
1139
+ if (!sameStringArray(aNodeIds, bNodeIds)) {
1140
+ throw new MachinaLayoutError(
1141
+ "IncompatibleLayouts",
1142
+ "Resolved layouts must have the same node ids."
1143
+ );
1144
+ }
1145
+ const aParentIds = Object.keys(a.children).sort();
1146
+ const bParentIds = Object.keys(b.children).sort();
1147
+ if (!sameStringArray(aParentIds, bParentIds)) {
1148
+ throw new MachinaLayoutError(
1149
+ "IncompatibleLayouts",
1150
+ "Resolved layouts must have the same parent-child map."
1151
+ );
1152
+ }
1153
+ for (const parentId of aParentIds) {
1154
+ const aChildren = a.children[parentId] ?? [];
1155
+ const bChildren = b.children[parentId] ?? [];
1156
+ if (!sameStringArray(aChildren, bChildren)) {
1157
+ throw new MachinaLayoutError(
1158
+ "IncompatibleLayouts",
1159
+ `Child order differs for parent ${parentId}.`
1160
+ );
1161
+ }
1162
+ }
955
1163
  }
956
- function normalizeNonNegative(value, fallback) {
957
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
1164
+ function copyChildren(children) {
1165
+ const copied = {};
1166
+ for (const [parentId, childIds] of Object.entries(children)) {
1167
+ copied[parentId] = [...childIds];
1168
+ }
1169
+ return copied;
958
1170
  }
959
- function normalizeLeading(value) {
960
- if (value === void 0) return DEFAULT_POLICY.leading;
961
- if (value === "tight" || value === "normal" || value === "loose") return value;
962
- return normalizePositive(value, resolveLineHeight(DEFAULT_POLICY));
1171
+ function lerpNumber(a, b, t) {
1172
+ assertFiniteNumber2(a);
1173
+ assertFiniteNumber2(b);
1174
+ assertFiniteNumber2(t);
1175
+ return a + (b - a) * t;
963
1176
  }
964
- function normalizeSpecPolicy(spec) {
1177
+ function lerpRect(a, b, t) {
965
1178
  return {
966
- variant: spec.variant ?? DEFAULT_POLICY.variant,
967
- wrap: spec.wrap ?? DEFAULT_POLICY.wrap,
968
- overflow: spec.overflow ?? DEFAULT_POLICY.overflow,
969
- align: spec.align ?? DEFAULT_POLICY.align,
970
- leading: normalizeLeading(spec.leading),
971
- blockGap: normalizeNonNegative(spec.blockGap, DEFAULT_POLICY.blockGap),
972
- listGap: normalizeNonNegative(spec.listGap, DEFAULT_POLICY.listGap),
973
- valign: spec.valign ?? DEFAULT_POLICY.valign
1179
+ x: lerpNumber(a.x, b.x, t),
1180
+ y: lerpNumber(a.y, b.y, t),
1181
+ width: lerpNumber(a.width, b.width, t),
1182
+ height: lerpNumber(a.height, b.height, t)
974
1183
  };
975
1184
  }
976
- function normalizeText(text) {
977
- if (isMachinaTextDocument(text)) return { document: text, diagnostics: [], policy: DEFAULT_POLICY };
978
- if (isMachinaTextSpec(text)) {
979
- const result2 = parseMachinaText(text.source);
980
- return { document: result2.document, diagnostics: result2.diagnostics, policy: normalizeSpecPolicy(text) };
1185
+ function lerpResolvedLayouts(a, b, t) {
1186
+ assertFiniteNumber2(t);
1187
+ assertCompatibleResolvedLayouts(a, b);
1188
+ const nodes = {};
1189
+ for (const id of Object.keys(b.nodes)) {
1190
+ const aNode = a.nodes[id];
1191
+ const bNode = b.nodes[id];
1192
+ nodes[id] = {
1193
+ ...bNode,
1194
+ rect: lerpRect(aNode.rect, bNode.rect, t)
1195
+ };
981
1196
  }
982
- const result = parseMachinaText(typeof text === "string" ? { kind: "machina-text", text } : text);
983
- return { document: result.document, diagnostics: result.diagnostics, policy: DEFAULT_POLICY };
984
- }
985
- function resolveLineHeight(policy) {
986
- if (policy.leading === "tight") return 1.15;
987
- if (policy.leading === "loose") return 1.6;
988
- if (typeof policy.leading === "number") return policy.leading;
989
- return VARIANT_STYLE[policy.variant].lineHeight;
990
- }
991
- function policyStyle(policy) {
992
- const wrapStyle = { word: { whiteSpace: "normal", overflowWrap: "anywhere" }, none: { whiteSpace: "nowrap" } };
993
- const overflowStyle = {
994
- clip: { overflow: "hidden" },
995
- ellipsis: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
996
- scroll: { overflow: "auto" }
997
- };
998
- const alignStyle = { start: { textAlign: "left" }, center: { textAlign: "center" }, end: { textAlign: "right" } };
999
- const justifyContent = {
1000
- top: "flex-start",
1001
- center: "center",
1002
- bottom: "flex-end"
1003
- };
1004
1197
  return {
1005
- width: "100%",
1006
- height: "100%",
1007
- boxSizing: "border-box",
1008
- display: "flex",
1009
- flexDirection: "column",
1010
- justifyContent: justifyContent[policy.valign],
1011
- minWidth: 0,
1012
- ...VARIANT_STYLE[policy.variant],
1013
- lineHeight: resolveLineHeight(policy),
1014
- ...wrapStyle[policy.wrap],
1015
- ...overflowStyle[policy.overflow],
1016
- ...alignStyle[policy.align]
1198
+ rootId: b.rootId,
1199
+ nodes,
1200
+ children: copyChildren(b.children)
1017
1201
  };
1018
1202
  }
1019
- function renderInline(inline, key, props) {
1020
- switch (inline.kind) {
1021
- case "text":
1022
- return /* @__PURE__ */ jsx2(React2.Fragment, { children: inline.text }, key);
1023
- case "strong":
1024
- return /* @__PURE__ */ jsx2("strong", { children: inline.children.map((c, i) => renderInline(c, `${key}-s-${i}`, props)) }, key);
1025
- case "emphasis":
1026
- return /* @__PURE__ */ jsx2("em", { children: inline.children.map((c, i) => renderInline(c, `${key}-e-${i}`, props)) }, key);
1027
- case "code":
1028
- return /* @__PURE__ */ jsx2("code", { style: { fontFamily: INLINE_CODE_FONT, backgroundColor: "rgba(127, 127, 127, 0.15)", borderRadius: 3, padding: "0 0.25em" }, children: inline.text }, key);
1029
- case "link": {
1030
- const rel = props.linkTarget === "_blank" ? "noreferrer noopener" : void 0;
1031
- return /* @__PURE__ */ jsx2("a", { href: inline.href, target: props.linkTarget, rel, onClick: (event) => props.onLinkClick?.(inline.href, event), children: inline.children.map((c, i) => renderInline(c, `${key}-l-${i}`, props)) }, key);
1032
- }
1033
- }
1034
- }
1035
- function renderBulletItem(item, path, props, listGap) {
1036
- return /* @__PURE__ */ jsxs2("li", { style: { marginBottom: listGap }, children: [
1037
- item.inline.map((i, idx) => renderInline(i, `${path}-i-${idx}`, props)),
1038
- item.children?.length ? /* @__PURE__ */ jsx2("ul", { style: { margin: "0.25em 0 0 0", paddingLeft: "1.25em" }, children: item.children.map((c, idx) => renderBulletItem(c, `${path}-c-${idx}`, props, listGap)) }) : null
1039
- ] }, path);
1040
- }
1041
- function MachinaTextView(props) {
1042
- const normalized = normalizeText(props.text);
1043
- return /* @__PURE__ */ jsx2("div", { className: props.className, style: { ...policyStyle(normalized.policy), ...props.style }, children: /* @__PURE__ */ jsxs2("div", { style: { minWidth: 0 }, children: [
1044
- normalized.document.blocks.map((block, index) => block.kind === "paragraph" ? /* @__PURE__ */ jsx2("p", { style: { margin: index === normalized.document.blocks.length - 1 ? "0" : `0 0 ${normalized.policy.blockGap}px 0` }, children: block.inline.map((i, idx) => renderInline(i, `b-${index}-${idx}`, props)) }, `b-${index}`) : /* @__PURE__ */ jsx2("ul", { style: { margin: index === normalized.document.blocks.length - 1 ? "0" : `0 0 ${normalized.policy.blockGap}px 0`, paddingLeft: "1.25em" }, children: block.items.map((item, itemIndex) => renderBulletItem(item, `b-${index}-item-${itemIndex}`, props, normalized.policy.listGap)) }, `b-${index}`)),
1045
- props.showDiagnostics && normalized.diagnostics.length > 0 ? /* @__PURE__ */ jsx2("pre", { style: { margin: `${normalized.policy.blockGap}px 0 0 0`, padding: "0.5em", fontSize: "11px", fontFamily: INLINE_CODE_FONT, whiteSpace: "pre-wrap", background: "rgba(127, 127, 127, 0.12)" }, children: normalized.diagnostics.map((d) => `${d.code} (${d.line}:${d.column}) ${d.message}`).join("\n") }) : null
1046
- ] }) });
1047
- }
1048
1203
  export {
1049
1204
  MachinaLayoutError,
1050
1205
  MachinaReactView,
@@ -1055,8 +1210,21 @@ export {
1055
1210
  assertNonNegativePadding,
1056
1211
  assertNonNegativeSize,
1057
1212
  compileLayoutRows,
1213
+ createViewportMatrix,
1214
+ defineMachinaScreens,
1215
+ defineMachinaViewports,
1216
+ expandScreenViewportTasks,
1058
1217
  flattenResolvedTree,
1059
1218
  formatRect,
1219
+ getArrangeContentRect,
1220
+ getMachinaViewport,
1221
+ getRemainingStackRect,
1222
+ getStackChildRects,
1223
+ getStackContentRect,
1224
+ getStackMainAxisMetrics,
1225
+ lerpNumber,
1226
+ lerpRect,
1227
+ lerpResolvedLayouts,
1060
1228
  normalizePadding,
1061
1229
  parseMachinaText,
1062
1230
  parseMachinaTextInline,
@@ -1064,5 +1232,7 @@ export {
1064
1232
  resolveLayoutDocument,
1065
1233
  resolveLayoutRows,
1066
1234
  resolveUiLength,
1235
+ selectLayoutRowsForRoot,
1236
+ slugMachinaArtifactName,
1067
1237
  toResolvedTree
1068
1238
  };