Skip to content

mrsaraiva/textual-rs

Repository files navigation

textual-rs

Crates.io Documentation License: MIT

A Rust port of Textual, a reactive TUI framework for building rich terminal applications with widgets, CSS styling, layout, and event-driven architecture.

Built on rich-rs for terminal rendering primitives and crossterm for terminal I/O.

Attribution. textual-rs is a derivative work: a Rust port of Textual, created by Will McGugan and the Textualize team. All credit for the original framework design, API, and concepts goes to them. This project exists only because of their work and aims to bring that experience to Rust. Published on crates.io as the textual crate.

Installing

[dependencies]
textual = "1.0.0-dev"

Features

  • 56 widgets: buttons, inputs, text areas, data tables, trees, tabs, markdown viewer, select, checkboxes, progress bars, overlays, and more
  • 108 CSS properties: type/id/class/pseudo-class selectors, descendant/child combinators, nested & rules, cascade with specificity and !important, theme tokens
  • Full layout engine: vertical, horizontal, grid, dock, and absolute positioning with box model (margin, border, padding), scrolling, min/max constraints, fractional/percentage/viewport units
  • Event system: capture/bubble phases, focus management, keyboard bindings with action maps, mouse hit-testing, message bus
  • Reactive runtime: Tokio-based event loop, reactive state with watchers, workers for background tasks, CSS transitions with easing functions
  • Hot-reloadable stylesheets: external .tcss files with App::watch_stylesheet()
  • Deterministic rendering: frame buffer with screen diffing, metadata-safe hit-testing across repaints
  • 1,490 tests: unit, integration, snapshot (via insta), and golden-file coverage
  • unsafe forbidden: enforced by lint configuration

Quick start

tools/run-doc-example.sh widgets buttons           # Interactive button demo
tools/run-doc-example.sh widgets hello             # Composed widget/layout showcase
tools/run-doc-example.sh widgets data_table        # Data table widget
tools/run-doc-example.sh widgets input             # Input fields
tools/run-doc-example.sh widgets text_area_example # Text editor

Widget-focused docs parity examples live in a dedicated crate:

cargo run --manifest-path docs/examples/widgets/Cargo.toml --example tabbed_content
tools/run-doc-example.sh widgets tabbed_content_label_color
tools/run-doc-example.sh guide/screens modal01

Demo Source Mapping

  • Python docs demos (../textual/docs/examples/**) map to our docs example lane (docs/examples/**).
    • Current crate-backed location: docs/examples/widgets/examples/** and docs/examples/guide/**.
  • Python app demos (../textual/examples/**) map to app examples under examples/**.

Widget catalog

Interactive: Button, Input, MaskedInput, TextArea, Checkbox, RadioSet, Switch, Select, OptionList, SelectionList, ListView, DataTable, Tree, DirectoryTree, Tabs, TabbedContent, Collapsible, CommandPalette, Link

Display: Label/Static, Text, Markdown, Pretty, Digits, ProgressBar, LoadingIndicator, Sparkline, RichLog, Log, Toast, Rule, Spacer, Placeholder, HelpPanel, KeyPanel

Containers: Container, ScrollView, Frame, Panel, Overlay, Constrained, Styled, Node

CSS styling

Stylesheets use Textual's TCSS syntax with nested rules:

Button {
    width: auto;
    min-width: 16;
    line-pad: 1;
    text-align: center;
    content-align: center middle;

    &.-style-flat {
        text-style: bold;
        color: auto 90%;
        background: $surface;
        border: block $surface;

        &:hover {
            background: $primary;
            border: block $primary;
        }
    }
}

Supported selectors: type, #id, .class, pseudo-classes (:hover, :focus, :active, :disabled, :can-focus, :dark, :light, :even, :odd, :first-child, :last-child, and more), descendant ( ), child (>), grouping (,), universal (*).

Theme tokens ($primary, $surface, $error-darken-2, etc.) resolve against the active theme and support lighten/darken/muted derivations.

Layout

Five layout modes: vertical, horizontal, grid, dock, and absolute.

Size units: cells (20), auto, percentage (50%), fractions (1fr), viewport (100vw, 50vh).

Box model with margin collapsing, border-box sizing (default, matching Python Textual), padding, and border. Constraints via min-width, max-width, min-height, max-height. Overflow handling with scrollbars (overflow: auto | hidden | scroll).

Architecture

Widget tree → rich-rs Segments (with metadata) → FrameBuffer (2D grid) → frame diff → ANSI output
  • Event routing: capture phase (root → focused) then bubble phase (focused → root)
  • Style resolution: CSS cascade with specificity, inheritance, and !important
  • Rendering: dirty-flag driven; widgets call ctx.request_repaint() to trigger re-render

Build and test

cargo build                      # Build library
cargo test                       # Run all 1,490 tests
cargo clippy                     # Lint
cargo fmt                        # Format

Debugging

Environment variables for targeted instrumentation:

TEXTUAL_DEBUG_STYLE_FILE=/tmp/style.log   # Log CSS resolution
TEXTUAL_DEBUG_LAYOUT_FILE=/tmp/layout.log # Log layout calculations
TEXTUAL_DEBUG_INPUT_FILE=/tmp/input.log   # Log input events
TEXTUAL_DEBUG_RENDER_FILE=/tmp/render.log # Log rendering
TEXTUAL_DEBUG_BORDER_FILE=/tmp/border.log # Log border painting
TEXTUAL_DEBUG_FOCUS=1                     # Log focus changes to stderr

Filters narrow output: TEXTUAL_DEBUG_STYLE_FILTER='type=Button,class=error'

Python parity

Python Textual is the source of truth for behavior and default styling. The port aligns:

  1. Semantics first: event/focus/message behavior, layout/box-model rules
  2. Defaults second: all 16 widget default CSS files match Python Textual verbatim
  3. Visuals third: render-time composition, border painting, opacity blending

Rust idioms are used where appropriate (ownership, type safety, modular boundaries) while preserving behavioral parity.

Roadmap

See ROADMAP.md for detailed phase tracking. Current status:

  • Phases 0–9.6: Complete (runtime, widgets, layout, styling, async, animations, debug tooling, input diagnostics, tabbed content parity)
  • Phase 9.7: Active (core modularization)
  • v0.2 execution: widget closure, invalidation improvements, broader test coverage

License

MIT

About

A reactive TUI framework for Rust: a port of Python's Textual by Will McGugan/Textualize

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages