feat(rust): ratatui renderer for SceneFrame, alt-screen TUI binary#959
Merged
Conversation
First visible Rust output. reasonix-render now reads JSONL frames from stdin, sets up an alt-screen TUI, and renders each frame through a recursive walker that paints into ratatui's Buffer. Layout model: - Text nodes paint runs left-to-right; styles map to ratatui's fg/bg + Modifier bits. Overflow truncates at the right edge of the area (single-line for now; wrap modes come when we hit a real wrapped Text node). - Box nodes apply paddingX/paddingY symmetrically, then split the inner rect by direction. Column direction stacks children vertically using each child's intrinsic height (1 for Text, sum of children + gap + 2·paddingY for Box). Row direction lays out horizontally with intrinsic width. - Gap is added between siblings, not after the last one. - Hex colors round-trip to ratatui::Color::Rgb (truecolor); named colors map 1:1; "default" → Reset. Binary controls: - Frames advance on 'n' / Right-arrow or after the 800ms dwell. - 'p' / Left-arrow goes back. - 'q' / Esc exits cleanly (raw mode disabled, alt screen left, cursor shown) even on panic-free error paths. Tests: - 7 render tests in tests/render.rs covering plain text, styled runs, column stacking with gap, row layout, padding offset, text overflow truncation, and hex truecolor. - Existing 6 round-trip tests still pass. - Buffer assertions use buf[(x,y)].symbol() / .style() so the tests fail at the cell level if styling regresses, not just on visual diffs. Deps added: ratatui 0.29, crossterm 0.28, anyhow 1. These are the canonical TUI dependency triple and we will live with them through Stage 3. Refs #868 #947
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First visible Rust output. `reasonix-render` now reads JSONL frames
from stdin, sets up an alt-screen TUI, and renders each frame through
a recursive walker that paints into ratatui's `Buffer`. The trace
files written by `REASONIX_SCENE_TRACE` (#952, #958) are now
viewable end-to-end.
How to try
```
REASONIX_SCENE_TRACE=/tmp/frames.jsonl reasonix # run a session, hit a few keys
cargo run -q --bin reasonix-render < /tmp/frames.jsonl
```
`n` / Right advances, `p` / Left goes back, `q` / Esc exits. Frames
also auto-advance after an 800ms dwell.
Layout model
truncates at the right edge. No wrap yet — the `Wrap` enum is
in the contract but the renderer ignores it; that lands when we
hit a real wrapped Text node.
`direction`. Column stacks vertically with children's intrinsic
heights (1 for Text, recursive for nested Box + gap + padding).
Row lays out horizontally with intrinsic widths.
→ `Color::Reset`.
Tests
7 new cell-level assertions in `tests/render.rs`:
Existing 6 round-trip tests still pass.
What's deferred
treats every text run as single-line and truncating. First real
wrapped frame from the producer forces a renderer extension.
on Box don't influence layout yet. Today the layout uses intrinsic
sizes. Comes when a producer needs to pin a region.
fine; renderer ignores them. Same rule.
Each deferred field is in the contract and decodes cleanly — the
renderer just chooses not to use them yet. No silent drops; future
extensions are additive.
Refs #868 #947