resq-tui
Shared TUI component library for all ResQ developer tools. Provides a unified theme system, console formatters, table rendering, progress bars, spinners, and terminal lifecycle management built on Ratatui and Crossterm.
Overview
resq-tui ensures every ResQ tool (resq-logs, resq-perf, resq-flame, resq-health, etc.) shares a consistent visual identity and interaction model. It provides two tiers of output:
- Full-screen TUI -- Ratatui-based widgets (header, footer, tabs, popups) with a standardized theme for interactive terminal applications.
- Non-TUI CLI -- Styled console formatters, tables, progress bars, and spinners for traditional command-line output that gracefully degrade when piped or redirected.
All styling is gated through environment detection so ANSI codes never bleed into pipes, redirects, or screen-reader environments.
Architecture
graph TD
subgraph resq-tui
LIB["lib.rs<br/><i>Theme, Widgets, Utilities</i>"]
THEME["theme.rs<br/><i>AdaptiveColor, Palette</i>"]
DETECT["detect.rs<br/><i>TTY, Color Mode, Accessibility</i>"]
CONSOLE["console.rs<br/><i>Styled Message Formatters</i>"]
TABLE["table.rs<br/><i>CLI Table Renderer</i>"]
PROGRESS["progress.rs<br/><i>CLI Progress Bar</i>"]
SPINNER["spinner.rs<br/><i>Threaded CLI Spinner</i>"]
TERMINAL["terminal.rs<br/><i>Init, Restore, Event Loop</i>"]
end
THEME --> DETECT
CONSOLE --> DETECT
CONSOLE --> THEME
TABLE --> DETECT
TABLE --> THEME
PROGRESS --> DETECT
PROGRESS --> THEME
SPINNER --> DETECT
LIB --> THEME
subgraph Consumers
LOGS["resq-logs"]
PERF["resq-perf"]
FLAME["resq-flame"]
HEALTH["resq-health"]
BIN["resq-bin"]
CLEAN["resq-clean"]
end
LOGS --> LIB
PERF --> LIB
FLAME --> LIB
HEALTH --> LIB
BIN --> LIB
CLEAN --> LIB
Module dependency flow
flowchart LR
detect.rs -->|ColorMode| theme.rs
theme.rs -->|AdaptiveColor| console.rs
theme.rs -->|AdaptiveColor| table.rs
theme.rs -->|AdaptiveColor| progress.rs
detect.rs -->|should_style| console.rs
detect.rs -->|should_style| table.rs
detect.rs -->|should_style| progress.rs
detect.rs -->|should_style / is_tty| spinner.rs
lib.rs -->|re-exports| theme.rs
terminal.rs -->|crossterm + ratatui| lib.rs
Installation
Add to your Cargo.toml:
[]
= { = true }
Or from crates.io:
[]
= "0.1.4"
Module Reference
lib.rs -- Core Widgets and Utilities
The root module re-exports crossterm and ratatui for convenience and provides the original Theme struct alongside shared TUI drawing functions.
Theme (root)
The original hardcoded dark-palette theme struct, retained for backward compatibility. For new code, prefer theme::Theme::adaptive() (see below).
| Field | Type | Default | Description |
|---|---|---|---|
primary |
Color |
Cyan |
Primary brand color |
secondary |
Color |
Blue |
Secondary supporting color |
accent |
Color |
Magenta |
Metadata accent |
success |
Color |
Green |
Success state |
warning |
Color |
Yellow |
Warning / pending state |
error |
Color |
Red |
Error / critical state |
bg |
Color |
Black |
Background |
fg |
Color |
White |
Foreground text |
highlight |
Color |
Rgb(50,50,50) |
Selection highlight |
inactive |
Color |
DarkGray |
Muted / inactive elements |
Widget Functions
draw_header
Renders a standardized header bar with service name, status badge, PID, and URL.
use ;
Signature:
draw_footer
Renders a keyboard-shortcut footer bar.
draw_footer;
Signature:
draw_tabs
Renders a tab bar with selection highlight. Uses the default theme internally.
draw_tabs;
Signature:
draw_popup
Renders a centered modal overlay for help dialogs or error messages.
use Line;
draw_popup;
Signature:
centered_rect
Helper that computes a centered Rect given percentage dimensions. Used internally by draw_popup.
let popup_area = centered_rect;
Utility Functions
format_bytes
Converts a byte count to a human-readable string using binary units (KiB, MiB, GiB).
use format_bytes;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
format_duration
Converts seconds to a human-readable duration string.
use format_duration;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
SPINNER_FRAMES
Braille animation frames for TUI spinner widgets:
pub const SPINNER_FRAMES: & = &;
theme -- Adaptive Color System
The theme module provides a Dracula-inspired adaptive color palette that switches between light and dark variants based on terminal environment detection.
AdaptiveColor
A color pair with light and dark variants that resolves at runtime via detect_color_mode().
use ;
use Color;
let resolved: Color = COLOR_PRIMARY.resolve;
| Method | Returns | Description |
|---|---|---|
resolve() |
Color |
Returns the appropriate variant for the terminal |
Palette Constants
| Constant | Dark (Dracula) | Light | Usage |
|---|---|---|---|
COLOR_PRIMARY |
Rgb(139, 233, 253) |
Rgb(0, 139, 139) |
Brand / primary accent |
COLOR_SECONDARY |
Rgb(189, 147, 249) |
Rgb(68, 71, 144) |
Supporting elements |
COLOR_ACCENT |
Rgb(255, 121, 198) |
Rgb(163, 55, 136) |
Metadata / PID |
COLOR_SUCCESS |
Rgb(80, 250, 123) |
Rgb(40, 130, 40) |
Success states |
COLOR_WARNING |
Rgb(241, 250, 140) |
Rgb(180, 120, 0) |
Warning states |
COLOR_ERROR |
Rgb(255, 85, 85) |
Rgb(215, 55, 55) |
Error states |
COLOR_FG |
Rgb(248, 248, 242) |
Rgb(40, 42, 54) |
Foreground text |
COLOR_BG |
Rgb(40, 42, 54) |
Rgb(248, 248, 242) |
Background |
COLOR_INACTIVE |
Rgb(98, 114, 164) |
Rgb(140, 140, 140) |
Muted / comments |
COLOR_HIGHLIGHT |
Rgb(68, 71, 90) |
Rgb(230, 230, 230) |
Selection background |
COLOR_PROGRESS_START |
Rgb(189, 147, 249) |
Rgb(100, 60, 180) |
Progress bar fill start |
COLOR_PROGRESS_END |
Rgb(139, 233, 253) |
Rgb(0, 139, 139) |
Progress bar fill end |
COLOR_PROGRESS_EMPTY |
Rgb(98, 114, 164) |
Rgb(200, 200, 200) |
Progress bar empty |
Theme (theme module)
Extended theme struct with adaptive color support.
| Constructor | Description |
|---|---|
Theme::adaptive() |
Resolves all colors via AdaptiveColor::resolve() (recommended) |
Theme::default() |
Hardcoded dark palette for backward compatibility |
use Theme;
// Recommended: adapts to terminal background
let theme = adaptive;
// Legacy: always dark
let theme = default;
detect -- Terminal Environment Detection
Detects TTY status, color support, and accessibility mode. All detection is cached per-process via OnceLock.
Environment Variables
| Variable | Effect when set |
|---|---|
NO_COLOR |
Disables all ANSI styling (no-color.org) |
TERM=dumb |
Disables all ANSI styling |
ACCESSIBLE |
Enables screen-reader / accessible mode |
COLORFGBG |
Used to detect light vs dark terminal background |
ColorMode
Public Functions
| Function | Returns | Description |
|---|---|---|
is_tty_stdout() |
bool |
Whether stdout is a TTY |
is_tty_stderr() |
bool |
Whether stderr is a TTY |
is_accessible_mode() |
bool |
Whether accessible / plain output is requested |
should_style() |
bool |
Master gate -- all console formatters check this |
detect_color_mode() |
ColorMode |
Resolved color mode for adaptive color selection |
console -- Styled Message Formatters
TTY-gated console formatters for non-TUI CLI output. Diagnostics go to stderr, structured data to stdout. All styling respects detect::should_style().
Format Functions (return String)
| Function | Prefix | Color | Usage |
|---|---|---|---|
format_success(msg) |
✅ |
Success | Completion messages |
format_error(msg) |
❌ |
Error | Error messages (bold) |
format_warning(msg) |
⚠️ |
Warning | Warning messages |
format_info(msg) |
ℹ️ |
Primary | Informational messages |
format_command(cmd) |
▶ |
Secondary | Command references (bold) |
format_progress(msg) |
⏳ |
Warning | In-flight operations |
format_prompt(msg) |
? |
Primary | Interactive prompts (bold) |
format_verbose(msg) |
-- | Dim | Debug / verbose output |
format_list_item(msg) |
• |
-- | Indented list items |
format_section_header(h) |
━━━ |
Primary | Section dividers with rule |
format_count(msg) |
📊 |
Accent | Metrics / counts |
format_location(msg) |
📁 |
Secondary | File paths / locations |
format_list_header(h) |
-- | FG (bold) | List / section headers |
format_search(msg) |
🔍 |
Primary | Search / scan operations |
Print Functions (write to stderr)
Convenience wrappers that call the corresponding format_* function and print to stderr:
use console;
success;
error;
warning;
info;
progress;
verbose;
section;
table -- CLI Table Renderer
Renders styled tables to stderr with zebra-striped rows, auto-computed column widths, and adaptive colors. Falls back to plain aligned text when styling is disabled.
Align
Column
Builder for table column definitions.
| Method | Description |
|---|---|
Column::new(header) |
Left-aligned column |
Column::right(header) |
Right-aligned column |
.width(w) |
Sets minimum column width |
render_table
Renders a complete table to stderr.
use ;
let columns = vec!;
let rows = vec!;
render_table;
Output (styled):
Service Latency Status
─────── ─────── ──────────
api 12ms healthy
worker 340ms degraded ← dimmed (zebra stripe)
cache 2ms healthy
progress -- CLI Progress Bar
Non-TUI progress bar rendered to stderr with adaptive gradient colors. Falls back to plain ASCII in non-TTY mode.
ProgressBar
| Method | Description |
|---|---|
ProgressBar::new(msg, width) |
Creates a progress bar with message and width |
.render(fraction) |
Renders at the given fraction (0.0 to 1.0) |
.finish() |
Ends the bar with a newline |
.finish_with_message(msg) |
Clears the bar and prints a final message |
use ProgressBar;
let pb = new;
for i in 0..=100
pb.finish_with_message;
TTY output: Downloading ████████████████░░░░░░░░░░░░░░░░░░░░░░░░ 40%
Non-TTY output: Downloading [################------------------------] 40%
spinner -- Threaded CLI Spinner
Thread-safe stderr spinner that respects TTY and accessibility settings. Uses braille animation by default with a plain-dots fallback.
SPINNER_FRAMES
Braille frames used by both the TUI spinner constant and the non-TUI Spinner:
pub const SPINNER_FRAMES: & = &;
Spinner
| Method | Description |
|---|---|
Spinner::start(msg) |
Starts the spinner in a background thread |
.stop_with_message(msg) |
Stops and prints a final message |
.stop() |
Stops without a final message |
The spinner is also stopped automatically on Drop.
use Spinner;
let spinner = start;
// ... long-running operation ...
spinner.stop_with_message;
In non-TTY mode, start() prints "Fetching service health..." once and returns immediately.
terminal -- Terminal Lifecycle Management
Manages raw mode, alternate screen, and provides a standard event loop for Ratatui applications.
Type Alias
pub type Term = ;
init() -> anyhow::Result<Term>
Enables raw mode, enters the alternate screen, and returns an initialized Term.
restore()
Leaves the alternate screen and disables raw mode. Safe to call even in a partially-initialized state.
TuiApp Trait
Implement this trait on your application state to use run_loop.
Return false from handle_key to exit the event loop. Ctrl+C always exits.
run_loop
Runs a standard TUI event loop. poll_ms controls input polling frequency.
Integration Guide
Building a new ResQ TUI tool
- Add the dependency to your crate's
Cargo.toml:
[]
= { = true }
- Implement
TuiAppon your application state:
use TuiApp;
use Theme;
use ;
use ;
- Run the event loop in
main:
Using non-TUI console output
For CLI tools that do not need a full-screen TUI:
use console;
use ;
use ProgressBar;
use Spinner;
Accessibility
resq-tui respects the following standards:
NO_COLOR(no-color.org) -- disables all ANSI color codesTERM=dumb-- plain text output onlyACCESSIBLE-- activates screen-reader-friendly output (plain dot spinners, no animation)- Non-TTY pipes and redirects receive unstyled output automatically
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.