Stack Layers
| Layer | Responsibilities | Does NOT own |
|---|---|---|
| @rezi-ui/core | Widget tree, layout engine, themes, keybindings, forms, drawlist encoding, event parsing | Terminal I/O, threads, OS APIs |
| @rezi-ui/node | Worker thread lifecycle, frame scheduling, buffer transport, execution mode selection | Widget logic, layout math |
| @rezi-ui/native | N-API binding, Zireael engine lifecycle, SharedArrayBuffer interop | Protocol semantics |
| Zireael (C) | Framebuffer management, diff rendering, ANSI emission, terminal capability detection, platform I/O | Widget definitions, layout, themes |
Data Flow
A single frame follows this path through the system:Binary Protocol Boundary
The native engine communicates exclusively through two binary formats:- ZRDL (drawlists): rendering commands flowing down from TypeScript to the engine
- ZREV (event batches): input events flowing up from the engine to TypeScript
- Little-endian
- 4-byte aligned
- Versioned with magic bytes (
ZRDL=0x4C44525A,ZREV=0x5645525A) - Strictly validated at both ends
Design Constraints
No Node.js APIs in core.@rezi-ui/core must remain runtime-agnostic. It contains no Buffer, worker_threads, fs, or node:* imports. This is enforced by CI.
Binary boundary for safety. All data crossing the native boundary goes through versioned binary formats with strict validation. No raw pointers or shared mutable state cross the TypeScript/C boundary.
Deterministic rendering. Same initial state + same event sequence = same frames. This is achieved through:
- Pinned Unicode tables (v15.1.0)
- Versioned protocols
- Strict commit-point semantics
- Deterministic layout algorithms
engine_present() writes exactly one chunk to the terminal on success, zero on failure. No partial ANSI sequences reach the terminal.
Comparison with Other Frameworks
vs Ink (React)
| Aspect | Rezi | Ink |
|---|---|---|
| Reconciliation | Custom VNode reconciler | React reconciler + Yoga |
| Layout | Native flexbox + grid engine | Yoga (C++ binding) |
| Rendering | Binary drawlist → native C engine | String ANSI building in JS |
| Performance | 10-206x faster (see benchmarks) | Baseline |
| Memory | 80-210 MB typical | 120-980 MB typical |
vs OpenTUI
| Aspect | Rezi | OpenTUI |
|---|---|---|
| Rendering | Binary protocol + native engine | Bun-native rendering |
| Layout | Full constraint solver | Box model |
| React path | Optional JSX layer | Required for React driver |
| Performance | 2-155x faster than React driver | Baseline |
vs Bubble Tea (Go)
| Aspect | Rezi | Bubble Tea |
|---|---|---|
| Architecture | Elm-inspired state management | Elm architecture |
| Rendering | Binary drawlist | String View() functions |
| Runtime | TypeScript/Node.js | Go |
| Memory | 80-210 MB | 7-10 MB (Go runtime) |
vs terminal-kit / blessed / Ratatui
These are lower-level libraries that operate closer to raw terminal buffers:- terminal-kit / blessed: Node.js terminal buffer libraries without structured layout engines
- Ratatui: Native Rust immediate-mode renderer
Module Boundaries
These boundaries are strict and enforced:@rezi-ui/coreMUST NOT import from@rezi-ui/node,@rezi-ui/jsx, or@rezi-ui/native@rezi-ui/nodeimports from@rezi-ui/coreonly@rezi-ui/jsximports from@rezi-ui/coreonly
Widget Protocol Registry
Widget capability detection is centralized inpackages/core/src/widgets/protocol.ts. The render/runtime pipeline uses protocol lookups for:
- Interactive/focusable/pressable detection
- Commit behavior
- Focus metadata
- Hit-testing
- Event routing
Related Documentation
- Render Pipeline — State → VNode → layout → drawlist flow
- Layout Engine — Constraint solving and intrinsic sizing
- Event System — Event routing and dispatch
- Protocol Overview — Binary format specs
- Node.js Backend — Backend lifecycle
- Worker Model — Thread ownership