Terminal UI library for MoonBit with reactive signals
Dependencies

{
"deps": {
"mizchi/tui": "0.9.0"
}
}mizchi/tui/
├── vnode/ # Virtual DOM primitives (row, column, view, grid, text)
├── components/ # Styled UI components (button, modal, table, etc.)
├── headless/ # State types (ButtonState, InputState, etc.)
├── events/ # Input parsing (KeyEvent, MouseEvent)
├── render/ # ANSI rendering engine
├── io/ # Platform I/O (terminal size, keypress)
└── core/ # Low-level types (Component, Color)fn main {
let node = @vnode.column(gap=1.0, padding=1.0, border="rounded", [
@vnode.text("Hello, TUI!", fg="cyan", bold=true),
@components.button("Click me"),
@components.progress_bar(0.7),
])
// Render to string
let output = @vnode.render_vnode_once(80, 24, node)
println(output)
}// Flex containers
@vnode.view([...]) // column by default
@vnode.view(direction="row", [...]) // horizontal
@vnode.row([...]) // shorthand for direction="row"
@vnode.column([...]) // shorthand for direction="column"
// Grid layout
@vnode.grid(columns=[1.0, 2.0, 1.0], [...]) // 1fr 2fr 1fr
@vnode.grid_item(column_span=2, child=...) // span multiple cells
// Named grid areas
@vnode.grid(
areas=["header header", "sidebar main", "footer footer"],
[
@vnode.grid_area("header", header_content),
@vnode.grid_area("sidebar", sidebar_content),
@vnode.grid_area("main", main_content),
@vnode.grid_area("footer", footer_content),
]
)
// Text
@vnode.text("Hello", fg="cyan", bold=true)
// Spacing
@vnode.spacer() // flexible space
@vnode.hspace(2.0) // horizontal space
@vnode.vspace(1.0) // vertical space// Buttons
@components.button("Submit", state=@headless.ButtonState::Focused)
@components.icon_button("✕")
@components.text_button("Learn more")
// Form
@components.checkbox("Remember me", true)
@components.radio("Option A", true)
@components.switch(true, "Dark mode")
@vnode.input("value", placeholder="Enter text...")
// Selection
@components.listbox(items, selected_id)
@components.tab_bar(tabs, selected_id)
@components.combobox_trigger("Select...", open=false)
// Feedback
@components.progress_bar(0.5)
@components.spinner(tick)
@components.gauge("CPU", 0.75)
@components.sparkline(data)
// Modal
@components.modal("Title", [...])
@components.alert_dialog("Error occurred")
@components.confirm_dialog("Delete?")
// Dashboard
@components.table(columns, rows)
@components.stat("Users", "1,234")
@components.meter("Memory", 0.8)# Base patterns
moon -C experiments/eval_ui run . -- --layout-snapshot
# Persona 5 patterns
moon -C experiments/eval_ui run . -- --layout-snapshot-p5moon -C experiments/eval_ui run . -- --infer-layoutjust run example=simple # Minimal counter app
moon run examples/simple --target js # Run an example directly
moon run examples/command-launcher --target js
moon run examples/completion --target js
moon run examples/components --target js
moon run examples/editor --target js
moon run examples/form --target js
moon run examples/grid-area --target js
moon run examples/grid-layout --target js
moon run examples/kitty-graphics --target js
moon run examples/roguelike --target js
moon run examples/wizard --target jsfn ansi_full_reset() -> Stringfn column(id? : String, gap? : Double, margin? : Double, margin_x? : Double, margin_y? : Double, padding? : Double, padding_x? : Double, padding_y? : Double, width? : Double, height? : Double, min_width? : Double, min_height? : Double, flex_grow? : Double, justify? : String, align? : String, border? : String, border_color? : String, bg? : String, role? : String, tab_index? : Int, on_click? : (TuiEvent) -> Unit?, children : Array[Node[TuiEvent, TuiAttrValue]]) -> Node[TuiEvent, TuiAttrValue]fn enable_mouse_all() -> Stringfn form_edit_config(field_name : String, signal : Signal[String], on_edit_start? : () -> Unit?, on_edit_end? : () -> Unit?, on_force_quit? : () -> Unit?) -> EditConfigfn grid(columns? : Array[Double], rows? : Array[Double], areas? : Array[String], auto_flow? : String, id? : String, gap? : Double, padding? : Double, padding_x? : Double, padding_y? : Double, width? : Double, height? : Double, border? : String, border_color? : String, bg? : String, role? : String, tab_index? : Int, children : Array[Node[TuiEvent, TuiAttrValue]]) -> Node[TuiEvent, TuiAttrValue]fn grid_item(column? : Int, row? : Int, column_span? : Int, row_span? : Int, tab_index? : Int, child~ : Node[TuiEvent, TuiAttrValue]) -> Node[TuiEvent, TuiAttrValue]fn init_vnode_terminal(width : Int, height : Int, output_fn : (String) -> Unit, mouse? : Bool, raw_mode? : Bool) -> (VNodeApp, () -> Unit)fn input(value : String, id? : String, placeholder? : String, state? : InputState, fg? : String, bg? : String, placeholder_fg? : String, focus_border_color? : String, edit_border_color? : String, disabled_fg? : String, disabled_bg? : String, width? : Double, min_width? : Double, padding_x? : Double, padding_y? : Double) -> Node[TuiEvent, TuiAttrValue]fn input_plain(value : String, placeholder? : String, state? : InputState, fg? : String, placeholder_fg? : String) -> Node[TuiEvent, TuiAttrValue]fn is_printable_char(c : Char) -> Boolfn is_printable_string(s : String) -> Boolfn read_key() -> Stringfn resolve_button_state(id : String, hover_id : String, focus? : FocusNav?, disabled? : Bool) -> ButtonStatefn row(id? : String, gap? : Double, margin? : Double, margin_x? : Double, margin_y? : Double, padding? : Double, padding_x? : Double, padding_y? : Double, width? : Double, height? : Double, min_width? : Double, min_height? : Double, flex_grow? : Double, justify? : String, align? : String, border? : String, border_color? : String, bg? : String, role? : String, tab_index? : Int, on_click? : (TuiEvent) -> Unit?, children : Array[Node[TuiEvent, TuiAttrValue]]) -> Node[TuiEvent, TuiAttrValue]fn run(render_fn : () -> Component, on_event : (InputEvent) -> Bool, mouse? : Bool, on_hover? : (String) -> Unit?) -> Unit@tui.run(
fn() { @tui.text("Hello") },
fn(event) { if event.is_quit() { false } else { true } },
)fn run_vnode_app(width : Int, height : Int, render_fn : () -> Node[TuiEvent, TuiAttrValue], output_fn : (String) -> Unit) -> (() -> Unit)fn start_edit(config : EditConfig, current_value : String, on_complete : () -> Unit, confirm_on_shift_enter? : Bool, esc_cancels? : Bool) -> Unitfn start_edit_inplace(config : EditConfig, current_value : String, row : Int, col : Int, width : Int, height : Int, multiline : Bool, on_complete : () -> Unit, confirm_on_shift_enter? : Bool, esc_cancels? : Bool, on_lines_change? : (Int) -> Int?, on_completion? : (String, Int) -> String??) -> Unitfn start_edit_inplace_in_bounds(config : EditConfig, current_value : String, bounds : HitTestResult, multiline : Bool, on_complete : () -> Unit, confirm_on_shift_enter? : Bool, esc_cancels? : Bool, border? : Int, on_lines_change? : (Int) -> Int?, on_completion? : (String, Int) -> String??) -> Unitfn start_keypress_listener(handler : (String) -> Unit) -> Unitfn text(content : String, fg? : String, bold? : Bool, underline? : Bool, role? : String) -> Node[TuiEvent, TuiAttrValue]fn text_dyn(getter : () -> String, fg? : String, bold? : Bool, underline? : Bool, role? : String) -> Node[TuiEvent, TuiAttrValue]fn textarea(value : String, id? : String, placeholder? : String, state? : InputState, rows? : Int, fg? : String, bg? : String, placeholder_fg? : String, focus_border_color? : String, edit_border_color? : String, min_width? : Double, padding_x? : Double, padding_y? : Double) -> Node[TuiEvent, TuiAttrValue]fn view(direction? : String, id? : String, gap? : Double, margin? : Double, margin_x? : Double, margin_y? : Double, padding? : Double, padding_x? : Double, padding_y? : Double, width? : Double, height? : Double, min_width? : Double, min_height? : Double, flex_grow? : Double, justify? : String, align? : String, border? : String, border_color? : String, bg? : String, role? : String, tab_index? : Int, on_click? : (TuiEvent) -> Unit?, children : Array[Node[TuiEvent, TuiAttrValue]]) -> Node[TuiEvent, TuiAttrValue]Terminal UI library for MoonBit with reactive signals
Dependencies