feat(harmony_tui): add initial TUI implementation with ratatui and crossterm

Add a new `harmony_tui` crate to initialize and run a text-based user interface (TUI) for the Harmony project using `ratatui` for rendering and `crossterm` for handling input. The `HarmonyTUI` struct initializes the terminal, enters a loop to render updates, and handles basic input events to exit. This commit sets up the foundation for further TUI development.
This commit is contained in:
2025-01-24 11:30:01 -05:00
parent 21258cf1af
commit 4bbe8e84d8
4 changed files with 473 additions and 9 deletions

View File

@@ -0,0 +1,13 @@
[package]
name = "harmony_tui"
version = "0.1.0"
edition = "2021"
[dependencies]
harmony = { path = "../harmony" }
log = { workspace = true }
env_logger = { workspace = true }
tokio = { workspace = true }
ratatui = "0.29.0"
crossterm = "0.28.1"
color-eyre = "0.6.3"

View File

@@ -0,0 +1,67 @@
use std::io;
use crossterm::event::{self, Event};
use harmony::maestro::Maestro;
use ratatui::{self, layout::Position, prelude::CrosstermBackend, Frame, Terminal};
pub mod tui {
// Export any necessary modules or types from the internal tui module
}
/// Initializes the terminal UI with the provided Maestro instance.
///
/// # Arguments
///
/// * `maestro` - A reference to the Maestro instance containing registered scores.
///
/// # Example
///
/// ```rust
/// use harmony;
/// use harmony_tui::init;
///
/// #[harmony::main]
/// pub async fn main(maestro: harmony::Maestro) {
/// maestro.register(DeploymentScore::new("nginx-test", "nginx"));
/// maestro.register(OKDLoadBalancerScore::new(&maestro.inventory, &maestro.topology));
/// // Register other scores as needed
///
/// init(maestro).await.unwrap();
/// }
/// ```
pub async fn init(maestro: Maestro) -> Result<(), Box<dyn std::error::Error>> {
HarmonyTUI::new(maestro).init().await
}
pub struct HarmonyTUI {
maestro: Maestro,
}
impl HarmonyTUI {
pub fn new(maestro: Maestro) -> Self {
HarmonyTUI { maestro }
}
pub async fn init(self) -> Result<(), Box<dyn std::error::Error>> {
color_eyre::install()?;
let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
loop {
terminal.draw(|f| self.render(f))?;
if matches!(event::read()?, Event::Key(_)) {
break;
}
}
Ok(())
}
fn render(&self, frame: &mut Frame) {
let size = frame.area();
frame.set_cursor_position(Position::new(size.x / 2, size.y / 2));
frame.render_widget(
ratatui::widgets::Paragraph::new("Hello World"),
frame.area(),
);
}
}