@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,611 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Derived from Yoga. See THIRD_PARTY_NOTICES.md.
3
+
4
+ // Port of yoga/node/Node.h and yoga/node/Node.cpp, including the
5
+ // LayoutableChildren traversal from yoga/node/LayoutableChildren.h.
6
+ //
7
+ // The persistence/cloning machinery (owners distinct from parents, clone
8
+ // callbacks) is not exposed through the JS binding, so children are always
9
+ // uniquely owned and cloneChildrenIfNeeded is a no-op.
10
+
11
+ import {
12
+ Align,
13
+ BoxSizing,
14
+ Dimension,
15
+ Direction,
16
+ Display,
17
+ FlexDirection,
18
+ MeasureMode,
19
+ NodeType,
20
+ PositionType,
21
+ } from "../generated/YGEnums.ts";
22
+ import { Config, configUpdateInvalidatesLayout, getDefaultConfig } from "./config.ts";
23
+ import {
24
+ PhysicalEdge,
25
+ dimension,
26
+ inlineStartEdge,
27
+ inlineEndEdge,
28
+ isRow,
29
+ resolveCrossDirection,
30
+ resolveDirection,
31
+ } from "./helpers.ts";
32
+ import { LayoutResults } from "./layoutResults.ts";
33
+ import { isDefined, isUndefined, maxOrDefined } from "./numeric.ts";
34
+ import { Style } from "./style.ts";
35
+ import type { Size, StyleSizeLength } from "./types.ts";
36
+ import { StyleSizeLength as SizeLength } from "./types.ts";
37
+
38
+ export type MeasureFunc = (
39
+ width: number,
40
+ widthMode: MeasureMode,
41
+ height: number,
42
+ heightMode: MeasureMode,
43
+ ) => Size;
44
+
45
+ export type BaselineFunc = (width: number, height: number) => number;
46
+
47
+ export type DirtiedFunc = () => void;
48
+
49
+ export class Node {
50
+ hasNewLayout_ = true;
51
+ isReferenceBaseline_ = false;
52
+ private isDirty_ = true;
53
+ alwaysFormsContainingBlock = false;
54
+ nodeType: NodeType = NodeType.Default;
55
+ private measureFunc_: MeasureFunc | null = null;
56
+ private measureResult_: Size | null = null;
57
+ private baselineFunc_: BaselineFunc | null = null;
58
+ private dirtiedFunc_: DirtiedFunc | null = null;
59
+ style: Style = new Style();
60
+ layout: LayoutResults = new LayoutResults();
61
+ lineIndex = 0;
62
+ private contentsChildrenCount_ = 0;
63
+ owner: Node | null = null;
64
+ children: Node[] = [];
65
+ config: Config;
66
+ private processedDimensionWidth_: StyleSizeLength = SizeLength.undefined();
67
+ private processedDimensionHeight_: StyleSizeLength = SizeLength.undefined();
68
+ private dimensionsDirty_ = true;
69
+
70
+ constructor(config: Config = getDefaultConfig()) {
71
+ this.config = config;
72
+ if (config.useWebDefaults()) {
73
+ this.style.setFlexDirection(FlexDirection.Row);
74
+ this.style.setAlignContent(Align.Stretch);
75
+ }
76
+ }
77
+
78
+ hasMeasureFunc(): boolean {
79
+ return this.measureFunc_ !== null;
80
+ }
81
+
82
+ measure(
83
+ availableWidth: number,
84
+ widthMode: MeasureMode,
85
+ availableHeight: number,
86
+ heightMode: MeasureMode,
87
+ ): Size {
88
+ const size = this.measureFunc_!(availableWidth, widthMode, availableHeight, heightMode);
89
+
90
+ const width = size.width === undefined || size.width === null ? NaN : size.width;
91
+ const height = size.height === undefined || size.height === null ? NaN : size.height;
92
+
93
+ const result = (this.measureResult_ ??= { width: 0, height: 0 });
94
+
95
+ if (isUndefined(height) || height < 0 || isUndefined(width) || width < 0) {
96
+ result.width = maxOrDefined(0, width);
97
+ result.height = maxOrDefined(0, height);
98
+ } else {
99
+ result.width = width;
100
+ result.height = height;
101
+ }
102
+
103
+ return result;
104
+ }
105
+
106
+ hasBaselineFunc(): boolean {
107
+ return this.baselineFunc_ !== null;
108
+ }
109
+
110
+ baseline(width: number, height: number): number {
111
+ return this.baselineFunc_!(width, height);
112
+ }
113
+
114
+ getDirtiedFunc(): DirtiedFunc | null {
115
+ return this.dirtiedFunc_;
116
+ }
117
+
118
+ dimensionWithMargin(axis: FlexDirection, widthSize: number): number {
119
+ return (
120
+ this.layout.measuredDimension(dimension(axis)) +
121
+ this.style.computeMarginForAxis(axis, widthSize)
122
+ );
123
+ }
124
+
125
+ isLayoutDimensionDefined(axis: FlexDirection): boolean {
126
+ const value = this.layout.measuredDimension(dimension(axis));
127
+ return isDefined(value) && value >= 0;
128
+ }
129
+
130
+ /**
131
+ * Whether the node has a "definite length" along the given axis.
132
+ * https://www.w3.org/TR/css-sizing-3/#definite
133
+ */
134
+ hasDefiniteLength(dim: Dimension, ownerSize: number): boolean {
135
+ const usedValue = this.getProcessedDimension(dim).resolve(ownerSize);
136
+ return isDefined(usedValue) && usedValue >= 0;
137
+ }
138
+
139
+ hasErrata(errata: number): boolean {
140
+ return this.config.hasErrata(errata);
141
+ }
142
+
143
+ hasContentsChildren(): boolean {
144
+ return this.contentsChildrenCount_ !== 0;
145
+ }
146
+
147
+ getChildCount(): number {
148
+ return this.children.length;
149
+ }
150
+
151
+ getChild(index: number): Node {
152
+ return this.children[index]!;
153
+ }
154
+
155
+ /**
156
+ * Children for layout purposes: skips display: contents nodes, splicing
157
+ * their children in place. Returns the raw children array when nothing
158
+ * needs splicing — do not mutate the result.
159
+ */
160
+ getLayoutChildren(): readonly Node[] {
161
+ if (this.contentsChildrenCount_ === 0) {
162
+ return this.children;
163
+ }
164
+ const result: Node[] = [];
165
+ appendLayoutChildren(this, result);
166
+ return result;
167
+ }
168
+
169
+ getLayoutChildCount(): number {
170
+ if (this.contentsChildrenCount_ === 0) {
171
+ return this.children.length;
172
+ }
173
+ return this.getLayoutChildren().length;
174
+ }
175
+
176
+ isDirty(): boolean {
177
+ return this.isDirty_;
178
+ }
179
+
180
+ getProcessedDimension(dim: Dimension): StyleSizeLength {
181
+ return dim === Dimension.Width ? this.processedDimensionWidth_ : this.processedDimensionHeight_;
182
+ }
183
+
184
+ getResolvedDimension(
185
+ direction: Direction,
186
+ dim: Dimension,
187
+ referenceLength: number,
188
+ ownerWidth: number,
189
+ ): number {
190
+ const value = this.getProcessedDimension(dim).resolve(referenceLength);
191
+ if (this.style.boxSizing() === BoxSizing.BorderBox) {
192
+ return value;
193
+ }
194
+
195
+ const dimensionPaddingAndBorder = this.style.computePaddingAndBorderForDimension(
196
+ direction,
197
+ dim,
198
+ ownerWidth,
199
+ );
200
+
201
+ return value + (isDefined(dimensionPaddingAndBorder) ? dimensionPaddingAndBorder : 0);
202
+ }
203
+
204
+ // Setters
205
+
206
+ setMeasureFunc(measureFunc: MeasureFunc | null): void {
207
+ if (measureFunc === null) {
208
+ this.nodeType = NodeType.Default;
209
+ } else {
210
+ if (this.children.length !== 0) {
211
+ throw new Error(
212
+ "Cannot set measure function: Nodes with measure functions cannot have children.",
213
+ );
214
+ }
215
+ this.nodeType = NodeType.Text;
216
+ }
217
+
218
+ this.measureFunc_ = measureFunc;
219
+ // nodeType selects text rounding behavior, which is a pixel-grid input.
220
+ this.markLayoutWritten();
221
+ }
222
+
223
+ setBaselineFunc(baselineFunc: BaselineFunc | null): void {
224
+ this.baselineFunc_ = baselineFunc;
225
+ }
226
+
227
+ setCoreDirtiedFunc(dirtiedFunc: DirtiedFunc | null): void {
228
+ this.dirtiedFunc_ = dirtiedFunc;
229
+ }
230
+
231
+ insertChild(child: Node, index: number): void {
232
+ if (child.style.display() === Display.Contents) {
233
+ this.contentsChildrenCount_++;
234
+ }
235
+
236
+ if (index === this.children.length) {
237
+ this.children.push(child);
238
+ } else {
239
+ this.children.splice(index, 0, child);
240
+ }
241
+ }
242
+
243
+ setConfig(config: Config): void {
244
+ if (config.useWebDefaults() !== this.config.useWebDefaults()) {
245
+ throw new Error("UseWebDefaults may not be changed after constructing a Node");
246
+ }
247
+
248
+ if (configUpdateInvalidatesLayout(this.config, config)) {
249
+ this.markDirtyAndPropagate();
250
+ this.layout.configVersion = 0;
251
+ } else {
252
+ // If the config is functionally the same, then align the configVersion
253
+ // so that we can reuse the layout cache
254
+ this.layout.configVersion = config.getVersion();
255
+ }
256
+
257
+ this.config = config;
258
+ }
259
+
260
+ setDirty(isDirty: boolean): void {
261
+ if (isDirty === this.isDirty_) {
262
+ return;
263
+ }
264
+ this.isDirty_ = isDirty;
265
+ if (isDirty && this.dirtiedFunc_ !== null) {
266
+ this.dirtiedFunc_();
267
+ }
268
+ }
269
+
270
+ setChildren(children: Node[]): void {
271
+ this.children = children.slice();
272
+
273
+ this.contentsChildrenCount_ = 0;
274
+ for (const child of children) {
275
+ if (child.style.display() === Display.Contents) {
276
+ this.contentsChildrenCount_++;
277
+ }
278
+ }
279
+ }
280
+
281
+ removeChild(child: Node): boolean {
282
+ const index = this.children.indexOf(child);
283
+ if (index >= 0) {
284
+ if (child.style.display() === Display.Contents) {
285
+ this.contentsChildrenCount_--;
286
+ }
287
+
288
+ this.children.splice(index, 1);
289
+ return true;
290
+ }
291
+ return false;
292
+ }
293
+
294
+ removeChildAtIndex(index: number): void {
295
+ if (this.children[index]!.style.display() === Display.Contents) {
296
+ this.contentsChildrenCount_--;
297
+ }
298
+
299
+ this.children.splice(index, 1);
300
+ }
301
+
302
+ clearChildren(): void {
303
+ this.children = [];
304
+ }
305
+
306
+ // Called by the public wrapper when a child's display changes between
307
+ // Contents and something else while attached.
308
+ syncContentsChildrenCount(): void {
309
+ this.contentsChildrenCount_ = 0;
310
+ for (const child of this.children) {
311
+ if (child.style.display() === Display.Contents) {
312
+ this.contentsChildrenCount_++;
313
+ }
314
+ }
315
+ }
316
+
317
+ setLayoutDirection(direction: Direction): void {
318
+ this.layout.setDirection(direction);
319
+ }
320
+
321
+ setLayoutMargin(margin: number, edge: PhysicalEdge): void {
322
+ this.layout.setMargin(edge, margin);
323
+ }
324
+
325
+ setLayoutBorder(border: number, edge: PhysicalEdge): void {
326
+ this.layout.setBorder(edge, border);
327
+ }
328
+
329
+ setLayoutPadding(padding: number, edge: PhysicalEdge): void {
330
+ this.layout.setPadding(edge, padding);
331
+ }
332
+
333
+ setLayoutLastOwnerDirection(direction: Direction): void {
334
+ this.layout.lastOwnerDirection = direction;
335
+ }
336
+
337
+ setLayoutComputedFlexBasis(computedFlexBasis: number): void {
338
+ this.layout.computedFlexBasis = computedFlexBasis;
339
+ }
340
+
341
+ // Marks this node's rounding state (and its ancestors') dirty. Ancestors
342
+ // are marked together with descendants, so an already-set flag means the
343
+ // whole ancestor chain is set.
344
+ markLayoutWritten(): void {
345
+ if (this.layout.roundingDirty) {
346
+ return;
347
+ }
348
+ this.layout.roundingDirty = true;
349
+ let node = this.owner;
350
+ while (node !== null && !node.layout.roundingDirty) {
351
+ node.layout.roundingDirty = true;
352
+ node = node.owner;
353
+ }
354
+ }
355
+
356
+ setLayoutPosition(position: number, edge: PhysicalEdge): void {
357
+ // Only value-changing writes dirty the pixel-grid state — rewriting an
358
+ // identical value leaves every rounding input unchanged. Object.is treats
359
+ // NaN as equal to itself (and only over-marks on -0 vs 0).
360
+ const value = position;
361
+ if (!Object.is(this.layout.position(edge), value)) {
362
+ this.layout.setPosition(edge, value);
363
+ this.markLayoutWritten();
364
+ }
365
+ }
366
+
367
+ setLayoutComputedFlexBasisGeneration(computedFlexBasisGeneration: number): void {
368
+ this.layout.computedFlexBasisGeneration = computedFlexBasisGeneration;
369
+ }
370
+
371
+ setLayoutMeasuredDimension(measuredDimension: number, dim: Dimension): void {
372
+ this.layout.setMeasuredDimension(dim, measuredDimension);
373
+ }
374
+
375
+ setLayoutHadOverflow(hadOverflow: boolean): void {
376
+ this.layout.setHadOverflow(hadOverflow);
377
+ }
378
+
379
+ setLayoutDimension(lengthValue: number, dim: Dimension): void {
380
+ const value = lengthValue;
381
+ if (
382
+ !Object.is(this.layout.dimension(dim), value) ||
383
+ !Object.is(this.layout.rawDimension(dim), value)
384
+ ) {
385
+ this.layout.setDimension(dim, value);
386
+ this.layout.setRawDimension(dim, value);
387
+ this.markLayoutWritten();
388
+ }
389
+ }
390
+
391
+ // If both left and right are defined, then use left. Otherwise return +left
392
+ // or -right depending on which is defined. Ignore statically positioned
393
+ // nodes as insets do not apply to them.
394
+ relativePosition(axis: FlexDirection, direction: Direction, axisSize: number): number {
395
+ if (this.style.positionType() === PositionType.Static) {
396
+ return 0;
397
+ }
398
+ if (
399
+ this.style.isInlineStartPositionDefined(axis, direction) &&
400
+ !this.style.isInlineStartPositionAuto(axis, direction)
401
+ ) {
402
+ return this.style.computeInlineStartPosition(axis, direction, axisSize);
403
+ }
404
+
405
+ return -1 * this.style.computeInlineEndPosition(axis, direction, axisSize);
406
+ }
407
+
408
+ setPositionFromStyle(direction: Direction, ownerWidth: number, ownerHeight: number): void {
409
+ if (!this.style.hasPositionOrMargin()) {
410
+ this.setLayoutPosition(0, PhysicalEdge.Left);
411
+ this.setLayoutPosition(0, PhysicalEdge.Top);
412
+ this.setLayoutPosition(0, PhysicalEdge.Right);
413
+ this.setLayoutPosition(0, PhysicalEdge.Bottom);
414
+ return;
415
+ }
416
+ /* Root nodes should be always layouted as LTR, so we don't return negative
417
+ * values. */
418
+ const directionRespectingRoot = this.owner !== null ? direction : Direction.LTR;
419
+ const mainAxis = resolveDirection(this.style.flexDirection(), directionRespectingRoot);
420
+ const crossAxis = resolveCrossDirection(mainAxis, directionRespectingRoot);
421
+
422
+ // In the case of position static these are just 0. See:
423
+ // https://www.w3.org/TR/css-position-3/#valdef-position-static
424
+ const relativePositionMain = this.relativePosition(
425
+ mainAxis,
426
+ directionRespectingRoot,
427
+ isRow(mainAxis) ? ownerWidth : ownerHeight,
428
+ );
429
+ const relativePositionCross = this.relativePosition(
430
+ crossAxis,
431
+ directionRespectingRoot,
432
+ isRow(mainAxis) ? ownerHeight : ownerWidth,
433
+ );
434
+
435
+ const mainAxisLeadingEdge = inlineStartEdge(mainAxis, direction);
436
+ const mainAxisTrailingEdge = inlineEndEdge(mainAxis, direction);
437
+ const crossAxisLeadingEdge = inlineStartEdge(crossAxis, direction);
438
+ const crossAxisTrailingEdge = inlineEndEdge(crossAxis, direction);
439
+
440
+ this.setLayoutPosition(
441
+ this.style.computeInlineStartMargin(mainAxis, direction, ownerWidth) + relativePositionMain,
442
+ mainAxisLeadingEdge,
443
+ );
444
+ this.setLayoutPosition(
445
+ this.style.computeInlineEndMargin(mainAxis, direction, ownerWidth) + relativePositionMain,
446
+ mainAxisTrailingEdge,
447
+ );
448
+ this.setLayoutPosition(
449
+ this.style.computeInlineStartMargin(crossAxis, direction, ownerWidth) + relativePositionCross,
450
+ crossAxisLeadingEdge,
451
+ );
452
+ this.setLayoutPosition(
453
+ this.style.computeInlineEndMargin(crossAxis, direction, ownerWidth) + relativePositionCross,
454
+ crossAxisTrailingEdge,
455
+ );
456
+ }
457
+
458
+ processFlexBasis(): StyleSizeLength {
459
+ const flexBasis = this.style.flexBasis();
460
+ if (!flexBasis.isAuto() && !flexBasis.isUndefined()) {
461
+ return flexBasis;
462
+ }
463
+ if (isDefined(this.style.flex()) && this.style.flex() > 0) {
464
+ return this.config.useWebDefaults() ? SizeLength.ofAuto() : SizeLength.points(0);
465
+ }
466
+ return SizeLength.ofAuto();
467
+ }
468
+
469
+ resolveFlexBasis(
470
+ direction: Direction,
471
+ flexDirection: FlexDirection,
472
+ referenceLength: number,
473
+ ownerWidth: number,
474
+ ): number {
475
+ const value = this.processFlexBasis().resolve(referenceLength);
476
+ if (this.style.boxSizing() === BoxSizing.BorderBox) {
477
+ return value;
478
+ }
479
+
480
+ const dim = dimension(flexDirection);
481
+ const dimensionPaddingAndBorder = this.style.computePaddingAndBorderForDimension(
482
+ direction,
483
+ dim,
484
+ ownerWidth,
485
+ );
486
+
487
+ return value + (isDefined(dimensionPaddingAndBorder) ? dimensionPaddingAndBorder : 0);
488
+ }
489
+
490
+ processDimensions(): void {
491
+ if (!this.dimensionsDirty_) {
492
+ return;
493
+ }
494
+ for (let dim = Dimension.Width; dim <= Dimension.Height; dim++) {
495
+ const maxDimension = this.style.maxDimension(dim);
496
+ const processed =
497
+ maxDimension.isDefined() && maxDimension.inexactEquals(this.style.minDimension(dim))
498
+ ? maxDimension
499
+ : this.style.dimension(dim);
500
+ if (dim === Dimension.Width) {
501
+ this.processedDimensionWidth_ = processed;
502
+ } else {
503
+ this.processedDimensionHeight_ = processed;
504
+ }
505
+ }
506
+ this.dimensionsDirty_ = false;
507
+ }
508
+
509
+ invalidateProcessedDimensions(): void {
510
+ this.dimensionsDirty_ = true;
511
+ }
512
+
513
+ resolveDirection(ownerDirection: Direction): Direction {
514
+ if (this.style.direction() === Direction.Inherit) {
515
+ return ownerDirection !== Direction.Inherit ? ownerDirection : Direction.LTR;
516
+ } else {
517
+ return this.style.direction();
518
+ }
519
+ }
520
+
521
+ cloneChildrenIfNeeded(): void {
522
+ // No-op: the JS binding does not support shared subtrees, so children are
523
+ // always uniquely owned by their parent.
524
+ }
525
+
526
+ cloneContentsChildrenIfNeeded(): void {
527
+ // No-op: see cloneChildrenIfNeeded.
528
+ }
529
+
530
+ markDirtyAndPropagate(): void {
531
+ if (!this.isDirty_) {
532
+ this.setDirty(true);
533
+ this.setLayoutComputedFlexBasis(NaN);
534
+ if (this.owner !== null) {
535
+ this.owner.markDirtyAndPropagate();
536
+ }
537
+ }
538
+ }
539
+
540
+ resolveFlexGrow(): number {
541
+ // Root nodes flexGrow should always be 0
542
+ if (this.owner === null) {
543
+ return 0;
544
+ }
545
+ if (isDefined(this.style.flexGrow())) {
546
+ return this.style.flexGrow();
547
+ }
548
+ if (isDefined(this.style.flex()) && this.style.flex() > 0) {
549
+ return this.style.flex();
550
+ }
551
+ return Style.DefaultFlexGrow;
552
+ }
553
+
554
+ resolveFlexShrink(): number {
555
+ if (this.owner === null) {
556
+ return 0;
557
+ }
558
+ if (isDefined(this.style.flexShrink())) {
559
+ return this.style.flexShrink();
560
+ }
561
+ if (!this.config.useWebDefaults() && isDefined(this.style.flex()) && this.style.flex() < 0) {
562
+ return -this.style.flex();
563
+ }
564
+ return this.config.useWebDefaults() ? Style.WebDefaultFlexShrink : Style.DefaultFlexShrink;
565
+ }
566
+
567
+ isNodeFlexible(): boolean {
568
+ return (
569
+ this.style.positionType() !== PositionType.Absolute &&
570
+ (this.resolveFlexGrow() !== 0 || this.resolveFlexShrink() !== 0)
571
+ );
572
+ }
573
+
574
+ reset(): void {
575
+ if (this.children.length !== 0) {
576
+ throw new Error("Cannot reset a node which still has children attached");
577
+ }
578
+ if (this.owner !== null) {
579
+ throw new Error("Cannot reset a node still attached to a owner");
580
+ }
581
+
582
+ const fresh = new Node(this.config);
583
+ this.hasNewLayout_ = fresh.hasNewLayout_;
584
+ this.isReferenceBaseline_ = fresh.isReferenceBaseline_;
585
+ this.isDirty_ = true;
586
+ this.alwaysFormsContainingBlock = fresh.alwaysFormsContainingBlock;
587
+ this.nodeType = fresh.nodeType;
588
+ this.measureFunc_ = null;
589
+ this.measureResult_ = null;
590
+ this.baselineFunc_ = null;
591
+ this.dirtiedFunc_ = null;
592
+ this.style = fresh.style;
593
+ this.layout = fresh.layout;
594
+ this.lineIndex = 0;
595
+ this.contentsChildrenCount_ = 0;
596
+ this.children = [];
597
+ this.processedDimensionWidth_ = SizeLength.undefined();
598
+ this.processedDimensionHeight_ = SizeLength.undefined();
599
+ this.dimensionsDirty_ = true;
600
+ }
601
+ }
602
+
603
+ function appendLayoutChildren(node: Node, out: Node[]): void {
604
+ for (const child of node.children) {
605
+ if (child.style.display() === Display.Contents) {
606
+ appendLayoutChildren(child, out);
607
+ } else {
608
+ out.push(child);
609
+ }
610
+ }
611
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Derived from Yoga. See THIRD_PARTY_NOTICES.md.
3
+
4
+ // Port of yoga/numeric/Comparison.h and yoga/numeric/FloatOptional.h.
5
+ //
6
+ // C++ FloatOptional wraps a float where NaN means "undefined". In this port an
7
+ // optional float is just a `number` with NaN as the undefined sentinel, and
8
+ // these helpers reproduce the C++ comparison semantics while calculations
9
+ // themselves use JavaScript's native double-precision numbers.
10
+
11
+ export function isUndefined(value: number): boolean {
12
+ return Number.isNaN(value);
13
+ }
14
+
15
+ export function isDefined(value: number): boolean {
16
+ return !Number.isNaN(value);
17
+ }
18
+
19
+ export function maxOrDefined(a: number, b: number): number {
20
+ if (isDefined(a) && isDefined(b)) {
21
+ return Math.max(a, b);
22
+ }
23
+ return isUndefined(a) ? b : a;
24
+ }
25
+
26
+ export function minOrDefined(a: number, b: number): number {
27
+ if (isDefined(a) && isDefined(b)) {
28
+ return Math.min(a, b);
29
+ }
30
+ return isUndefined(a) ? b : a;
31
+ }
32
+
33
+ // Custom equality using a hardcoded epsilon of 0.0001, or true if both are NaN.
34
+ export function inexactEquals(a: number, b: number): boolean {
35
+ if (isDefined(a) && isDefined(b)) {
36
+ return Math.abs(a - b) < 0.0001;
37
+ }
38
+ return isUndefined(a) && isUndefined(b);
39
+ }
40
+
41
+ // FloatOptional::operator== — exact equality, or both undefined.
42
+ export function optionalEquals(a: number, b: number): boolean {
43
+ return a === b || (isUndefined(a) && isUndefined(b));
44
+ }