term_render

Crates.ioterm_render
lib.rsterm_render
version0.1.3-alpha.1
created_at2025-09-22 05:10:46.122543+00
updated_at2025-09-25 18:03:48.923745+00
descriptionA terminal UI library for building rich terminal applications in Rust.
homepage
repositoryhttps://github.com/AndrewDMorgan/TermRender
max_upload_size
id1849512
size243,804
Andrew Morgan (AndrewDMorgan)

documentation

README

TermRender

A high-performance, async-ready terminal UI library for Rust, built with flexibility and performance in mind. TermRender provides a modern widget system, efficient rendering, and comprehensive input handling for creating rich terminal applications.

Features

  • High Performance: Optimized rendering with dirty-rectangle tracking and efficient escape sequence handling

  • Flexible Widget System: Build custom UI components with a trait-based approach

  • Comprehensive Input: Full keyboard and mouse support with modifier keys

  • Rich Text Support: ANSI colors, 24-bit RGB, and text styling

  • Async Ready: Built on Tokio for modern async/await workflows

  • Dynamic Layouts: Responsive layouts that adapt to terminal resizing

  • Extensible: Easy to create custom widgets and renderers

Quick Start

Add TermRender to your Cargo.toml:

toml
[dependencies]
term_render = "0.1.2"
tokio = { version = "1.47.1", features = ["full"] }

Basic usage example:

use term_render::{self, event_handler::KeyCode};
use term_render::widget_impls::WidgetBuilder;

// this acts as the callback that is called every frame
// this is the entry point and any logic needs to branch out from here
fn app_callback(app: &mut term_render::App<AppData>, data: &mut AppData) -> Result<bool, ()> {
    // place app logic here
    if app.events.read().contains_key_code(KeyCode::Return) {
        return Ok(true);
    }
    if data.time.elapsed().as_secs_f64() > 15.0 {
        return Ok(true);
    }
    
    Ok(false)  // return true to exit the app
}

// defining the application data structure
// this can contain any data you want to use in the app callback
struct AppData {
    pub time: std::time::Instant,
}

#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
async fn main() -> tokio::io::Result<()> {
    // creating an instance of the app
    let mut app = term_render::App::new()?;
    let data = AppData {
        time: std::time::Instant::now(),
    };
    
    // creating a new scene to attach widgets to
    let mut scene = term_render::widget::Scene::new();
    
    // creating a widget using the builder trait.
    let (widget, window) =
        term_render::widget_impls::StaticWidgetBuilder::<AppData>::builder(String::from("name"))
            .with_border(true)
            .with_renderer(Box::new(|_size, _position| {
                Some(vec![])
            }))
            .with_position((10, 10))
            .with_size((50, 10))
            .build(&app.area.read()).unwrap();
    scene.add_widget(widget, window, &mut *app.renderer.write()).unwrap();
    app.scene = Some(scene);
    
    // running the application with the callback, and provided data for state tracking.
    app.run(data, |data, app_instance| {
        app_callback(app_instance, data)
    }).await.unwrap();
    Ok(())
}

Basic Explanation

The basis of a TermRender application is the App struct, which manages the terminal state, event handling, and rendering. You create an instance of App, set up your UI components (widgets), and then call run with a callback function that contains your application logic. The callback function is called every frame, allowing you to update your application state and respond to events. The AppData struct is a user-defined structure that holds any state you want to maintain across frames. In this example, it tracks the elapsed time since the application started. The App instance optionally can have a Scene, which is a container for widgets. You can create widgets using the provided builders or implement your own by adhering to the Widget trait.

When a name/string identifier is requested for a widget, it should be unique to that scene, as the backend renderer references Window's (similar to a Widget, but lower level) not by the widget instance itself, but rather that string

Widget System

TermRender provides a flexible widget system:

Built-in Widgets

  • StaticWidget: Basic widget with custom rendering logic

  • DynamicWidget: An interactable widget. In the future, more advanced versions will be implemented which will default as buttons or other such higher-level widgets.

  • ButtonWidget: A simple button with automatic click handling.

  • More coming soon!

  • Creating Custom Widgets Implement the Widget trait to create custom components:

use term_render::{Widget, SendSync, event_handler::KeyParser};

// Where type C is the user defined app data state
struct MyCustomWidget<C> {
    // Your widget state
}

impl<C> Widget<C> for MyCustomWidget<C> {
    fn get_window_ref(&self) -> String {
        "my_custom_widget".to_string()  // note: each widget instance requires a unique identifyer
    }
    
    fn update_with_events(&mut self, events: &SendSync<KeyParser>) {
        // Handle events
    }
    
    fn update_render(&mut self, window: &mut Window, area: &Rect) {
        // Update rendering
    }
    
    // ... implement other required methods
}

Performance Features

TermRender includes several optimizations:

  • Dirty Rectangle Rendering: Only updates changed portions of the screen

  • Efficient Event Handling: Minimal overhead input processing

  • Smart Caching: Redundant render calls are avoided

  • Async Rendering: Non-blocking UI updates

Examples

Check out the examples directory for complete implementations (more coming soon):

  • Run basic example cargo run --example basic

Documentation

Full API documentation is available:

https://docs.rs/term_render/latest/term_render/

Contributing

Contributions are welcome! Please feel free to submit pull requests, open issues, or suggest new features.

  1. Fork the repository

  2. Create your feature branch (git checkout -b feature/amazing-feature)

  3. Commit your changes (git commit -m 'Add amazing feature')

  4. Push to the branch (git push origin feature/amazing-feature)

  5. Open a Pull Request

License

This project is licensed under:

MIT license (LICENSE-MIT)

Acknowledgments

  • Inspired by Ratatui

  • Built with performance and developer experience in mind

  • Thanks to all contributors and the Rust community

TermRender - Build beautiful terminal applications with Rust

Commit count: 68

cargo fmt