@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.2

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 (121) hide show
  1. package/README.md +299 -299
  2. package/dist/ansi.d.ts +223 -0
  3. package/dist/ansi.js +2 -0
  4. package/dist/{devtools-QpCMm9JH.mjs → devtools-BhYGjb7h.js} +1 -1
  5. package/dist/index-DDVME65c.d.ts +919 -0
  6. package/dist/index.d.ts +1657 -0
  7. package/dist/index.js +4643 -0
  8. package/dist/sgr-CMfEpjSk.d.ts +91 -0
  9. package/dist/truncate-CBiyyZzw.js +2156 -0
  10. package/dist/yoga-5jKhYCJC.js +3465 -0
  11. package/dist/yoga.d.ts +2 -0
  12. package/dist/yoga.js +2 -0
  13. package/package.json +37 -17
  14. package/src/ansi/chalk.ts +179 -0
  15. package/src/ansi/cursor.ts +48 -0
  16. package/src/ansi/east-asian-width.ts +215 -0
  17. package/src/ansi/escapes.ts +128 -0
  18. package/src/ansi/index.ts +27 -0
  19. package/src/ansi/sgr.ts +237 -0
  20. package/src/ansi/slice.ts +43 -0
  21. package/src/ansi/string-width.ts +236 -0
  22. package/src/ansi/strip.ts +33 -0
  23. package/src/ansi/supports-color.ts +213 -0
  24. package/src/ansi/tokenize.ts +453 -0
  25. package/src/ansi/truncate.ts +194 -0
  26. package/src/ansi/widest-line.ts +12 -0
  27. package/src/ansi/wrap.ts +766 -0
  28. package/src/ansi-tokenizer.ts +510 -0
  29. package/src/auto-bind.ts +41 -0
  30. package/src/boxes.ts +100 -0
  31. package/src/code-excerpt.ts +39 -0
  32. package/src/colorize.ts +60 -0
  33. package/src/components/AccessibilityContext.ts +5 -0
  34. package/src/components/AnimationContext.ts +24 -0
  35. package/src/components/App.tsx +781 -0
  36. package/src/components/AppContext.ts +111 -0
  37. package/src/components/BackgroundContext.ts +8 -0
  38. package/src/components/Box.tsx +116 -0
  39. package/src/components/CursorContext.ts +19 -0
  40. package/src/components/ErrorBoundary.tsx +38 -0
  41. package/src/components/ErrorOverview.tsx +133 -0
  42. package/src/components/FocusContext.ts +30 -0
  43. package/src/components/Newline.tsx +15 -0
  44. package/src/components/Spacer.tsx +10 -0
  45. package/src/components/Static.tsx +59 -0
  46. package/src/components/StderrContext.ts +26 -0
  47. package/src/components/StdinContext.ts +49 -0
  48. package/src/components/StdoutContext.ts +28 -0
  49. package/src/components/Text.tsx +144 -0
  50. package/src/components/Transform.tsx +37 -0
  51. package/src/cursor-position.ts +103 -0
  52. package/src/devtools-window-polyfill.ts +73 -0
  53. package/src/devtools.ts +43 -0
  54. package/src/dom.ts +292 -0
  55. package/src/get-max-width.ts +11 -0
  56. package/src/global.d.ts +36 -0
  57. package/src/hooks/use-animation.ts +142 -0
  58. package/src/hooks/use-app.ts +8 -0
  59. package/src/hooks/use-box-metrics.ts +134 -0
  60. package/src/hooks/use-cursor.ts +33 -0
  61. package/src/hooks/use-focus-manager.ts +62 -0
  62. package/src/hooks/use-focus.ts +83 -0
  63. package/src/hooks/use-input.ts +267 -0
  64. package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
  65. package/src/hooks/use-paste.ts +78 -0
  66. package/src/hooks/use-stderr.ts +8 -0
  67. package/src/hooks/use-stdin.ts +10 -0
  68. package/src/hooks/use-stdout.ts +8 -0
  69. package/src/hooks/use-window-size.ts +41 -0
  70. package/src/indent-string.ts +16 -0
  71. package/src/index.ts +44 -0
  72. package/src/ink.tsx +1506 -0
  73. package/src/input-parser.ts +283 -0
  74. package/src/instances.ts +9 -0
  75. package/src/is-in-ci.ts +7 -0
  76. package/src/kitty-keyboard.ts +57 -0
  77. package/src/log-update.ts +370 -0
  78. package/src/measure-element.ts +62 -0
  79. package/src/measure-text.ts +31 -0
  80. package/src/output.ts +308 -0
  81. package/src/parse-keypress.ts +516 -0
  82. package/src/parse-stack-line.ts +139 -0
  83. package/src/patch-console.ts +62 -0
  84. package/src/quick-lru.ts +85 -0
  85. package/src/reconciler.ts +451 -0
  86. package/src/render-background.ts +38 -0
  87. package/src/render-border.ts +134 -0
  88. package/src/render-node-to-output.ts +191 -0
  89. package/src/render-to-string.ts +131 -0
  90. package/src/render.ts +276 -0
  91. package/src/renderer.ts +73 -0
  92. package/src/sanitize-ansi.ts +33 -0
  93. package/src/signal-exit.ts +107 -0
  94. package/src/squash-text-nodes.ts +40 -0
  95. package/src/stream.ts +30 -0
  96. package/src/styles.ts +748 -0
  97. package/src/terminal-size.ts +57 -0
  98. package/src/throttle.ts +73 -0
  99. package/src/types.ts +15 -0
  100. package/src/utils.ts +40 -0
  101. package/src/wrap-text.ts +50 -0
  102. package/src/write-synchronized.ts +9 -0
  103. package/src/yoga/config.ts +57 -0
  104. package/src/yoga/core/absoluteLayout.ts +626 -0
  105. package/src/yoga/core/baseline.ts +66 -0
  106. package/src/yoga/core/cache.ts +136 -0
  107. package/src/yoga/core/calculateLayout.ts +2920 -0
  108. package/src/yoga/core/config.ts +104 -0
  109. package/src/yoga/core/flexLine.ts +177 -0
  110. package/src/yoga/core/helpers.ts +293 -0
  111. package/src/yoga/core/layoutResults.ts +167 -0
  112. package/src/yoga/core/node.ts +611 -0
  113. package/src/yoga/core/numeric.ts +44 -0
  114. package/src/yoga/core/pixelGrid.ts +151 -0
  115. package/src/yoga/core/style.ts +887 -0
  116. package/src/yoga/core/types.ts +224 -0
  117. package/src/yoga/generated/YGEnums.ts +263 -0
  118. package/src/yoga/index.ts +19 -0
  119. package/src/yoga/node.ts +1140 -0
  120. package/dist/index.d.mts +0 -2379
  121. package/dist/index.mjs +0 -10072
