Crate rxtui

Crate rxtui 

Source
Expand description

§RxTUI - Reactive Terminal User Interface Framework

A modern terminal UI framework for Rust that brings React-style component architecture and declarative UI building to the terminal. Build interactive, stateful terminal applications with ease using familiar patterns.

§Features

  • Component-based architecture - Build reusable, composable UI components
  • Declarative UI with node! macro - Express your UI structure clearly
  • Virtual DOM with diffing - Efficient, minimal terminal updates
  • Message-based state management - Predictable state updates like Elm
  • Async effects - Handle background tasks, timers, and I/O operations
  • Rich styling - Colors, borders, text styles, and layout control
  • Built-in components - TextInput, forms, and more

§Architecture Overview

    ┌────────────┐     ┌─────────────┐     ┌─────────────┐
    │  Component │────▶│  node!      │────▶│    Node     │
    │   (trait)  │     │   macro     │     │    Tree     │
    └────────────┘     └─────────────┘     └─────────────┘
           │                  │                   │
           │                  ▼                   ▼
    ┌─────────────┐    ┌─────────────┐     ┌─────────────┐
    │   Update    │    │    View     │────▶│    VDom     │
    │  (messages) │    │  (render)   │     │   (state)   │
    └─────────────┘    └─────────────┘     └─────────────┘
           │                                     │
           ▼                                     ▼
    ┌─────────────┐                       ┌─────────────┐
    │   Action    │                       │    Diff     │
    │  (state)    │                       │   Engine    │
    └─────────────┘                       └─────────────┘
                                                 │
                                                 ▼
                                          ┌─────────────┐
                                          │  Terminal   │
                                          │   Render    │
                                          └─────────────┘

§Quick Start

use rxtui::prelude::*;

#[derive(Component)]
struct Counter;

impl Counter {
    #[update]
    fn update(&self, _ctx: &Context, msg: &str, mut count: i32) -> Action {
        match msg {
            "inc" => Action::update(count + 1),
            "dec" => Action::update(count - 1),
            _ => Action::exit(),
        }
    }

    #[view]
    fn view(&self, ctx: &Context, count: i32) -> Node {
        node! {
            div(
                pad: 2,
                align: center,
                w_frac: 1.0,
                gap: 1,
                @key(up): ctx.handler("inc"),
                @key(down): ctx.handler("dec"),
                @key(esc): ctx.handler("exit")
            ) [
                text(format!("Count: {count}"), color: white, bold),
                text("use ↑/↓ to change, esc to exit", color: bright_black)
            ]
        }
    }
}

fn main() -> std::io::Result<()> {
    App::new()?.run(Counter)
}

§Key Concepts

  • Component: Main trait for building UI components with state management
  • node! macro: Declarative macro for building UI trees with JSX-like syntax
  • Node: Virtual representation of UI elements (divs, text, components)
  • Context: Provides access to message handlers and component communication
  • Action: Return type from update methods (update state, exit, etc.)
  • App: Main application runner that manages the event loop
  • VDom: Virtual DOM that tracks UI state and calculates diffs

§Examples

§Basic Hello World

use rxtui::prelude::*;

#[derive(Component)]
struct HelloWorld;

impl HelloWorld {
    #[view]
    fn view(&self, ctx: &Context) -> Node {
        node! {
            div(bg: blue, pad: 2, @key_global(esc): ctx.handler(())) [
                text("Hello, Terminal!", color: white, bold),
                text("Press Esc to exit", color: white)
            ]
        }
    }
}

fn main() -> std::io::Result<()> {
    App::new()?.run(HelloWorld)
}

§Using the node! Macro

The node! macro provides a declarative way to build UI trees:

use rxtui::prelude::*;

fn build_ui(ctx: &Context) -> Node {
    node! {
        div(
            bg: black,              // Background color
            border_color: white,    // Border color
            border_style: rounded,  // Border style
            pad: 2,                 // Padding
            gap: 1,                 // Gap between children
            dir: vertical           // Layout direction
        ) [
            text("Title", color: yellow, bold),

            div(dir: horizontal, gap: 2) [
                div(bg: blue, w: 20, h: 5) [
                    text("Left Panel", color: white)
                ],
                div(bg: green, w: 20, h: 5) [
                    text("Right Panel", color: white)
                ]
            ],

            text("Status: Ready", color: bright_black)
        ]
    }
}

§State Management

Components manage state through messages and updates:

use rxtui::prelude::*;

#[derive(Clone, Default)]
struct TodoState {
    items: Vec<String>,
    input: String,
}

#[derive(Component)]
struct TodoApp;

