flexily 0.5.0 → 0.5.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.
- package/README.md +25 -21
- package/package.json +11 -2
- package/src/logger.ts +5 -4
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ root.setWidth(80)
|
|
|
16
16
|
root.setFlexDirection(FLEX_DIRECTION_ROW)
|
|
17
17
|
|
|
18
18
|
const label = flex.createNode()
|
|
19
|
-
label.setTextContent("Hello")
|
|
19
|
+
label.setTextContent("Hello") // auto-measured: 5 wide
|
|
20
20
|
|
|
21
21
|
const content = flex.createNode()
|
|
22
22
|
content.setFlexGrow(1)
|
|
@@ -106,6 +106,7 @@ const flex = pipe(createBareFlexily(), withTestMeasurer())
|
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Text measurement backends:
|
|
109
|
+
|
|
109
110
|
- **`withMonospace()`** — terminal grids (1 char = 1 cell), default
|
|
110
111
|
- **`withTestMeasurer()`** — deterministic widths for CI (Latin 0.8, CJK 1.0, emoji 1.8)
|
|
111
112
|
- **`withPretext(pretext)`** — proportional fonts via [Pretext](https://github.com/chenglou/pretext)
|
|
@@ -136,7 +137,7 @@ Text measurement backends:
|
|
|
136
137
|
npm install flexily
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
**Runtimes:** Bun >= 1.0, Node.js >=
|
|
140
|
+
**Runtimes:** Bun >= 1.0, Node.js >= 23.6. Pure JavaScript — no native or WASM dependencies.
|
|
140
141
|
|
|
141
142
|
## Performance
|
|
142
143
|
|
|
@@ -149,7 +150,7 @@ Flexily and Yoga each win in different scenarios:
|
|
|
149
150
|
| **Single dirty leaf** | Yoga | 2.8-3.4x | 406-969 nodes |
|
|
150
151
|
| **Deep nesting (15+)** | Yoga | increasing | 1 node per level |
|
|
151
152
|
|
|
152
|
-
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.
|
|
153
|
+
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/guide/performance.md](docs/guide/performance.md) for full methodology.
|
|
153
154
|
|
|
154
155
|
**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.
|
|
155
156
|
|
|
@@ -166,7 +167,7 @@ Flexily's fingerprint cache makes no-change re-layout essentially free (27ns reg
|
|
|
166
167
|
|
|
167
168
|
**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.
|
|
168
169
|
|
|
169
|
-
See [docs/performance.md](docs/performance.md) for detailed benchmarks including TUI-realistic trees with measure functions.
|
|
170
|
+
See [docs/guide/performance.md](docs/guide/performance.md) for detailed benchmarks including TUI-realistic trees with measure functions.
|
|
170
171
|
|
|
171
172
|
## Algorithm
|
|
172
173
|
|
|
@@ -195,7 +196,7 @@ Incremental re-layout (caching unchanged subtrees) is essential for performance
|
|
|
195
196
|
|
|
196
197
|
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.
|
|
197
198
|
|
|
198
|
-
See [docs/testing.md](docs/testing.md) for methodology and [docs/incremental-layout-bugs.md](docs/incremental-layout-bugs.md) for the bug taxonomy.
|
|
199
|
+
See [docs/guide/testing.md](docs/guide/testing.md) for methodology and [docs/guide/incremental-layout-bugs.md](docs/guide/incremental-layout-bugs.md) for the bug taxonomy.
|
|
199
200
|
|
|
200
201
|
## Bundle Size
|
|
201
202
|
|
|
@@ -225,15 +226,15 @@ Same constants, same method names, same behavior.
|
|
|
225
226
|
|
|
226
227
|
## Documentation
|
|
227
228
|
|
|
228
|
-
| Document
|
|
229
|
-
|
|
|
230
|
-
| [Getting Started](docs/getting-started.md) | Quick guide to building layouts |
|
|
231
|
-
| [API Reference](docs/api.md)
|
|
232
|
-
| [Algorithm](docs/algorithm.md) | How the layout algorithm works |
|
|
233
|
-
| [Performance](docs/performance.md) | Benchmarks and methodology |
|
|
234
|
-
| [Yoga Comparison](docs/yoga-comparison.md) | Feature comparison with Yoga |
|
|
235
|
-
| [Testing](docs/testing.md) | Test infrastructure and methodology |
|
|
236
|
-
| [Incremental Layout Bugs](docs/incremental-layout-bugs.md) | Bug taxonomy and debugging guide |
|
|
229
|
+
| Document | Description |
|
|
230
|
+
| ---------------------------------------------------------------- | ----------------------------------- |
|
|
231
|
+
| [Getting Started](docs/guide/getting-started.md) | Quick guide to building layouts |
|
|
232
|
+
| [API Reference](docs/api/reference.md) | Complete API documentation |
|
|
233
|
+
| [Algorithm](docs/guide/algorithm.md) | How the layout algorithm works |
|
|
234
|
+
| [Performance](docs/guide/performance.md) | Benchmarks and methodology |
|
|
235
|
+
| [Yoga Comparison](docs/guide/yoga-comparison.md) | Feature comparison with Yoga |
|
|
236
|
+
| [Testing](docs/guide/testing.md) | Test infrastructure and methodology |
|
|
237
|
+
| [Incremental Layout Bugs](docs/guide/incremental-layout-bugs.md) | Bug taxonomy and debugging guide |
|
|
237
238
|
|
|
238
239
|
## Related Projects
|
|
239
240
|
|
|
@@ -249,14 +250,17 @@ Same constants, same method names, same behavior.
|
|
|
249
250
|
|
|
250
251
|
```
|
|
251
252
|
src/
|
|
252
|
-
├── index.ts # Main export (everything)
|
|
253
|
-
├── create-flexily.ts # createFlexily, createBareFlexily, pipe, FlexilyNode
|
|
253
|
+
├── index.ts # Main export (everything: createFlexily, Node, constants, plugins)
|
|
254
|
+
├── create-flexily.ts # createFlexily, createBareFlexily, pipe, FlexilyNode mixin
|
|
254
255
|
├── text-layout.ts # TextLayoutService, PreparedText interfaces
|
|
255
|
-
├── monospace-measurer.ts #
|
|
256
|
-
├── test-measurer.ts # Deterministic test measurer
|
|
257
|
-
├── pretext-measurer.ts #
|
|
258
|
-
├── node-zero.ts # Node class with FlexInfo
|
|
259
|
-
├── layout-zero.ts #
|
|
256
|
+
├── monospace-measurer.ts # Monospace text measurement (terminal: 1 char = 1 cell)
|
|
257
|
+
├── test-measurer.ts # Deterministic test measurer (Latin 0.8, CJK 1.0, emoji 1.8)
|
|
258
|
+
├── pretext-measurer.ts # Pretext proportional text plugin (peer dep)
|
|
259
|
+
├── node-zero.ts # Node class with FlexInfo (hot path)
|
|
260
|
+
├── layout-zero.ts # Core layout: computeLayout + layoutNode (hot path)
|
|
261
|
+
├── layout-helpers.ts # Edge resolution: margins, padding, borders (hot path)
|
|
262
|
+
├── layout-flex-lines.ts # Pre-alloc arrays, line breaking, flex distribution (hot path)
|
|
263
|
+
├── layout-measure.ts # measureNode — intrinsic sizing (hot path)
|
|
260
264
|
├── constants.ts # Flexbox constants (Yoga-compatible)
|
|
261
265
|
├── types.ts # TypeScript interfaces
|
|
262
266
|
├── utils.ts # Shared utilities
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flexily",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Pure JavaScript flexbox layout engine — composable plugins, text measurement, Yoga-compatible API, no WASM",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas-ui",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"yoga-wasm",
|
|
18
18
|
"zero-dependency"
|
|
19
19
|
],
|
|
20
|
-
"homepage": "https://beorn.
|
|
20
|
+
"homepage": "https://beorn.codes/flexily/",
|
|
21
21
|
"bugs": {
|
|
22
22
|
"url": "https://github.com/beorn/flexily/issues"
|
|
23
23
|
},
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"./classic": {
|
|
42
42
|
"types": "./src/classic/index.ts",
|
|
43
43
|
"import": "./src/classic/index.ts"
|
|
44
|
+
},
|
|
45
|
+
"./testing": {
|
|
46
|
+
"types": "./src/testing.ts",
|
|
47
|
+
"import": "./src/testing.ts"
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"publishConfig": {
|
|
@@ -58,12 +62,17 @@
|
|
|
58
62
|
"docs:preview": "vitepress preview docs"
|
|
59
63
|
},
|
|
60
64
|
"devDependencies": {
|
|
65
|
+
"@types/node": "^25.5.0",
|
|
66
|
+
"loggily": "^0.4.2",
|
|
61
67
|
"typescript": "^5.9.3",
|
|
62
68
|
"vitepress": "^1.6.3",
|
|
69
|
+
"vitepress-enrich": "^0.4.0",
|
|
70
|
+
"vitepress-plugin-llms": "^1.0.0",
|
|
63
71
|
"vitest": "^3.1.0",
|
|
64
72
|
"yoga-wasm-web": "^0.3.3"
|
|
65
73
|
},
|
|
66
74
|
"engines": {
|
|
75
|
+
"bun": ">=1.0",
|
|
67
76
|
"node": ">=23.6.0"
|
|
68
77
|
}
|
|
69
78
|
}
|
package/src/logger.ts
CHANGED
|
@@ -14,11 +14,12 @@ interface ConditionalLogger {
|
|
|
14
14
|
|
|
15
15
|
let _logger: ConditionalLogger | null = null
|
|
16
16
|
|
|
17
|
-
function createFallbackLogger(namespace: string): ConditionalLogger {
|
|
18
|
-
// Dynamic
|
|
17
|
+
async function createFallbackLogger(namespace: string): Promise<ConditionalLogger> {
|
|
18
|
+
// Dynamic import to avoid bundling debug if not needed
|
|
19
19
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const { default: createDebug } = (await import("debug")) as {
|
|
21
|
+
default: (ns: string) => DebugFn & { enabled: boolean }
|
|
22
|
+
}
|
|
22
23
|
const debug = createDebug(namespace)
|
|
23
24
|
return { debug: debug.enabled ? debug : undefined }
|
|
24
25
|
} catch {
|