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.
Files changed (3) hide show
  1. package/README.md +25 -21
  2. package/package.json +11 -2
  3. 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") // auto-measured: 5 wide
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 >= 18. Pure JavaScript — no native or WASM dependencies.
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 | Description |
229
- | ---------------------------------------------------------- | ----------------------------------- |
230
- | [Getting Started](docs/getting-started.md) | Quick guide to building layouts |
231
- | [API Reference](docs/api.md) | Complete API documentation |
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 # Terminal text measurement (1 char = 1 cell)
256
- ├── test-measurer.ts # Deterministic test measurer
257
- ├── pretext-measurer.ts # Proportional font measurement (peer dep)
258
- ├── node-zero.ts # Node class with FlexInfo
259
- ├── layout-zero.ts # Layout algorithm (~2000 lines)
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.0",
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.github.io/flexily/",
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 require to avoid bundling debug if not needed
17
+ async function createFallbackLogger(namespace: string): Promise<ConditionalLogger> {
18
+ // Dynamic import to avoid bundling debug if not needed
19
19
  try {
20
- // eslint-disable-next-line @typescript-eslint/no-require-imports
21
- const createDebug = require("debug") as (ns: string) => DebugFn & { enabled: boolean }
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 {