impl TodoApp {
    #[update]
    fn update(&self, _ctx: &Context, msg: &str, mut state: TodoState) -> Action {
        match msg {
            "add" => {
                if !state.input.is_empty() {
                    state.items.push(state.input.clone());
                    state.input.clear();
                }
                Action::update(state)
            }
            "clear" => {
                state.items.clear();
                Action::update(state)
            }
            _ => Action::exit()
        }
    }

    #[view]
    fn view(&self, ctx: &Context, state: TodoState) -> Node {
        node! {
            div(pad: 2, gap: 1) [
                text("Todo List", color: yellow, bold),
                div(gap: 1) [
                    ...(state.items.iter().map(|item| {
                        node! { text(format!("• {}", item), color: white) }
                    }).collect::<Vec<_>>())
                ],
                text(format!("{} items", state.items.len()), color: bright_black)
            ]
        }
    }
}

§Async Effects

Handle background tasks with effects (requires effects feature):

use rxtui::prelude::*;

#[derive(Component)]
struct Timer;

impl Timer {
    #[update]
    fn update(&self, _ctx: &Context, tick: bool, seconds: u32) -> Action {
        if !tick {
            return Action::exit();
        }
        Action::update(seconds + 1)
    }

    #[view]
    fn view(&self, ctx: &Context, seconds: u32) -> Node {
        node! {
            div(pad: 2, align: center, @key(esc): ctx.handler(false)) [
                text(format!("Timer: {}s", seconds), color: white, bold),
                text("Press Esc to stop", color: bright_black)
            ]
        }
    }

    #[cfg(feature = "effects")]
    #[effect]
    async fn tick(&self, ctx: &Context) {
        loop {
            tokio::time::sleep(std::time::Duration::from_secs(1)).await;
            ctx.send(true);
        }
    }
}

§Rich Text Formatting

Create styled text with multiple segments:

use rxtui::prelude::*;

fn status_line() -> Node {
    node! {
        richtext [
            text("Status: ", color: white),
            text("Connected", color: green, bold),
            text(" | ", color: bright_black),
            text("CPU: ", color: white),
            text("42%", color: yellow),
            text(" | ", color: bright_black),
            text("Mem: ", color: white),
            text("2.1GB", color: cyan)
        ]
    }
}

§Input Handling

Use the built-in TextInput component:

use rxtui::prelude::*;

fn input_form(ctx: &Context) -> Node {
    node! {
        div(pad: 2, gap: 1) [
            text("Enter your name:", color: white),
            input(
                placeholder: "Type here...",
                border_color: cyan,
                w: 30,
                focusable,
                @submit: ctx.handler("submit")
            )
        ]
    }
}

Re-exports§

pub use app::App;
pub use app::Context;
pub use bounds::Rect;
pub use component::Action;
pub use component::Component;
pub use component::Message;
pub use component::MessageExt;
pub use component::State;
pub use components::ShimmerSpeed;
pub use components::ShimmerText;
pub use components::TextInput;
pub use key::Key;
pub use key::KeyWithModifiers;
pub use node::Div;
pub use node::Node;
pub use node::RichText;
pub use node::Text;
pub use node::TextSpan;
pub use style::BorderEdges;
pub use style::BorderStyle;
pub use style::Color;
pub use style::Dimension;
pub use style::Direction;
pub use style::Overflow;
pub use style::Position;
pub use style::Spacing;
pub use style::Style;
pub use style::TextStyle;
pub use style::TextWrap;
pub use style::WrapMode;

Modules§

app
Application framework for building terminal UIs. Provides the main application lifecycle and event handling.
bounds
Bounds and rectangle operations for dirty region tracking. Provides types for tracking screen regions that need redrawing. Bounds and rectangle operations for dirty region tracking.
component
New component-based system (parallel implementation)
components
Reusable UI components for building forms and interfaces Provides pre-built components like TextInput, Button, etc. Reusable UI components for rxtui
effect
Async effects system for running background tasks Async effects system for running background tasks in components
key
Key representation for keyboard input. Provides an enum for representing both characters and special keys. Key representation for keyboard input handling.
macros
Macro-based DSL for building TUI components Provides ergonomic macros for composing components with less boilerplate Macro-based DSL for building TUI components
node
Node types for component tree (includes div, text, rich_text)
prelude
Prelude module for convenient imports Prelude module for convenient imports.
style
Styling system for terminal UI components. Defines colors, spacing, borders, and other visual properties. Styling system for terminal UI models.

Macros§

debug_log
Debug logging macro that only compiles in debug builds. Writes timestamped messages to /tmp/radical_debug.log
node
Main macro for building TUI components with a declarative syntax

Attribute Macros§

component
Impl-level macro that automatically handles Component trait boilerplate.
effect
Marks an async method as a single effect that runs in the background.
update
Simplifies component update methods by automatically handling message downcasting, state fetching, and topic routing.
view
Simplifies component view methods by automatically fetching state from the context.