Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion tui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,27 @@ minreq = "3"
# these are `[dependencies]`, not `[dev-dependencies]`, because the pane is production code.
# That is unavoidable for a TUI and is the reason the feature set is narrowed rather than
# taken as it comes. (#321)
ratatui = { version = "0.30.2", default-features = false, features = ["std", "crossterm"] }
#
# `unstable-rendered-line-info` is added for `Paragraph::line_count`, which the left column's
# viewport needs to know how tall its content is ONCE WRAPPED. It adds **zero crates**
# (`Cargo.lock` is byte-identical with and without it — verified) and compiles on stable rustc:
# ratatui gates the API behind an attribute, not behind a nightly feature.
#
# The alternative was to compute wrapped height here, and that is the worse risk: it would be a
# second implementation of word wrapping that agrees with the one actually rendering only until a
# line holds a long word or a wide grapheme. Measured at the 80x24 floor, 32 lines occupy 44 rows,
# so the naive `Vec::len()` count understates height by a third and an overflow guard built on it
# passes while content runs off screen.
#
# The exposure is that the API is unstable upstream (ratatui#293) and could change or vanish in a
# minor release. `wrapped_heights_agrees_with_what_is_actually_drawn` pins the behaviour against a
# real rendered buffer rather than against the API's own claim, so an upstream change surfaces as a
# red test rather than as a silently mis-sized viewport. (PR #564 review)
ratatui = { version = "0.30.2", default-features = false, features = [
"std",
"crossterm",
"unstable-rendered-line-info",
] }
crossterm = "0.29.0"

# ---------------------------------------------------------------------------------------
Expand Down
23 changes: 23 additions & 0 deletions tui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ mod results_pane;
/// Six methods, six error variants, the 21-route table, and no subprocess anywhere (ADR-02).
/// (#321)
mod server;
/// The semantic colour layer (#556): six roles, an ANSI-16 palette, and `NO_COLOR`. **The only
/// module permitted to name a `Color`** — every other module says `theme.error`, and
/// `tests/no_colour_literal_outside_theme.rs` makes that a failing test rather than a convention.
/// Colour is decoration only: every state this crate shows is already textual, which is NFR-3 of
/// #321 and the reason `NO_COLOR` costs nothing to honour. (#556)
mod theme;
/// The wire vocabulary six later units share. Declared here so every consumer imports the
/// types from one place rather than redeclaring the server's shapes locally. (#321)
mod types;
Expand Down Expand Up @@ -255,6 +261,12 @@ fn run_app() -> Result<(), TuiError> {
let mut shell = renderer::Renderer::new(server.as_ref(), &host, cols, rows)
.with_concurrent_pickers(Arc::clone(&server));

// `NO_COLOR` is read exactly ONCE, here, and the resolved palette is threaded down (#556).
// Re-reading it per frame would let a mid-session change produce a half-coloured screen, and
// reading it deeper in the call tree would make every unit test's output depend on the ambient
// environment. This is the only `from_env` call in the crate.
shell.set_theme(theme::Theme::from_env());

// A `Fatal` here exits non-zero with one styled line — never a traceback (SR-1). Mapped into
// `TuiError` because this function's signature is the boundary contract, and `Fatal`'s own
// `Display` already carries the whole operator-facing sentence.
Expand All @@ -268,6 +280,17 @@ fn run_app() -> Result<(), TuiError> {
if !interactive {
let frame = shell.render();
let mut out = io::stdout().lock();
// `{line}` on a `Line` writes its spans' content and **no SGR codes** — checked in
// ratatui-core 0.1.2, `Span`'s `Display` is a plain `write!` of `content`. That is what
// keeps a pipe free of escapes now that these are styled values (SR-1); it is relied upon
// here rather than merely true, so it is written down. Guarded by
// `renderer::tests::a_styled_line_displays_without_escape_codes` — nothing asserted this
// from a real piped process, and a dependency on an upstream `Display` impl with no test
// behind it is what silently breaks on a minor-version bump.
//
// Still header+footer only, deliberately: this is the pipe frame from #321, and widening it
// to `plain_lines()` would change what `cao-tui | ...` prints under cover of a colour
// change. (#556)
for line in frame.header.iter().chain(&frame.footer) {
writeln!(out, "{line}")?;
}
Expand Down
Loading
Loading