flexily 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/classic/layout.d.ts +57 -0
  4. package/dist/classic/layout.d.ts.map +1 -0
  5. package/dist/classic/layout.js +1558 -0
  6. package/dist/classic/layout.js.map +1 -0
  7. package/dist/classic/node.d.ts +648 -0
  8. package/dist/classic/node.d.ts.map +1 -0
  9. package/dist/classic/node.js +1002 -0
  10. package/dist/classic/node.js.map +1 -0
  11. package/dist/constants.d.ts +58 -0
  12. package/dist/constants.d.ts.map +1 -0
  13. package/dist/constants.js +70 -0
  14. package/dist/constants.js.map +1 -0
  15. package/dist/index-classic.d.ts +30 -0
  16. package/dist/index-classic.d.ts.map +1 -0
  17. package/dist/index-classic.js +57 -0
  18. package/dist/index-classic.js.map +1 -0
  19. package/dist/index.d.ts +30 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +57 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/layout-flex-lines.d.ts +77 -0
  24. package/dist/layout-flex-lines.d.ts.map +1 -0
  25. package/dist/layout-flex-lines.js +317 -0
  26. package/dist/layout-flex-lines.js.map +1 -0
  27. package/dist/layout-helpers.d.ts +48 -0
  28. package/dist/layout-helpers.d.ts.map +1 -0
  29. package/dist/layout-helpers.js +108 -0
  30. package/dist/layout-helpers.js.map +1 -0
  31. package/dist/layout-measure.d.ts +25 -0
  32. package/dist/layout-measure.d.ts.map +1 -0
  33. package/dist/layout-measure.js +231 -0
  34. package/dist/layout-measure.js.map +1 -0
  35. package/dist/layout-stats.d.ts +19 -0
  36. package/dist/layout-stats.d.ts.map +1 -0
  37. package/dist/layout-stats.js +37 -0
  38. package/dist/layout-stats.js.map +1 -0
  39. package/dist/layout-traversal.d.ts +28 -0
  40. package/dist/layout-traversal.d.ts.map +1 -0
  41. package/dist/layout-traversal.js +65 -0
  42. package/dist/layout-traversal.js.map +1 -0
  43. package/dist/layout-zero.d.ts +26 -0
  44. package/dist/layout-zero.d.ts.map +1 -0
  45. package/dist/layout-zero.js +1601 -0
  46. package/dist/layout-zero.js.map +1 -0
  47. package/dist/logger.d.ts +14 -0
  48. package/dist/logger.d.ts.map +1 -0
  49. package/dist/logger.js +61 -0
  50. package/dist/logger.js.map +1 -0
  51. package/dist/node-zero.d.ts +702 -0
  52. package/dist/node-zero.d.ts.map +1 -0
  53. package/dist/node-zero.js +1268 -0
  54. package/dist/node-zero.js.map +1 -0
  55. package/dist/testing.d.ts +69 -0
  56. package/dist/testing.d.ts.map +1 -0
  57. package/dist/testing.js +179 -0
  58. package/dist/testing.js.map +1 -0
  59. package/dist/trace.d.ts +74 -0
  60. package/dist/trace.d.ts.map +1 -0
  61. package/dist/trace.js +191 -0
  62. package/dist/trace.js.map +1 -0
  63. package/dist/types.d.ts +170 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +43 -0
  66. package/dist/types.js.map +1 -0
  67. package/dist/utils.d.ts +41 -0
  68. package/dist/utils.d.ts.map +1 -0
  69. package/dist/utils.js +197 -0
  70. package/dist/utils.js.map +1 -0
  71. package/package.json +58 -3
  72. package/src/CLAUDE.md +512 -0
  73. package/src/beorn-logger.d.ts +10 -0
  74. package/src/classic/layout.ts +1783 -0
  75. package/src/classic/node.ts +1121 -0
  76. package/src/constants.ts +81 -0
  77. package/src/index-classic.ts +110 -0
  78. package/src/index.ts +110 -0
  79. package/src/layout-flex-lines.ts +346 -0
  80. package/src/layout-helpers.ts +140 -0
  81. package/src/layout-measure.ts +259 -0
  82. package/src/layout-stats.ts +43 -0
  83. package/src/layout-traversal.ts +70 -0
  84. package/src/layout-zero.ts +1792 -0
  85. package/src/logger.ts +67 -0
  86. package/src/node-zero.ts +1412 -0
  87. package/src/testing.ts +209 -0
  88. package/src/trace.ts +252 -0
  89. package/src/types.ts +229 -0
  90. package/src/utils.ts +217 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Beorn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # Flexily
