Bridge between Dioxus Core (reactive components) and Ratatui (terminal rendering). Architecture: retained TuiNode tree + immediate Ratatui render Dioxus VirtualDom → WriteMutations → TuiTree → Frame → Terminal Modules: tree.rs — retained node tree (Element, Text, Placeholder) renderer.rs — WriteMutations impl (14 methods) layout.rs — Dioxus attrs → Ratatui Constraint/Direction style.rs — attrs → Ratatui Style (colors, bold, italic) events.rs — crossterm quit detection render.rs — tree walker → Ratatui widget rendering lib.rs — launch() + event loop + terminal lifecycle Elements: div, p, span, h1-h3, hr Layout: vertical/horizontal split, percentage/length/fill constraints Styling: named colors, hex, bold, italic, underline, dim Events: keyboard quit (Ctrl+C, q) 453 lines. Zero governance/substrate dependencies. Apache 2.0 — pure community crate. Signed-off-by: Tyler King <tking@guildhouse.dev>
40 lines
1.5 KiB
Rust
40 lines
1.5 KiB
Rust
//! Map Dioxus layout attributes to Ratatui Layout/Constraint.
|
|
|
|
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
|
use std::collections::HashMap;
|
|
|
|
pub fn direction_from_attrs(attrs: &HashMap<String, String>) -> Direction {
|
|
match attrs.get("flex_direction").or(attrs.get("direction")).map(|s| s.as_str()) {
|
|
Some("row") | Some("horizontal") => Direction::Horizontal,
|
|
_ => Direction::Vertical,
|
|
}
|
|
}
|
|
|
|
pub fn constraint_from_attrs(attrs: &HashMap<String, String>, direction: Direction) -> Constraint {
|
|
let size_attr = match direction {
|
|
Direction::Vertical => attrs.get("height"),
|
|
Direction::Horizontal => attrs.get("width"),
|
|
};
|
|
if let Some(size) = size_attr {
|
|
return parse_constraint(size);
|
|
}
|
|
if let Some(flex) = attrs.get("flex") {
|
|
if let Ok(r) = flex.parse::<u16>() { return Constraint::Fill(r); }
|
|
}
|
|
Constraint::Fill(1)
|
|
}
|
|
|
|
fn parse_constraint(s: &str) -> Constraint {
|
|
let s = s.trim();
|
|
if s.ends_with('%') {
|
|
if let Ok(p) = s.trim_end_matches('%').parse::<u16>() { return Constraint::Percentage(p); }
|
|
}
|
|
let numeric = s.trim_end_matches(|c: char| c.is_alphabetic());
|
|
if let Ok(n) = numeric.parse::<u16>() { return Constraint::Length(n); }
|
|
Constraint::Fill(1)
|
|
}
|
|
|
|
pub fn split_area(area: Rect, direction: Direction, constraints: &[Constraint]) -> Vec<Rect> {
|
|
if constraints.is_empty() { return vec![area]; }
|
|
Layout::default().direction(direction).constraints(constraints).split(area).to_vec()
|
|
}
|