@@ -0,0 +1,1140 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Derived from Yoga. See THIRD_PARTY_NOTICES.md.
3
+
4
+ // Public Yoga API on the engine node itself. This keeps the WASM-compatible
5
+ // surface without allocating a separate binding wrapper for every node.
6
+
7
+ import { Config } from "./config.ts";
8
+ import { calculateLayout } from "./core/calculateLayout.ts";
9
+ import { getDefaultConfig } from "./core/config.ts";
10
+ import { PhysicalEdge } from "./core/helpers.ts";
11
+ import { LayoutResults } from "./core/layoutResults.ts";
12
+ import { Node as CoreNode } from "./core/node.ts";
13
+ import { StyleLength, StyleSizeLength } from "./core/types.ts";
14
+ import {
15
+ Align,
16
+ BoxSizing,
17
+ Direction,
18
+ Dimension,
19
+ Display,
20
+ Edge,
21
+ FlexDirection,
22
+ Gutter,
23
+ Justify,
24
+ MeasureMode,
25
+ Overflow,
26
+ PositionType,
27
+ Unit,
28
+ Wrap,
29
+ } from "./generated/YGEnums.ts";
30
+
31
+ type Layout = {
32
+ left: number;
33
+ right: number;
34
+ top: number;
35
+ bottom: number;
36
+ width: number;
37
+ height: number;
38
+ hadOverflow: boolean;
39
+ };
40
+
41
+ type Size = {
42
+ width: number;
43
+ height: number;
44
+ };
45
+
46
+ type Value = {
47
+ unit: Unit;
48
+ value: number;
49
+ };
50
+
51
+ export type DirtiedFunction = (node: Node) => void;
52
+
53
+ export type MeasureFunction = (
54
+ width: number,
55
+ widthMode: MeasureMode,
56
+ height: number,
57
+ heightMode: MeasureMode,
58
+ ) => Size;
59
+
60
+ function styleValue(length: StyleLength | StyleSizeLength): Value {
61
+ return { value: length.value, unit: length.unit };
62
+ }
63
+
64
+ // Mirrors wrapAssembly's polymorphic setter dispatch, including its handling
65
+ // of numeric strings, Value-like objects, and error messages.
66
+ function dispatchSetter(
67
+ self: Node,
68
+ fnName: string,
69
+ pointFn: (...args: never[]) => void,
70
+ args: unknown[],
71
+ ): void {
72
+ const value = args.pop();
73
+ let unit: Unit | undefined;
74
+ let asNumber: number | undefined;
75
+
76
+ if (value === "auto") {
77
+ unit = Unit.Auto;
78
+ asNumber = undefined;
79
+ } else if (value === "max-content") {
80
+ unit = Unit.MaxContent;
81
+ asNumber = undefined;
82
+ } else if (value === "fit-content") {
83
+ unit = Unit.FitContent;
84
+ asNumber = undefined;
85
+ } else if (value === "stretch") {
86
+ unit = Unit.Stretch;
87
+ asNumber = undefined;
88
+ } else if (typeof value === "object" && value !== null) {
89
+ unit = (value as Value).unit;
90
+ asNumber = (value as Value).valueOf() as unknown as number;
91
+ } else {
92
+ unit = typeof value === "string" && value.endsWith("%") ? Unit.Percent : Unit.Point;
93
+ asNumber = parseFloat(value as string);
94
+ if (value !== undefined && !Number.isNaN(value) && Number.isNaN(asNumber)) {
95
+ throw new Error(`Invalid value ${JSON.stringify(value) ?? "undefined"} for ${fnName}`);
96
+ }
97
+ }
98
+
99
+ if (unit === Unit.Point) {
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ return (pointFn as any).call(self, ...args, asNumber);
102
+ }
103
+
104
+ const suffix = {
105
+ [Unit.Percent]: "Percent",
106
+ [Unit.Auto]: "Auto",
107
+ [Unit.MaxContent]: "MaxContent",
108
+ [Unit.FitContent]: "FitContent",
109
+ [Unit.Stretch]: "Stretch",
110
+ }[unit as number];
111
+
112
+ if (suffix === undefined) {
113
+ throw new Error(
114
+ `Failed to execute "${fnName}": Unsupported unit '${JSON.stringify(value) ?? "undefined"}'`,
115
+ );
116
+ }
117
+
118
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
119
+ const method = (self as any)[`${fnName}${suffix}`];
120
+ if (!method) {
121
+ throw new Error(
122
+ `Failed to execute "${fnName}": Unsupported unit '${JSON.stringify(value) ?? "undefined"}'`,
123
+ );
124
+ }
125
+
126
+ if (asNumber !== undefined) {
127
+ return method.call(self, ...args, asNumber);
128
+ } else {
129
+ return method.call(self, ...args);
130
+ }
131
+ }
132
+
133
+ export class Node extends CoreNode {
134
+ private constructor(config?: Config) {
135
+ super(config ? config.core : getDefaultConfig());
136
+ }
137
+
138
+ static create(config?: Config): Node {
139
+ return new Node(config);
140
+ }
141
+
142
+ static createDefault(): Node {
143
+ return new Node();
144
+ }
145
+
146
+ static createWithConfig(config: Config): Node {
147
+ return new Node(config);
148
+ }
149
+
150
+ /**
151
+ @deprecated No-op in the TypeScript port: the garbage collector owns this
152
+ node's lifetime. Kept only for yoga-layout binding compatibility (WASM
153
+ nodes require explicit destruction).
154
+ */
155
+ free(): void {
156
+ // No-op.
157
+ }
158
+
159
+ /**
160
+ @deprecated No-op in the TypeScript port: garbage collection handles
161
+ object graphs and cycles. Kept only for yoga-layout binding compatibility.
162
+ */
163
+ freeRecursive(): void {
164
+ // No-op.
165
+ }
166
+
167
+ // --- Tree hierarchy ---
168
+ override insertChild(child: Node, index: number): void {
169
+ if (child.owner !== null) {
170
+ throw new Error("Child already has a owner, it must be removed first.");
171
+ }
172
+ if (this.hasMeasureFunc()) {
173
+ throw new Error("Cannot add child: Nodes with measure functions cannot have children.");
174
+ }
175
+
176
+ super.insertChild(child, index);
177
+ child.owner = this;
178
+ this.markDirtyAndPropagate();
179
+ }
180
+
181
+ override removeChild(child: Node): boolean {
182
+ let removed = false;
183
+ if (super.getChildCount() > 0) {
184
+ if (super.removeChild(child)) {
185
+ removed = true;
186
+ child.layout = new LayoutResults(); // layout is no longer valid
187
+ child.owner = null;
188
+ // Mark dirty to invalidate cache, but suppress the dirtied callback
189
+ // since the node is being detached from the tree.
190
+ const dirtiedFunc = child.getDirtiedFunc();
191
+ child.setCoreDirtiedFunc(null);
192
+ child.setDirty(true);
193
+ child.setCoreDirtiedFunc(dirtiedFunc);
194
+ this.markDirtyAndPropagate();
195
+ }
196
+ }
197
+
198
+ return removed;
199
+ }
200
+
201
+ override getChildCount(): number {
202
+ return super.getChildCount();
203
+ }
204
+
205
+ override getChild(index: number): Node {
206
+ return super.getChild(index) as Node;
207
+ }
208
+
209
+ getParent(): Node | null {
210
+ return this.owner as Node | null;
211
+ }
212
+
213
+ override setChildren(children: Node[]): void {
214
+ while (super.getChildCount() !== 0) {
215
+ this.removeChild(this.getChild(super.getChildCount() - 1));
216
+ }
217
+ children.forEach((child, index) => this.insertChild(child, index));
218
+ }
219
+
220
+ // --- Lifecycle ---
221
+ override reset(): void {
222
+ super.reset();
223
+ }
224
+
225
+ // --- Style setters ---
226
+ copyStyle(other: Node): void {
227
+ if (!this.style.equals(other.style)) {
228
+ this.style.copyFrom(other.style);
229
+ this.invalidateProcessedDimensions();
230
+ this.markDirtyAndPropagate();
231
+ }
232
+ }
233
+
234
+ private updateStyleLength(
235
+ get: () => StyleLength,
236
+ set: (value: StyleLength) => void,
237
+ value: StyleLength,
238
+ ): void {
239
+ if (!get().equals(value)) {
240
+ set(value);
241
+ this.markDirtyAndPropagate();
242
+ }
243
+ }
244
+
245
+ private updateStyleSize(
246
+ get: () => StyleSizeLength,
247
+ set: (value: StyleSizeLength) => void,
248
+ value: StyleSizeLength,
249
+ ): void {
250
+ if (!get().equals(value)) {
251
+ set(value);
252
+ this.markDirtyAndPropagate();
253
+ }
254
+ }
255
+
256
+ private updateStyleEnum<T extends number>(get: () => T, set: (value: T) => void, value: T): void {
257
+ if (get() !== value) {
258
+ set(value);
259
+ this.markDirtyAndPropagate();
260
+ }
261
+ }
262
+
263
+ private setDimensionValue(dim: Dimension, value: StyleSizeLength): void {
264
+ if (!this.style.dimension(dim).equals(value)) {
265
+ this.style.setDimension(dim, value);
266
+ this.invalidateProcessedDimensions();
267
+ this.markDirtyAndPropagate();
268
+ }
269
+ }
270
+
271
+ private setMinDimensionValue(dim: Dimension, value: StyleSizeLength): void {
272
+ if (!this.style.minDimension(dim).equals(value)) {
273
+ this.style.setMinDimension(dim, value);
274
+ this.invalidateProcessedDimensions();
275
+ this.markDirtyAndPropagate();
276
+ }
277
+ }
278
+
279
+ private setMaxDimensionValue(dim: Dimension, value: StyleSizeLength): void {
280
+ if (!this.style.maxDimension(dim).equals(value)) {
281
+ this.style.setMaxDimension(dim, value);
282
+ this.invalidateProcessedDimensions();
283
+ this.markDirtyAndPropagate();
284
+ }
285
+ }
286
+
287
+ setPositionType(positionType: PositionType): void {
288
+ this.updateStyleEnum(
289
+ () => this.style.positionType(),
290
+ (v) => this.style.setPositionType(v),
291
+ positionType,
292
+ );
293
+ }
294
+
295
+ setPosition(edge: Edge, position: number | `${number}%` | undefined): void {
296
+ if (typeof position === "number" || position === undefined) {
297
+ this.updateStyleLength(
298
+ () => this.style.position(edge),
299
+ (v) => this.style.setPosition(edge, v),
300
+ StyleLength.points(position === undefined ? NaN : position),
301
+ );
302
+ return;
303
+ }
304
+ dispatchSetter(
305
+ this,
306
+ "setPosition",
307
+ (e: Edge, value?: number) =>
308
+ this.updateStyleLength(
309
+ () => this.style.position(e),
310
+ (v) => this.style.setPosition(e, v),
311
+ StyleLength.points(value === undefined ? NaN : value),
312
+ ),
313
+ [edge, position],
314
+ );
315
+ }
316
+
317
+ setPositionPercent(edge: Edge, position: number | undefined): void {
318
+ this.updateStyleLength(
319
+ () => this.style.position(edge),
320
+ (v) => this.style.setPosition(edge, v),
321
+ StyleLength.percent(position === undefined ? NaN : position),
322
+ );
323
+ }
324
+
325
+ setPositionAuto(edge: Edge): void {
326
+ this.updateStyleLength(
327
+ () => this.style.position(edge),
328
+ (v) => this.style.setPosition(edge, v),
329
+ StyleLength.ofAuto(),
330
+ );
331
+ }
332
+
333
+ setAlignContent(alignContent: Align): void {
334
+ this.updateStyleEnum(
335
+ () => this.style.alignContent(),
336
+ (v) => this.style.setAlignContent(v),
337
+ alignContent,
338
+ );
339
+ }
340
+
341
+ setAlignItems(alignItems: Align): void {
342
+ this.updateStyleEnum(
343
+ () => this.style.alignItems(),
344
+ (v) => this.style.setAlignItems(v),
345
+ alignItems,
346
+ );
347
+ }
348
+
349
+ setAlignSelf(alignSelf: Align): void {
350
+ this.updateStyleEnum(
351
+ () => this.style.alignSelf(),
352
+ (v) => this.style.setAlignSelf(v),
353
+ alignSelf,
354
+ );
355
+ }
356
+
357
+ setFlexDirection(flexDirection: FlexDirection): void {
358
+ if (this.style.flexDirection() !== flexDirection) {
359
+ this.style.setFlexDirection(flexDirection);
360
+ this.markDirtyAndPropagate();
361
+ }
362
+ }
363
+
364
+ setFlexWrap(flexWrap: Wrap): void {
365
+ this.updateStyleEnum(
366
+ () => this.style.flexWrap(),
367
+ (v) => this.style.setFlexWrap(v),
368
+ flexWrap,
369
+ );
370
+ }
371
+
372
+ setJustifyContent(justifyContent: Justify): void {
373
+ this.updateStyleEnum(
374
+ () => this.style.justifyContent(),
375
+ (v) => this.style.setJustifyContent(v),
376
+ justifyContent,
377
+ );
378
+ }
379
+
380
+ setDirection(direction: Direction): void {
381
+ this.updateStyleEnum(
382
+ () => this.style.direction(),
383
+ (v) => this.style.setDirection(v),
384
+ direction,
385
+ );
386
+ }
387
+
388
+ setMargin(edge: Edge, margin: number | "auto" | `${number}%` | undefined): void {
389
+ if (typeof margin === "number" || margin === undefined) {
390
+ this.updateStyleLength(
391
+ () => this.style.margin(edge),
392
+ (v) => this.style.setMargin(edge, v),
393
+ StyleLength.points(margin === undefined ? NaN : margin),
394
+ );
395
+ return;
396
+ }
397
+ dispatchSetter(
398
+ this,
399
+ "setMargin",
400
+ (e: Edge, value?: number) =>
401
+ this.updateStyleLength(
402
+ () => this.style.margin(e),
403
+ (v) => this.style.setMargin(e, v),
404
+ StyleLength.points(value === undefined ? NaN : value),
405
+ ),
406
+ [edge, margin],
407
+ );
408
+ }
409
+
410
+ setMarginPercent(edge: Edge, margin: number | undefined): void {
411
+ this.updateStyleLength(
412
+ () => this.style.margin(edge),
413
+ (v) => this.style.setMargin(edge, v),
414
+ StyleLength.percent(margin === undefined ? NaN : margin),
415
+ );
416
+ }
417
+
418
+ setMarginAuto(edge: Edge): void {
419
+ this.updateStyleLength(
420
+ () => this.style.margin(edge),
421
+ (v) => this.style.setMargin(edge, v),
422
+ StyleLength.ofAuto(),
423
+ );
424
+ }
425
+
426
+ setOverflow(overflow: Overflow): void {
427
+ this.updateStyleEnum(
428
+ () => this.style.overflow(),
429
+ (v) => this.style.setOverflow(v),
430
+ overflow,
431
+ );
432
+ }
433
+
434
+ setDisplay(display: Display): void {
435
+ const current = this.style.display();
436
+ if (current !== display) {
437
+ this.style.setDisplay(display);
438
+ this.owner?.syncContentsChildrenCount();
439
+ this.markDirtyAndPropagate();
440
+ }
441
+ }
442
+
443
+ setFlex(flex: number | undefined): void {
444
+ const value = flex === undefined ? NaN : flex;
445
+ const current = this.style.flex();
446
+ if (current !== value && !(Number.isNaN(current) && Number.isNaN(value))) {
447
+ this.style.setFlex(value);
448
+ this.markDirtyAndPropagate();
449
+ }
450
+ }
451
+
452
+ setFlexBasis(
453
+ flexBasis:
454
+ | number
455
+ | "auto"
456
+ | "fit-content"
457
+ | "max-content"
458
+ | "stretch"
459
+ | `${number}%`
460
+ | undefined,
461
+ ): void {
462
+ if (typeof flexBasis === "number" || flexBasis === undefined) {
463
+ this.updateStyleSize(
464
+ () => this.style.flexBasis(),
465
+ (v) => this.style.setFlexBasis(v),
466
+ StyleSizeLength.points(flexBasis === undefined ? NaN : flexBasis),
467
+ );
468
+ return;
469
+ }
470
+ dispatchSetter(
471
+ this,
472
+ "setFlexBasis",
473
+ (value?: number) =>
474
+ this.updateStyleSize(
475
+ () => this.style.flexBasis(),
476
+ (v) => this.style.setFlexBasis(v),
477
+ StyleSizeLength.points(value === undefined ? NaN : value),
478
+ ),
479
+ [flexBasis],
480
+ );
481
+ }
482
+
483
+ setFlexBasisPercent(flexBasis: number | undefined): void {
484
+ this.updateStyleSize(
485
+ () => this.style.flexBasis(),
486
+ (v) => this.style.setFlexBasis(v),
487
+ StyleSizeLength.percent(flexBasis === undefined ? NaN : flexBasis),
488
+ );
489
+ }
490
+
491
+ setFlexBasisAuto(): void {
492
+ this.updateStyleSize(
493
+ () => this.style.flexBasis(),
494
+ (v) => this.style.setFlexBasis(v),
495
+ StyleSizeLength.ofAuto(),
496
+ );
497
+ }
498
+
499
+ setFlexBasisMaxContent(): void {
500
+ this.updateStyleSize(
501
+ () => this.style.flexBasis(),
502
+ (v) => this.style.setFlexBasis(v),
503
+ StyleSizeLength.ofMaxContent(),
504
+ );
505
+ }
506
+
507
+ setFlexBasisFitContent(): void {
508
+ this.updateStyleSize(
509
+ () => this.style.flexBasis(),
510
+ (v) => this.style.setFlexBasis(v),
511
+ StyleSizeLength.ofFitContent(),
512
+ );
513
+ }
514
+
515
+ setFlexBasisStretch(): void {
516
+ this.updateStyleSize(
517
+ () => this.style.flexBasis(),
518
+ (v) => this.style.setFlexBasis(v),
519
+ StyleSizeLength.ofStretch(),
520
+ );
521
+ }
522
+
523
+ setFlexGrow(flexGrow: number | undefined): void {
524
+ const value = flexGrow === undefined ? NaN : flexGrow;
525
+ const current = this.style.flexGrow();
526
+ if (current !== value && !(Number.isNaN(current) && Number.isNaN(value))) {
527
+ this.style.setFlexGrow(value);
528
+ this.markDirtyAndPropagate();
529
+ }
530
+ }
531
+
532
+ setFlexShrink(flexShrink: number | undefined): void {
533
+ const value = flexShrink === undefined ? NaN : flexShrink;
534
+ const current = this.style.flexShrink();
535
+ if (current !== value && !(Number.isNaN(current) && Number.isNaN(value))) {
536
+ this.style.setFlexShrink(value);
537
+ this.markDirtyAndPropagate();
538
+ }
539
+ }
540
+
541
+ setWidth(
542
+ width: number | "auto" | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
543
+ ): void {
544
+ if (typeof width === "number" || width === undefined) {
545
+ this.setDimensionValue(
546
+ Dimension.Width,
547
+ StyleSizeLength.points(width === undefined ? NaN : width),
548
+ );
549
+ return;
550
+ }
551
+ dispatchSetter(
552
+ this,
553
+ "setWidth",
554
+ (value?: number) =>
555
+ this.setDimensionValue(
556
+ Dimension.Width,
557
+ StyleSizeLength.points(value === undefined ? NaN : value),
558
+ ),
559
+ [width],
560
+ );
561
+ }
562
+
563
+ setWidthPercent(width: number | undefined): void {
564
+ this.setDimensionValue(
565
+ Dimension.Width,
566
+ StyleSizeLength.percent(width === undefined ? NaN : width),
567
+ );
568
+ }
569
+
570
+ setWidthAuto(): void {
571
+ this.setDimensionValue(Dimension.Width, StyleSizeLength.ofAuto());
572
+ }
573
+
574
+ setWidthMaxContent(): void {
575
+ this.setDimensionValue(Dimension.Width, StyleSizeLength.ofMaxContent());
576
+ }
577
+
578
+ setWidthFitContent(): void {
579
+ this.setDimensionValue(Dimension.Width, StyleSizeLength.ofFitContent());
580
+ }
581
+
582
+ setWidthStretch(): void {
583
+ this.setDimensionValue(Dimension.Width, StyleSizeLength.ofStretch());
584
+ }
585
+
586
+ setHeight(
587
+ height: number | "auto" | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
588
+ ): void {
589
+ if (typeof height === "number" || height === undefined) {
590
+ this.setDimensionValue(
591
+ Dimension.Height,
592
+ StyleSizeLength.points(height === undefined ? NaN : height),
593
+ );
594
+ return;
595
+ }
596
+ dispatchSetter(
597
+ this,
598
+ "setHeight",
599
+ (value?: number) =>
600
+ this.setDimensionValue(
601
+ Dimension.Height,
602
+ StyleSizeLength.points(value === undefined ? NaN : value),
603
+ ),
604
+ [height],
605
+ );
606
+ }
607
+
608
+ setHeightPercent(height: number | undefined): void {
609
+ this.setDimensionValue(
610
+ Dimension.Height,
611
+ StyleSizeLength.percent(height === undefined ? NaN : height),
612
+ );
613
+ }
614
+
615
+ setHeightAuto(): void {
616
+ this.setDimensionValue(Dimension.Height, StyleSizeLength.ofAuto());
617
+ }
618
+
619
+ setHeightMaxContent(): void {
620
+ this.setDimensionValue(Dimension.Height, StyleSizeLength.ofMaxContent());
621
+ }
622
+
623
+ setHeightFitContent(): void {
624
+ this.setDimensionValue(Dimension.Height, StyleSizeLength.ofFitContent());
625
+ }
626
+
627
+ setHeightStretch(): void {
628
+ this.setDimensionValue(Dimension.Height, StyleSizeLength.ofStretch());
629
+ }
630
+
631
+ setMinWidth(
632
+ minWidth: number | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
633
+ ): void {
634
+ if (typeof minWidth === "number" || minWidth === undefined) {
635
+ this.setMinDimensionValue(
636
+ Dimension.Width,
637
+ StyleSizeLength.points(minWidth === undefined ? NaN : minWidth),
638
+ );
639
+ return;
640
+ }
641
+ dispatchSetter(
642
+ this,
643
+ "setMinWidth",
644
+ (value?: number) =>
645
+ this.setMinDimensionValue(
646
+ Dimension.Width,
647
+ StyleSizeLength.points(value === undefined ? NaN : value),
648
+ ),
649
+ [minWidth],
650
+ );
651
+ }
652
+
653
+ setMinWidthPercent(minWidth: number | undefined): void {
654
+ this.setMinDimensionValue(
655
+ Dimension.Width,
656
+ StyleSizeLength.percent(minWidth === undefined ? NaN : minWidth),
657
+ );
658
+ }
659
+
660
+ setMinWidthMaxContent(): void {
661
+ this.setMinDimensionValue(Dimension.Width, StyleSizeLength.ofMaxContent());
662
+ }
663
+
664
+ setMinWidthFitContent(): void {
665
+ this.setMinDimensionValue(Dimension.Width, StyleSizeLength.ofFitContent());
666
+ }
667
+
668
+ setMinWidthStretch(): void {
669
+ this.setMinDimensionValue(Dimension.Width, StyleSizeLength.ofStretch());
670
+ }
671
+
672
+ setMinHeight(
673
+ minHeight: number | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
674
+ ): void {
675
+ if (typeof minHeight === "number" || minHeight === undefined) {
676
+ this.setMinDimensionValue(
677
+ Dimension.Height,
678
+ StyleSizeLength.points(minHeight === undefined ? NaN : minHeight),
679
+ );
680
+ return;
681
+ }
682
+ dispatchSetter(
683
+ this,
684
+ "setMinHeight",
685
+ (value?: number) =>
686
+ this.setMinDimensionValue(
687
+ Dimension.Height,
688
+ StyleSizeLength.points(value === undefined ? NaN : value),
689
+ ),
690
+ [minHeight],
691
+ );
692
+ }
693
+
694
+ setMinHeightPercent(minHeight: number | undefined): void {
695
+ this.setMinDimensionValue(
696
+ Dimension.Height,
697
+ StyleSizeLength.percent(minHeight === undefined ? NaN : minHeight),
698
+ );
699
+ }
700
+
701
+ setMinHeightMaxContent(): void {
702
+ this.setMinDimensionValue(Dimension.Height, StyleSizeLength.ofMaxContent());
703
+ }
704
+
705
+ setMinHeightFitContent(): void {
706
+ this.setMinDimensionValue(Dimension.Height, StyleSizeLength.ofFitContent());
707
+ }
708
+
709
+ setMinHeightStretch(): void {
710
+ this.setMinDimensionValue(Dimension.Height, StyleSizeLength.ofStretch());
711
+ }
712
+
713
+ setMaxWidth(
714
+ maxWidth: number | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
715
+ ): void {
716
+ if (typeof maxWidth === "number" || maxWidth === undefined) {
717
+ this.setMaxDimensionValue(
718
+ Dimension.Width,
719
+ StyleSizeLength.points(maxWidth === undefined ? NaN : maxWidth),
720
+ );
721
+ return;
722
+ }
723
+ dispatchSetter(
724
+ this,
725
+ "setMaxWidth",
726
+ (value?: number) =>
727
+ this.setMaxDimensionValue(
728
+ Dimension.Width,
729
+ StyleSizeLength.points(value === undefined ? NaN : value),
730
+ ),
731
+ [maxWidth],
732
+ );
733
+ }
734
+
735
+ setMaxWidthPercent(maxWidth: number | undefined): void {
736
+ this.setMaxDimensionValue(
737
+ Dimension.Width,
738
+ StyleSizeLength.percent(maxWidth === undefined ? NaN : maxWidth),
739
+ );
740
+ }
741
+
742
+ setMaxWidthMaxContent(): void {
743
+ this.setMaxDimensionValue(Dimension.Width, StyleSizeLength.ofMaxContent());
744
+ }
745
+
746
+ setMaxWidthFitContent(): void {
747
+ this.setMaxDimensionValue(Dimension.Width, StyleSizeLength.ofFitContent());
748
+ }
749
+
750
+ setMaxWidthStretch(): void {
751
+ this.setMaxDimensionValue(Dimension.Width, StyleSizeLength.ofStretch());
752
+ }
753
+
754
+ setMaxHeight(
755
+ maxHeight: number | "fit-content" | "max-content" | "stretch" | `${number}%` | undefined,
756
+ ): void {
757
+ if (typeof maxHeight === "number" || maxHeight === undefined) {
758
+ this.setMaxDimensionValue(
759
+ Dimension.Height,
760
+ StyleSizeLength.points(maxHeight === undefined ? NaN : maxHeight),
761
+ );
762
+ return;
763
+ }
764
+ dispatchSetter(
765
+ this,
766
+ "setMaxHeight",
767
+ (value?: number) =>
768
+ this.setMaxDimensionValue(
769
+ Dimension.Height,
770
+ StyleSizeLength.points(value === undefined ? NaN : value),
771
+ ),
772
+ [maxHeight],
773
+ );
774
+ }
775
+
776
+ setMaxHeightPercent(maxHeight: number | undefined): void {
777
+ this.setMaxDimensionValue(
778
+ Dimension.Height,
779
+ StyleSizeLength.percent(maxHeight === undefined ? NaN : maxHeight),
780
+ );
781
+ }
782
+
783
+ setMaxHeightMaxContent(): void {
784
+ this.setMaxDimensionValue(Dimension.Height, StyleSizeLength.ofMaxContent());
785
+ }
786
+
787
+ setMaxHeightFitContent(): void {
788
+ this.setMaxDimensionValue(Dimension.Height, StyleSizeLength.ofFitContent());
789
+ }
790
+
791
+ setMaxHeightStretch(): void {
792
+ this.setMaxDimensionValue(Dimension.Height, StyleSizeLength.ofStretch());
793
+ }
794
+
795
+ setAspectRatio(aspectRatio: number | undefined): void {
796
+ const value = aspectRatio === undefined ? NaN : aspectRatio;
797
+ const current = this.style.aspectRatio();
798
+ // setAspectRatio normalizes degenerate ratios; compare post-normalization.
799
+ const normalized = value === 0 || value === Infinity || value === -Infinity ? NaN : value;
800
+ if (current !== normalized && !(Number.isNaN(current) && Number.isNaN(normalized))) {
801
+ this.style.setAspectRatio(value);
802
+ this.markDirtyAndPropagate();
803
+ }
804
+ }
805
+
806
+ setBorder(edge: Edge, borderWidth: number | undefined): void {
807
+ this.updateStyleLength(
808
+ () => this.style.border(edge),
809
+ (v) => this.style.setBorder(edge, v),
810
+ StyleLength.points(borderWidth === undefined ? NaN : borderWidth),
811
+ );
812
+ }
813
+
814
+ setPadding(edge: Edge, padding: number | `${number}%` | undefined): void {
815
+ if (typeof padding === "number" || padding === undefined) {
816
+ this.updateStyleLength(
817
+ () => this.style.padding(edge),
818
+ (v) => this.style.setPadding(edge, v),
819
+ StyleLength.points(padding === undefined ? NaN : padding),
820
+ );
821
+ return;
822
+ }
823
+ dispatchSetter(
824
+ this,
825
+ "setPadding",
826
+ (e: Edge, value?: number) =>
827
+ this.updateStyleLength(
828
+ () => this.style.padding(e),
829
+ (v) => this.style.setPadding(e, v),
830
+ StyleLength.points(value === undefined ? NaN : value),
831
+ ),
832
+ [edge, padding],
833
+ );
834
+ }
835
+
836
+ setPaddingPercent(edge: Edge, padding: number | undefined): void {
837
+ this.updateStyleLength(
838
+ () => this.style.padding(edge),
839
+ (v) => this.style.setPadding(edge, v),
840
+ StyleLength.percent(padding === undefined ? NaN : padding),
841
+ );
842
+ }
843
+
844
+ setGap(gutter: Gutter, gapLength: number | `${number}%` | undefined): void {
845
+ if (typeof gapLength === "number" || gapLength === undefined) {
846
+ this.updateStyleLength(
847
+ () => this.style.gap(gutter),
848
+ (v) => this.style.setGap(gutter, v),
849
+ StyleLength.points(gapLength === undefined ? NaN : gapLength),
850
+ );
851
+ return;
852
+ }
853
+ dispatchSetter(
854
+ this,
855
+ "setGap",
856
+ (g: Gutter, value?: number) =>
857
+ this.updateStyleLength(
858
+ () => this.style.gap(g),
859
+ (v) => this.style.setGap(g, v),
860
+ StyleLength.points(value === undefined ? NaN : value),
861
+ ),
862
+ [gutter, gapLength],
863
+ );
864
+ }
865
+
866
+ setGapPercent(gutter: Gutter, gapLength: number | undefined): void {
867
+ this.updateStyleLength(
868
+ () => this.style.gap(gutter),
869
+ (v) => this.style.setGap(gutter, v),
870
+ StyleLength.percent(gapLength === undefined ? NaN : gapLength),
871
+ );
872
+ }
873
+
874
+ setBoxSizing(boxSizing: BoxSizing): void {
875
+ this.updateStyleEnum(
876
+ () => this.style.boxSizing(),
877
+ (v) => this.style.setBoxSizing(v),
878
+ boxSizing,
879
+ );
880
+ }
881
+
882
+ setIsReferenceBaseline(isReferenceBaseline: boolean): void {
883
+ if (this.isReferenceBaseline_ !== isReferenceBaseline) {
884
+ this.isReferenceBaseline_ = isReferenceBaseline;
885
+ this.markDirtyAndPropagate();
886
+ }
887
+ }
888
+
889
+ setAlwaysFormsContainingBlock(alwaysFormsContainingBlock: boolean): void {
890
+ this.alwaysFormsContainingBlock = alwaysFormsContainingBlock;
891
+ }
892
+
893
+ // --- Style getters ---
894
+ getPositionType(): PositionType {
895
+ return this.style.positionType();
896
+ }
897
+
898
+ getPosition(edge: Edge): Value {
899
+ return styleValue(this.style.position(edge));
900
+ }
901
+
902
+ getAlignContent(): Align {
903
+ return this.style.alignContent();
904
+ }
905
+
906
+ getAlignItems(): Align {
907
+ return this.style.alignItems();
908
+ }
909
+
910
+ getAlignSelf(): Align {
911
+ return this.style.alignSelf();
912
+ }
913
+
914
+ getFlexDirection(): FlexDirection {
915
+ return this.style.flexDirection();
916
+ }
917
+
918
+ getFlexWrap(): Wrap {
919
+ return this.style.flexWrap();
920
+ }
921
+
922
+ getJustifyContent(): Justify {
923
+ return this.style.justifyContent();
924
+ }
925
+
926
+ getDirection(): Direction {
927
+ return this.style.direction();
928
+ }
929
+
930
+ getMargin(edge: Edge): Value {
931
+ return styleValue(this.style.margin(edge));
932
+ }
933
+
934
+ getOverflow(): Overflow {
935
+ return this.style.overflow();
936
+ }
937
+
938
+ getDisplay(): Display {
939
+ return this.style.display();
940
+ }
941
+
942
+ getFlexBasis(): Value {
943
+ return styleValue(this.style.flexBasis());
944
+ }
945
+
946
+ getFlexGrow(): number {
947
+ const flexGrow = this.style.flexGrow();
948
+ return Number.isNaN(flexGrow) ? 0 : flexGrow;
949
+ }
950
+
951
+ getFlexShrink(): number {
952
+ const flexShrink = this.style.flexShrink();
953
+ return Number.isNaN(flexShrink) ? (this.config.useWebDefaults() ? 1 : 0) : flexShrink;
954
+ }
955
+
956
+ getWidth(): Value {
957
+ return styleValue(this.style.dimension(Dimension.Width));
958
+ }
959
+
960
+ getHeight(): Value {
961
+ return styleValue(this.style.dimension(Dimension.Height));
962
+ }
963
+
964
+ getMinWidth(): Value {
965
+ return styleValue(this.style.minDimension(Dimension.Width));
966
+ }
967
+
968
+ getMinHeight(): Value {
969
+ return styleValue(this.style.minDimension(Dimension.Height));
970
+ }
971
+
972
+ getMaxWidth(): Value {
973
+ return styleValue(this.style.maxDimension(Dimension.Width));
974
+ }
975
+
976
+ getMaxHeight(): Value {
977
+ return styleValue(this.style.maxDimension(Dimension.Height));
978
+ }
979
+
980
+ getAspectRatio(): number {
981
+ return this.style.aspectRatio();
982
+ }
983
+
984
+ getBorder(edge: Edge): number {
985
+ const border = this.style.border(edge);
986
+ if (border.isUndefined() || border.isAuto()) {
987
+ return NaN;
988
+ }
989
+ return border.value;
990
+ }
991
+
992
+ getPadding(edge: Edge): Value {
993
+ return styleValue(this.style.padding(edge));
994
+ }
995
+
996
+ getGap(gutter: Gutter): Value {
997
+ return styleValue(this.style.gap(gutter));
998
+ }
999
+
1000
+ getBoxSizing(): BoxSizing {
1001
+ return this.style.boxSizing();
1002
+ }
1003
+
1004
+ isReferenceBaseline(): boolean {
1005
+ return this.isReferenceBaseline_;
1006
+ }
1007
+
1008
+ // --- Measure / Dirtied ---
1009
+ override setMeasureFunc(measureFunc: MeasureFunction | null): void {
1010
+ if (measureFunc) {
1011
+ super.setMeasureFunc(measureFunc);
1012
+ } else {
1013
+ this.unsetMeasureFunc();
1014
+ }
1015
+ }
1016
+
1017
+ unsetMeasureFunc(): void {
1018
+ super.setMeasureFunc(null);
1019
+ }
1020
+
1021
+ setDirtiedFunc(dirtiedFunc: DirtiedFunction | null): void {
1022
+ if (dirtiedFunc) {
1023
+ this.setCoreDirtiedFunc(() => dirtiedFunc(this));
1024
+ } else {
1025
+ this.unsetDirtiedFunc();
1026
+ }
1027
+ }
1028
+
1029
+ unsetDirtiedFunc(): void {
1030
+ this.setCoreDirtiedFunc(null);
1031
+ }
1032
+
1033
+ // --- Dirty / Layout ---
1034
+ markDirty(): void {
1035
+ if (!this.hasMeasureFunc()) {
1036
+ throw new Error(
1037
+ "Only leaf nodes with custom measure functions should manually mark themselves as dirty",
1038
+ );
1039
+ }
1040
+ this.markDirtyAndPropagate();
1041
+ }
1042
+
1043
+ override isDirty(): boolean {
1044
+ return super.isDirty();
1045
+ }
1046
+
1047
+ markLayoutSeen(): void {
1048
+ this.hasNewLayout_ = false;
1049
+ }
1050
+
1051
+ hasNewLayout(): boolean {
1052
+ return this.hasNewLayout_;
1053
+ }
1054
+
1055
+ calculateLayout(
1056
+ width: number | "auto" | undefined = NaN,
1057
+ height: number | "auto" | undefined = NaN,
1058
+ direction: Direction = Direction.LTR,
1059
+ ): void {
1060
+ calculateLayout(
1061
+ this,
1062
+ typeof width === "number" ? width : NaN,
1063
+ typeof height === "number" ? height : NaN,
1064
+ direction,
1065
+ );
1066
+ }
1067
+
1068
+ // --- Layout getters ---
1069
+ getComputedLeft(): number {
1070
+ return this.layout.position(PhysicalEdge.Left);
1071
+ }
1072
+
1073
+ getComputedRight(): number {
1074
+ return this.layout.position(PhysicalEdge.Right);
1075
+ }
1076
+
1077
+ getComputedTop(): number {
1078
+ return this.layout.position(PhysicalEdge.Top);
1079
+ }
1080
+
1081
+ getComputedBottom(): number {
1082
+ return this.layout.position(PhysicalEdge.Bottom);
1083
+ }
1084
+
1085
+ getComputedWidth(): number {
1086
+ return this.layout.dimension(Dimension.Width);
1087
+ }
1088
+
1089
+ getComputedHeight(): number {
1090
+ return this.layout.dimension(Dimension.Height);
1091
+ }
1092
+
1093
+ getComputedHadOverflow(): boolean {
1094
+ return this.layout.hadOverflow();
1095
+ }
1096
+
1097
+ getComputedLayout(): Layout {
1098
+ return {
1099
+ left: this.getComputedLeft(),
1100
+ right: this.getComputedRight(),
1101
+ top: this.getComputedTop(),
1102
+ bottom: this.getComputedBottom(),
1103
+ width: this.getComputedWidth(),
1104
+ height: this.getComputedHeight(),
1105
+ hadOverflow: this.getComputedHadOverflow(),
1106
+ };
1107
+ }
1108
+
1109
+ private resolvedLayoutProperty(getter: (edge: PhysicalEdge) => number, edge: Edge): number {
1110
+ if (edge > Edge.End) {
1111
+ throw new Error("Cannot get layout properties of multi-edge shorthands");
1112
+ }
1113
+
1114
+ if (edge === Edge.Start) {
1115
+ return this.layout.direction() === Direction.RTL
1116
+ ? getter(PhysicalEdge.Right)
1117
+ : getter(PhysicalEdge.Left);
1118
+ }
1119
+
1120
+ if (edge === Edge.End) {
1121
+ return this.layout.direction() === Direction.RTL
1122
+ ? getter(PhysicalEdge.Left)
1123
+ : getter(PhysicalEdge.Right);
1124
+ }
1125
+
1126
+ return getter(edge as unknown as PhysicalEdge);
1127
+ }
1128
+
1129
+ getComputedMargin(edge: Edge): number {
1130
+ return this.resolvedLayoutProperty((e) => this.layout.margin(e), edge);
1131
+ }
1132
+
1133
+ getComputedBorder(edge: Edge): number {
1134
+ return this.resolvedLayoutProperty((e) => this.layout.border(e), edge);
1135
+ }
1136
+
1137
+ getComputedPadding(edge: Edge): number {
1138
+ return this.resolvedLayoutProperty((e) => this.layout.padding(e), edge);
1139
+ }
1140
+ }