2
+
3
+ **Pure JavaScript flexbox layout engine with Yoga-compatible API.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/flexily.svg)](https://www.npmjs.com/package/flexily)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ```typescript
9
+ import { Node, FLEX_DIRECTION_ROW, DIRECTION_LTR } from "@beorn/flexily"
10
+
11
+ const root = Node.create()
12
+ root.setWidth(100)
13
+ root.setFlexDirection(FLEX_DIRECTION_ROW)
14
+
15
+ const child = Node.create()
16
+ child.setFlexGrow(1)
17
+ root.insertChild(child, 0)
18
+
19
+ root.calculateLayout(100, 100, DIRECTION_LTR)
20
+ console.log(child.getComputedWidth()) // 100
21
+ ```
22
+
23
+ ## Why Flexily?
24
+
25
+ [Yoga](https://yogalayout.dev/) is the industry standard flexbox engine, used by React Native, Ink, and thousands of apps. It's mature and battle-tested. But it's C++ compiled to WASM, and that creates real problems for JavaScript applications:
26
+
27
+ **Async initialization.** Yoga requires `await Yoga.init()` before creating any nodes. No synchronous startup, no use at module load time, no use in config files or build scripts. For CLIs that should start instantly, this adds latency and complexity.
28
+
29
+ **WASM boundary crossing.** Every method call (`setWidth`, `setFlexGrow`, etc.) crosses the JS-to-WASM boundary. Node creation is ~8x more expensive than a JS object. For TUIs that rebuild layout trees per render, this dominates.
30
+
31
+ **Memory growth.** WASM linear memory grows but never shrinks. Yoga's yoga-wasm-web has a [known memory growth bug](https://github.com/nicolo-ribaudo/yoga-wasm-web/issues/1) where each node allocation permanently grows the WASM heap. In long-running apps, this caused [120GB RAM usage in Claude Code](https://github.com/anthropics/claude-code/issues/4953).
32
+
33
+ **Debugging opacity.** You can't step into WASM in a JS debugger. When layout is wrong, you get a computed number with no way to inspect the algorithm's intermediate state. Flexily is readable JS — set a breakpoint in `layout-zero.ts`.
34
+
35
+ **No tree-shaking.** The WASM binary is monolithic. You get the entire engine even if you use a fraction of the features.
36
+
37
+ Facebook's original pure-JS flexbox engine (`css-layout`) was abandoned when they moved to C++. [flexbox.js](https://github.com/Planning-nl/flexbox.js) exists but is unmaintained and missing features. Flexily fills the gap: full CSS flexbox spec, Yoga-compatible API, pure JS, zero WASM.
38
+
39
+ ## Who Should Use Flexily
40
+
41
+ Most developers should use a framework built on Flexily, not Flexily directly. Flexily is for:
42
+
43
+ - **Framework authors** building a TUI or layout framework that needs a JS layout engine
44
+ - **Canvas/game developers** who need flexbox for non-DOM rendering
45
+ - **Specialized tools** where you need direct control over layout computation
46
+ - **Anyone replacing Yoga** who wants a drop-in pure-JS alternative
47
+
48
+ > **Building a terminal UI?** Use [silvery](https://github.com/beorn/silvery), which uses Flexily by default. You get React components, hooks, and layout feedback without touching the low-level API.
49
+
50
+ ## Status
51
+
52
+ 1368 tests passing, including 41/41 Yoga compatibility tests and 1200+ incremental re-layout fuzz tests. Used by [silvery](https://github.com/beorn/silvery) as its default layout engine.
53
+
54
+ | Feature | Status |
55
+ | --------------------------------------------- | -------- |
56
+ | Core flexbox (direction, grow, shrink, basis) | Complete |
57
+ | Alignment (justify-content, align-items) | Complete |
58
+ | Spacing (gap, padding, margin, border) | Complete |
59
+ | Constraints (min/max width/height) | Complete |
60
+ | Measure functions (text sizing) | Complete |
61
+ | Absolute positioning | Complete |
62
+ | Aspect ratio | Complete |
63
+ | Flex-wrap (multi-line layouts) | Complete |
64
+ | Logical edges (EDGE_START/END) | Complete |
65
+ | RTL support | Complete |
66
+ | Baseline alignment | Complete |
67
+
68
+ ## Installation
69
+
70
+ ```bash
71
+ bun add @beorn/flexily
72
+ # or
73
+ npm install @beorn/flexily
74
+ ```
75
+
76
+ ## Performance
77
+
78
+ Flexily and Yoga each win in different scenarios:
79
+
80
+ | Scenario | Winner | Margin | Tree Size |
81
+ | ----------------------- | ----------- | ---------- | ---------------- |
82
+ | **Initial layout** | Flexily | 1.5-2.5x | 64-969 nodes |
83
+ | **No-change re-layout** | **Flexily** | **5.5x** | 406-969 nodes |
84
+ | **Single dirty leaf** | Yoga | 2.8-3.4x | 406-969 nodes |
85
+ | **Deep nesting (15+)** | Yoga | increasing | 1 node per level |
86
+
87
+ Benchmarks use TUI-realistic trees: columns × bordered cards with measure functions (e.g., 5 columns × 20 cards = ~406 nodes, 8×30 = ~969 nodes). Typical depth is 3-5 levels (column → card → content → text). See [docs/performance.md](docs/performance.md) for full methodology.
88
+
89
+ **Where Yoga wins — and why it matters less in practice.** Yoga is 2.8-3.4x faster in the single-dirty-leaf scenario: one node changes in a ~400-1000 node tree. WASM's per-node layout computation is genuinely faster than JS. But in interactive TUIs, most renders are no-change frames (cursor moved, selection changed) where Flexily is 5.5x faster. Initial layout (new screen, tab switch) also favors Flexily at 1.5-2.5x. The single-dirty-leaf case is a minority of frames in practice.
90
+
91
+ **Typical interactive TUI operation mix:**
92
+
93
+ | Operation | Frequency | Winner | Why |
94
+ | --------------------- | ------------- | -------------- | ------------------------------------- |
95
+ | Cursor/selection move | Very frequent | Flexily 5.5x | No layout change → fingerprint cache |
96
+ | Content edit | Frequent | Yoga 3x | Single dirty leaf in existing tree |
97
+ | Initial render | Once | Flexily 1.5-2x | JS node creation avoids WASM boundary |
98
+ | Window resize | Occasional | Yoga 2.7x | Full re-layout of existing tree |
99
+
100
+ Flexily's fingerprint cache makes no-change re-layout essentially free (27ns regardless of tree size). Initial layout wins come from JS node creation avoiding WASM boundary crossings (~8x cheaper per node). Most TUI apps have shallow nesting (3-5 levels) — well below the 15-level crossover where Yoga overtakes Flexily.
101
+
102
+ **Use Yoga instead when** your primary workload is frequent incremental re-layout of large pre-existing trees, you have deep nesting (15+ levels), or you're in the React Native ecosystem.
103
+
104
+ See [docs/performance.md](docs/performance.md) for detailed benchmarks including TUI-realistic trees with measure functions.
105
+
106
+ ## Algorithm
107
+
108
+ Flexily provides two layout implementations that produce identical output and pass identical tests:
109
+
110
+ **Zero-allocation** (default, `@beorn/flexily`): Mutates `FlexInfo` structs on nodes instead of allocating temporary objects. Faster for flat/wide trees typical of TUI layouts. Not reentrant — a single layout pass must complete before another starts.
111
+
112
+ **Classic** (`@beorn/flexily/classic`): Allocates temporary objects during layout. Easier to read and debug. Use this when stepping through the algorithm or comparing behavior.
113
+
114
+ ```typescript
115
+ import { Node } from "@beorn/flexily" // zero-allocation (default)
116
+ import { Node } from "@beorn/flexily/classic" // allocating (debugging)
117
+ ```
118
+
119
+ Both implement CSS Flexbox spec Section 9.7 with iterative freeze for min/max constraints, Yoga-compatible edge-based rounding, weighted flex-shrink, auto margin absorption, and full RTL support.
120
+
121
+ ## Correctness
122
+
123
+ Incremental re-layout (caching unchanged subtrees) is essential for performance but introduces subtle bugs — Chrome's Blink team experienced a "chain of ~10 bugs over a year" in their flexbox implementation. Flexily addresses this with layered testing:
124
+
125
+ | Layer | Tests | What it verifies |
126
+ | ------------------ | --------- | -------------------------------------------------------------- |
127
+ | Yoga compatibility | 41 | Identical output to Yoga for every feature |
128
+ | Feature tests | ~110 | Each flexbox feature in isolation |
129
+ | **Re-layout fuzz** | **1200+** | Incremental re-layout matches fresh layout across random trees |
130
+
131
+ The fuzz tests use a **differential oracle**: build a random tree, layout, mark nodes dirty, re-layout, then compare against a fresh layout of the identical tree. This has caught 3 distinct caching bugs that all 524 single-pass tests missed.
132
+
133
+ See [docs/testing.md](docs/testing.md) for methodology and [docs/incremental-layout-bugs.md](docs/incremental-layout-bugs.md) for the bug taxonomy.
134
+
135
+ ## Bundle Size
136
+
137
+ | | Yoga | Flexily | Savings |
138
+ | -------- | ------ | ----------------- | -------------------- |
139
+ | Minified | 117 KB | 47 KB (35 KB[^1]) | **2.5-3.4x smaller** |
140
+ | Gzipped | 39 KB | 16 KB (11 KB[^1]) | **2.5-3.6x smaller** |
141
+
142
+ [^1]: 11 KB when bundlers tree-shake the optional `debug` dependency.
143
+
144
+ ## API Compatibility
145
+
146
+ 100% Yoga API compatibility (41/41 comparison tests passing). Drop-in replacement:
147
+
148
+ ```typescript
149
+ // Yoga
150
+ import Yoga from "yoga-wasm-web"
151
+ const yoga = await Yoga.init() // Async!
152
+ const root = yoga.Node.create()
153
+
154
+ // Flexily
155
+ import { Node } from "@beorn/flexily"
156
+ const root = Node.create() // Sync!
157
+ ```
158
+
159
+ Same constants, same method names, same behavior.
160
+
161
+ ## Documentation
162
+
163
+ | Document | Description |
164
+ | ---------------------------------------------------------- | ----------------------------------- |
165
+ | [Getting Started](docs/getting-started.md) | Quick guide to building layouts |
166
+ | [API Reference](docs/api.md) | Complete API documentation |
167
+ | [Algorithm](docs/algorithm.md) | How the layout algorithm works |
168
+ | [Performance](docs/performance.md) | Benchmarks and methodology |
169
+ | [Yoga Comparison](docs/yoga-comparison.md) | Feature comparison with Yoga |
170
+ | [Testing](docs/testing.md) | Test infrastructure and methodology |
171
+ | [Incremental Layout Bugs](docs/incremental-layout-bugs.md) | Bug taxonomy and debugging guide |
172
+
173
+ ## Related Projects
174
+
175
+ | Project | Language | Description |
176
+ | ------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------- |
177
+ | [Yoga](https://yogalayout.dev/) | C++/WASM | Facebook's flexbox engine. Industry standard, used by React Native, Ink, Litho. |
178
+ | [Taffy](https://github.com/DioxusLabs/taffy) | Rust | High-performance layout library supporting Flexbox and CSS Grid. Used by Dioxus and Bevy. |
179
+ | [flexbox.js](https://github.com/Planning-nl/flexbox.js) | JavaScript | Pure JS flexbox engine by Planning-nl. Reference implementation that inspired Flexily's algorithm. |
180
+ | [css-layout](https://www.npmjs.com/package/css-layout) | JavaScript | Facebook's original pure-JS flexbox, predecessor to Yoga. Deprecated. |
181
+ | [silvery](https://github.com/beorn/silvery) | TypeScript | React for CLIs with layout feedback. Uses Flexily by default. |
182
+
183
+ ## Code Structure
184
+
185
+ ```
186
+ src/
187
+ ├── index.ts # Main export
188
+ ├── node-zero.ts # Node class with FlexInfo
189
+ ├── layout-zero.ts # Layout algorithm (~2300 lines)
190
+ ├── constants.ts # Flexbox constants (Yoga-compatible)
191
+ ├── types.ts # TypeScript interfaces
192
+ ├── utils.ts # Shared utilities
193
+ └── classic/ # Allocating algorithm (for debugging)
194
+ ├── node.ts
195
+ └── layout.ts
196
+ ```
197
+
198
+ ## License
199
+
200
+ MIT
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Flexily Layout Algorithm
3
+ *
4
+ * Core flexbox layout computation extracted from node.ts.
5
+ * Based on Planning-nl/flexbox.js reference implementation.
6
+ */
7
+ import type { Node } from "./node.js";
8
+ import type { Value } from "../types.js";
9
+ /**
10
+ * Check if flex direction is row-oriented (horizontal main axis).
11
+ */
12
+ export declare function isRowDirection(flexDirection: number): boolean;
13
+ /**
14
+ * Check if flex direction is reversed.
15
+ */
16
+ export declare function isReverseDirection(flexDirection: number): boolean;
17
+ /**
18
+ * Resolve logical (START/END) margins/padding to physical values.
19
+ * EDGE_START/EDGE_END always resolve along the inline (horizontal) axis:
20
+ * - LTR: START→left, END→right
21
+ * - RTL: START→right, END→left
22
+ *
23
+ * Physical edges (LEFT/RIGHT/TOP/BOTTOM) are used directly.
24
+ * When both physical and logical are set, logical takes precedence.
25
+ */
26
+ export declare function resolveEdgeValue(arr: [Value, Value, Value, Value, Value, Value], physicalIndex: number, // 0=left, 1=top, 2=right, 3=bottom
27
+ flexDirection: number, availableSize: number, direction?: number): number;
28
+ /**
29
+ * Check if a logical edge margin is set to auto.
30
+ */
31
+ export declare function isEdgeAuto(arr: [Value, Value, Value, Value, Value, Value], physicalIndex: number, flexDirection: number, direction?: number): boolean;
32
+ /**
33
+ * Resolve logical (START/END) border widths to physical values.
34
+ * Border values are plain numbers (always points), so resolution is simpler
35
+ * than for margin/padding. Uses NaN as the "not set" sentinel for logical slots.
36
+ * When both physical and logical are set, logical takes precedence.
37
+ */
38
+ export declare function resolveEdgeBorderValue(arr: [number, number, number, number, number, number], physicalIndex: number, // 0=left, 1=top, 2=right, 3=bottom
39
+ _flexDirection: number, direction?: number): number;
40
+ export declare function markSubtreeLayoutSeen(node: Node): void;
41
+ export declare function countNodes(node: Node): number;
42
+ /**
43
+ * Compute layout for a node tree.
44
+ *
45
+ * @param root - Root node of the tree
46
+ * @param availableWidth - Available width for layout
47
+ * @param availableHeight - Available height for layout
48
+ * @param direction - Text direction (LTR or RTL), affects horizontal edge resolution
49
+ */
50
+ export declare function computeLayout(root: Node, availableWidth: number, availableHeight: number, direction?: number): void;
51
+ export declare let layoutNodeCalls: number;
52
+ export declare let resolveEdgeCalls: number;
53
+ export declare let layoutSizingCalls: number;
54
+ export declare let layoutPositioningCalls: number;
55
+ export declare let layoutCacheHits: number;
56
+ export declare function resetLayoutStats(): void;
57
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/classic/layout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAQxC;;GAEG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAEjE;AA4BD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAC/C,aAAa,EAAE,MAAM,EAAE,mCAAmC;AAC1D,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAwB,GAClC,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAC/C,aAAa,EAAE,MAAM,EACrB,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAwB,GAClC,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACrD,aAAa,EAAE,MAAM,EAAE,mCAAmC;AAC1D,cAAc,EAAE,MAAM,EACtB,SAAS,GAAE,MAAwB,GAClC,MAAM,CAaR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAKtD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAM7C;AA6OD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,EACV,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,EACvB,SAAS,GAAE,MAAwB,GAClC,IAAI,CAGN;AA81CD,eAAO,IAAI,eAAe,QAAI,CAAA;AAC9B,eAAO,IAAI,gBAAgB,QAAI,CAAA;AAC/B,eAAO,IAAI,iBAAiB,QAAI,CAAA;AAChC,eAAO,IAAI,sBAAsB,QAAI,CAAA;AACrC,eAAO,IAAI,eAAe,QAAI,CAAA;AAE9B,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC"}