You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(layout): phase 1 quick wins for incremental layout
* feat(layout): phase 2 layout tree caching
* feat(layout): phase 3 dirty-aware incremental layout
* fix(bench): preserve backend markers in latching wrapper
* feat(drawlist): generate v3 writers from spec with CI guardrails
* bench: finalize strict/full-ui harness and CI perf guardrails
* bench: add opentui-core as first-class framework + update benchmark docs
- Add `opentui-core` to Framework type so both OpenTUI React and Core
drivers run in a single benchmark invocation
- Fix PTY bytesProduced validation: fall back to PTY-observed bytes
when framework writes directly to fd (OpenTUI)
- Add periodic GC in OpenTUI startup loops to prevent OOM
- Update all 21 scenario files with opentui-core case
- Rewrite BENCHMARKS.md with full 8-framework results and methodology
- Update README.md Benchmarks section with cross-framework context
- Update docs/benchmarks.md with new reproduction commands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* style: fix biome format and import ordering
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(bench): honor --opentui-driver flag for opentui framework
The opentui framework entry now forwards the parsed opentuiDriver
option to both checkFramework() and the worker env, so
--framework opentui --opentui-driver core probes and runs the
correct backend. opentui-core continues to always use core.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+16Lines changed: 16 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -86,6 +86,21 @@ These boundaries are strict. Violating them breaks the runtime-agnostic guarante
86
86
-`@rezi-ui/node` imports from `@rezi-ui/core` only
87
87
-`@rezi-ui/jsx` imports from `@rezi-ui/core` only
88
88
89
+
### Drawlist writer codegen guardrail (MUST for ZRDL command changes)
90
+
91
+
The v3/v4/v5 command writer implementation is code-generated. Never hand-edit
92
+
`packages/core/src/drawlist/writers.gen.ts`.
93
+
94
+
When changing drawlist command layout/opcodes/field widths/offsets:
95
+
96
+
1. Update `scripts/drawlist-spec.ts` (single source of truth).
97
+
2. Regenerate writers: `npm run codegen`.
98
+
3. Verify sync guardrail: `npm run codegen:check`.
99
+
4. Update `packages/core/src/drawlist/__tests__/writers.gen.test.ts` if command bytes changed.
100
+
5. Update protocol docs (`docs/protocol/zrdl.md`, `docs/protocol/versioning.md`) in the same PR.
101
+
102
+
CI enforces this with `codegen:check`; stale generated writers are a hard failure.
103
+
89
104
## Testing Protocol
90
105
91
106
```bash
@@ -185,3 +200,4 @@ src/
185
200
7.**Skipping tests after pipeline changes.** Any change to commit, reconcile, layout, or renderer files requires running the full test suite. Subtle regressions are common.
186
201
8.**Breaking module boundaries.** Core must remain runtime-agnostic. Never add Node.js-specific imports (`Buffer`, `worker_threads`, `node:*`) to `@rezi-ui/core`.
187
202
9.**Misconfiguring box transitions.**`ui.box` transition defaults to animating `position`, `size`, and `opacity`; use explicit `properties` filters (or `[]` to disable) when behavior should be constrained.
203
+
10.**Editing generated drawlist writers by hand.** Update `scripts/drawlist-spec.ts` and run `npm run codegen` instead.
-`terminal-strict-ui-navigation` — same structured layout with navigation
43
+
44
+
The strict scenarios exist to reduce bias when comparing Rezi (which has a layout engine) against libraries that operate closer to raw terminal buffers (blessed, ratatui, terminal-kit). In primitive scenarios these libraries skip layout entirely, which flatters their numbers on simple workloads but does not reflect real application complexity.
45
+
46
+
## Results (2026-02-22, all frameworks, PTY mode)
47
+
48
+
Single-replicate, default iteration counts. WSL host (see caveats). These numbers are directional.
49
+
50
+
### Relative performance vs Rezi
51
+
52
+
Ratio >1 means the competitor is slower. Ratio <1 means the competitor is faster.
Rezi is faster than Ink in every measured scenario. The gap is largest on tree construction and rerender workloads (45-206x) and smallest on complex UI composition (10x).
77
+
78
+
**vs OpenTUI (React)** (21 scenarios):
79
+
80
+
Rezi faster in all 21 scenarios. Range: 1.8x to 155x. Geomean: ~10x.
81
+
82
+
**vs OpenTUI (Core)** (21 scenarios):
83
+
84
+
Rezi faster in 19/21 scenarios. Range: 1.3x to 13x. Geomean: ~2.6x.
85
+
86
+
OpenTUI Core wins:
87
+
-`layout-stress`: Core 1.5x faster (517 vs 347 ops/s)
88
+
-`tables`: Core 1.6x faster (434 vs 277 ops/s)
89
+
90
+
**vs Bubble Tea** (21 scenarios):
91
+
92
+
Rezi faster in 20/21 scenarios. Bubble Tea throughput clusters around ~120 ops/s in most scenarios due to its 8.33ms tick rate. Rezi wins are large where it exceeds this ceiling.
93
+
94
+
Bubble Tea wins:
95
+
-`scroll-stress`: Bubble Tea 2.5x faster (120 vs 48 ops/s)
96
+
97
+
**vs terminal-kit / blessed / Ratatui** (subset of scenarios):
98
+
99
+
These three participate only in scenarios that make sense for their level of abstraction. terminal-kit and blessed are low-level buffer libraries without layout engines or widget systems. Ratatui is a native Rust renderer.
100
+
101
+
On **primitive** workloads (rerender, frame-fill, simple construction):
102
+
- terminal-kit: 1x-13x faster than Rezi
103
+
- blessed: 2x-19x faster than Rezi
104
+
- Ratatui: 2x-20x faster than Rezi
105
+
106
+
On **complex UI** workloads (strict-ui, full-ui, fps-stream, tables, virtual-list):
107
+
- Rezi is competitive or faster, because these scenarios exercise layout and composition which the primitive libraries lack
108
+
109
+
This is expected. terminal-kit and blessed write directly to terminal buffers without computing layout. Ratatui is compiled Rust. On workloads that actually require structured layout and widget composition, the gap narrows or reverses.
110
+
111
+
### Representative numbers
112
+
113
+
Selected scenarios showing the performance landscape across categories:
OpenTUI has two rendering paths. The React driver (`@opentui/react`) uses React reconciliation with `flushSync()`. The Core driver (`@opentui/core`) uses direct imperative object mutation.
141
+
142
+
Core is faster than React in all 21 scenarios. Geomean speedup: ~4x. The difference is most dramatic on memory-intensive workloads:
143
+
- content-update: Core 96 ops/s vs React 4 ops/s (24x)
- Peak RSS: Core ~180 MB vs React ~15 GB on content-update
146
+
147
+
Both drivers are benchmarked as separate framework entries (`opentui` and `opentui-core`).
148
+
149
+
## Fairness notes
150
+
151
+
### What is measured
152
+
153
+
Each benchmark iteration measures one complete render cycle: state update through final frame output. For PTY-mode benchmarks, this includes the terminal write path.
154
+
155
+
### Runtime differences
156
+
157
+
These benchmarks compare frameworks, but also inevitably compare runtimes:
158
+
- Rezi, Ink, blessed: Node.js worker processes
159
+
- OpenTUI (both drivers): Bun subprocesses
160
+
- Bubble Tea: Go subprocess
161
+
- Ratatui: Rust subprocess
162
+
163
+
Cross-framework numbers include runtime and toolchain effects alongside framework costs.
164
+
165
+
### Scenario parity
166
+
167
+
- Primitive scenarios (rerender, frame-fill) test narrow subsystem performance. Libraries without layout engines (terminal-kit, blessed, Ratatui) have an inherent advantage here because they do less work per frame.
168
+
- Strict-ui scenarios build equivalent multi-panel layouts across all frameworks. Bubble Tea renders via lipgloss string composition rather than a widget tree, which is a different code path than the others.
169
+
- Full-ui scenarios test composite dashboards with data sections, status bars, and navigation.
170
+
171
+
### Byte metrics
172
+
173
+
`bytesProduced` is framework-local and not directly comparable across frameworks. In PTY mode, `ptyBytesObserved` provides a cross-framework terminal I/O comparison.
174
+
175
+
### Environment
176
+
177
+
WSL and virtualized hosts introduce measurable scheduler jitter. Treat WSL-collected results as directional. For publication-grade numbers, use bare-metal Linux with CPU pinning and multiple replicates.
- OpenTUI Core vs OpenTUI React (same scenario set):
44
-
- Core is faster in `21/21` cases
45
-
- Geomean core/react speedup: `~4.0x`
46
-
47
-
## Fairness Notes
48
-
49
-
- OpenTUI now has explicit driver selection in the harness:
50
-
-`--opentui-driver react` (default)
51
-
-`--opentui-driver core` (imperative)
52
-
- Bubble Tea is executed through `packages/bench/bubbletea-bench/main.go` using Bubble Tea's normal program loop and terminal renderer.
53
-
- In these runs, Bubble Tea throughput clusters around ~120 fps-equivalent in many scenarios. This behavior is visible directly in per-scenario ops/s and should be considered when interpreting throughput-style loops.
54
-
- PTY mode (`--io pty`) is required for OpenTUI and Bubble Tea measurements in this suite.
0 commit comments