Crates.io | hollowtea |
lib.rs | hollowtea |
version | 0.0.1 |
created_at | 2025-02-16 15:59:16.519724+00 |
updated_at | 2025-02-16 15:59:16.519724+00 |
description | A hollowed TUI framework 👻 |
homepage | https://raphamorim.io/hollowtea |
repository | https://github.com/raphamorim/hollowtea |
max_upload_size | |
id | 1557863 |
size | 363,900 |
A hollowed TUI framework.
tl;dr: Hollow Tea is your choice to go build TUI if love Bubbletea and Rust.
🚧 hollowtea is under development.
Originally, Hollow Tea was created to serve Boo text editor, however I started to use in other projects, so I have decided to move to it's own repository and create a proper documentation.
Disclaimer: It is not a project from the Charm.sh but the name is a tribute to the famous Bubbletea.
Hollow Tea was created to make TUI creating much more simple and intuitive. You shouldn't need to know any other dependencies besides Hollow Tea to create your TUI app.
These three concepts are the core of The Elm Architecture:
Let's see it in practice, by doing a quick TUI app.
The following code:
use hollowtea::styles::*;
use hollowtea::{
hollows::{Hollow, Text},
term::event::{Event, KeyCode},
Application, Command, Message, Model,
};
struct ColorPicker<'a> {
current: usize,
items: Vec<(&'a str, Color, Color)>,
}
impl ColorPicker<'_> {
fn new() -> ColorPicker<'static> {
let items = vec![
("yellow", Color::LightYellow, Color::Yellow),
("red", Color::LightRed, Color::Red),
("blue", Color::LightBlue, Color::Blue),
("magenta", Color::LightMagenta, Color::Magenta),
("green", Color::LightGreen, Color::Green),
("cyan", Color::LightCyan, Color::Cyan),
("black", Color::LightBlack, Color::Black),
("white", Color::LightWhite, Color::White),
];
ColorPicker { current: 0, items }
}
}
impl Model for ColorPicker<'_> {
fn init(&self) -> Vec<Command> {
vec![]
}
fn update(&mut self, message: Message) -> Vec<Command> {
if let Some(Event::Key(key_event)) = message.as_ref::<Event>() {
match key_event.code {
KeyCode::Right => {
if self.current >= self.items.len() - 1 {
self.current = 0;
} else {
self.current += 1;
}
}
KeyCode::Left => {
if self.current == 0 {
self.current = self.items.len() - 1;
} else {
self.current -= 1;
}
}
KeyCode::Esc => {
return vec![Command::Quit];
}
_ => {}
}
}
vec![]
}
fn view(&self) -> Box<dyn Hollow> {
let mut content = String::default();
if let Some(current) = self.items.get(self.current) {
content.push_str(
&StyleSheet::new()
.add(Style::TextStyle(TextStyle::Bold))
.add(Style::TextColor(Color::LightBlack))
.build("Pick a color: "),
);
let idx = self.current + 1;
let indicator = String::from_utf8(vec![b' '; idx]).unwrap_or_default();
content.push_str(
&StyleSheet::new()
.add(Style::BackgroundColor(current.1.to_owned()))
.build(indicator),
);
let indicator =
String::from_utf8(vec![b' '; self.items.len() - idx]).unwrap_or_default();
content.push_str(
&StyleSheet::new()
.add(Style::BackgroundColor(current.2.to_owned()))
.build(indicator),
);
content.push_str(" ❮ ");
content.push_str(
&StyleSheet::new()
.add(Style::TextStyle(TextStyle::Bold))
.add(Style::TextColor(current.2.to_owned()))
.build(current.0),
);
content.push_str(" ❯ ");
}
Text::new(content)
}
}
fn main() {
Application {
inline: true,
..Application::default()
}
.run(&mut ColorPicker::new())
.expect("failed to run");
println!("app exited");
}
examples/color-picker
examples/textarea
examples/list
examples/table
examples/progress
examples/grid