#user-interface #declarative-ui #ui-component #tui #hook #text-based #section

intuitive

a library for building declarative text-based user interfaces

16 releases

0.7.0-alpha.0 Dec 13, 2022
0.6.3 Oct 10, 2023
0.6.2 Sep 24, 2022
0.5.1 Sep 12, 2022
0.1.0 Sep 5, 2022

#1047 in GUI

29 downloads per month

CC0 license

74KB
1.5K SLoC

Intuitive

docs.rs Documentation


lib.rs:

Intuitive

Intuitive is a component-based library for creating text-based user interfaces (TUIs) easily.

It is heavily inspired by React and SwiftUI, containing features that resemble functional components, hooks, and a declarative DSL.

Check out the Getting Started section below for a brief introduction to using Intuitive.

Design

The main focus of Intuitive is to simplify the implementation of full-terminal TUIs, such as lazygit. Intuitive attempts to make it easy to write reusable TUI components that are:

  • readable (minimal-code)
  • familiar (similar to existing web frameworks)
  • fully-featured (conditional rendering, key/mouse-handling, responsive)

Getting Started

There are a few fundamental concepts within Intuitive:

A quick example of these three concepts in action looks like this:

use std::{thread, time::Duration};

use intuitive::{
  component,
  components::{Fixed, Padding, Section, Text},
  element::Any as AnyElement,
  error::Result,
  render,
  render::hooks::{UseEffect, UseState},
  style::Color,
  terminal::Terminal,
  utils::layout::{Alignment, Amount},
};

#[component(Root)]
fn render() -> AnyElement {
  let seconds = hooks.use_state(|| 0);

  hooks.use_effect(|| {
    thread::spawn({
      let seconds = seconds.clone();
      move || loop {
        thread::sleep(Duration::from_secs(1));

        seconds.update(|seconds| seconds + 1).unwrap();
      }
    });
  });

  render! {
    Padding(amount: Amount::Percentage(10)) {
      Fixed(height: Amount::Fixed(3)) {
        Section(title: "Seconds", border: Color::Red) {
          Text(
            text: format!("This program has run for {} seconds", seconds.get()),
            alignment: Alignment::Center
          )
        }
      }
    }
  }
}

fn main() -> Result<()> {
  Terminal::new()?.render(Root {})?;

  Ok(())
}

Above, a Root component is being defined using the #[component(..)] attribute macro. It uses the UseState hook to create a State<i32>, whose value is initially 0. Then it uses the UseEffect hook to run a function when this component is first rendered. Specifically, it spawns a thread that increments seconds once every second. This means that Root will be re-rendered once a second, each time the seconds is updated.

Then, Root::render returns an element constructed using the Padding, Fixed, Section, Text components, and the render! macro. Specifically, it constructs a section of text that will be centered on the screen with a fixed height. The text displayed will show how many seconds have passed since the program started.

See the relevant documentation linked at the beginning of the Getting Started section for more details.

Dependencies

~5–13MB
~158K SLoC