ansi-to-tui
Convert ANSI SGR escape codes in raw bytes into styled Ratatui Text, ready to render in your TUI.
Repository Health
Technical Analysis
ansi-to-tui is a Rust crate that parses bytes containing ANSI SGR escape sequences (like \x1b[31m) and produces a Ratatui Text value with equivalent foreground/background colors and style modifiers. It exists so terminal-colored output captured from subprocesses, log files, or CI tooling can be dropped straight into a Ratatui-based TUI without hand-rolling an ANSI parser or pre-cleaning the input first.
The crate supports named 3/4-bit colors, 8-bit indexed colors, and 24-bit truecolor, plus common SGR modifiers (bold, italic, underline, strikethrough, blink, reverse, conceal). Unknown or malformed escape sequences are ignored rather than causing a hard failure, so real-world terminal output that doesn’t perfectly conform to the spec still parses cleanly.
What You Get
- A single
IntoTexttrait, blanket-implemented for anyAsRef<[u8]>type (String, &str, Vec<u8>, &[u8]), exposing.into_text()for an ownedText<'static> - An optional
.to_text()zero-copy method (behind the default-onzero-copyfeature) that borrows spans from the input instead of allocating - Support for named/indexed/truecolor ANSI color codes and SGR style modifiers (bold, italic, underline, strikethrough, blink, reverse, conceal), mapped onto Ratatui’s
Color,Style, andModifiertypes - Optional SIMD-accelerated UTF-8 validation via the
simdfeature (using thesimdutf8crate), with a safe fallback toString::from_utf8when disabled - Lenient parsing that silently ignores unrecognized or malformed escape sequences instead of erroring out on real-world terminal output
Common Use Cases
- Rendering
cargo build,docker, or other CLI subprocess output with its original ANSI coloring inside a Ratatui TUI panel - Displaying colorized log files or CI output logs in a terminal dashboard without losing their original styling
- Building terminal pagers, log viewers, or
tail -f-style widgets that need to preserve upstream color codes
Under The Hood
Architecture — The public surface is a single IntoText trait defined in src/lib.rs, blanket-implemented for any T: AsRef<[u8]>. into_text() delegates to parser::text() for an owned, always-available Text<'static>; the feature-gated to_text() delegates to parser::text_fast() for a zero-copy Text<'_> borrowing from the input. All real parsing work lives in src/parser.rs (~400 lines), built on the nom parser-combinator crate: it walks the byte stream recognizing CSI/SGR escape sequences, converts each into an AnsiCode variant (defined in src/code.rs, carrying an optional Color), and folds a smallvec-backed sequence of these AnsiItems into a ratatui_core::style::Style via an AnsiStates -> Style conversion that walks each code (bold, faint, colors, resets, etc.) and applies or removes the corresponding Modifier/Color. All error paths funnel into one Error enum (src/error.rs) via thiserror, unifying nom parse failures and UTF-8 decode failures. Tech Stack — Rust 2024 edition, MSRV 1.86. Core dependencies are minimal and purposeful: nom 8 for the parser combinators, ratatui-core 0.1 with default-features = false (kept lean for embedding), smallvec 1 with const generics to avoid heap allocation for the common few-item style sequence, and thiserror 2 for the error enum. simdutf8 is optional (simd feature, on by default) for faster UTF-8 validation. Dev-dependencies (anyhow, eyre, criterion, pretty_assertions) support testing and a Criterion benchmark (benches/parsing); a Nix flake pins the dev environment, and CI runs via GitHub Actions with Codecov coverage reporting. Code Quality — src/tests.rs (548 lines, larger than the parser it tests) exercises plain text, individual and combined SGR codes, all three color modes, and edge cases such as empty escape sequences (\x1b[m) treated as a reset, referencing the upstream GitHub issue that motivated the fix directly in a code comment. Tests use pretty_assertions for readable diff output and a shared test_both helper to assert into_text/to_text parity. No unsafe code appears anywhere in src/. The crate enables #![warn(missing_docs)], enforcing doc comments on every public item. API Design — The public API is intentionally tiny: one trait, two methods, both callable with zero setup on any byte-like type via the blanket AsRef<[u8]> impl (bytes.into_text()?). Every public item carries a doc comment, and src/lib.rs embeds runnable doctests that double as the crate’s own usage examples. Feature flags (simd, zero-copy) are additive performance opt-outs, not prerequisites — the crate works with zero configuration out of the box.