LunarTUI is a MoonBit terminal user interface library. It provides rendering primitives, layout algorithms, widgets, and a terminal-agnostic event protocol for building native terminal applications.
Chinese documentation is available in README.zh-CN.md.
- Double-buffered terminal rendering with diff updates.
- Layout traits and built-in horizontal, vertical, grid, and flex layouts.
- Widgets including
Label,Paragraph,Divider,Image,Container,Block, andProgressBar. - UTF-8 text rendering and basic text wrapping support.
- A terminal-agnostic event model that can be driven by any compatible event backend.
moon add FrozenLemonTee/LunarTUIOr add it manually to moon.mod:
import {
"FrozenLemonTee/LunarTUI@0.1.0",
}LunarTUI currently targets MoonBit native builds.
FrozenLemonTee/LunarTUI/src/base: core rendering types, layout/widget traits, and event protocol.FrozenLemonTee/LunarTUI/src/layouts: built-in layout implementations.FrozenLemonTee/LunarTUI/src/widgets: built-in widgets.FrozenLemonTee/LunarTUI/src/terminal: terminal drawing, buffering, cursor helpers, and native terminal output.
let title = @widgets.Label::new(
"Welcome to LunarTUI",
left=2,
top=1,
)
let progress = @widgets.ProgressBar::new(
20,
value=0.75,
left=2,
top=3,
prefix="Loading:",
suffix="complete",
)
let container = @widgets.Container::new(
0,
0,
80,
24,
layout=@layouts.VLayout::new(),
children=[title, progress],
)
let terminal = @terminal.Terminal::new(@base.Area::new(80, 24))
@terminal.Terminal::clear()
terminal.draw(container)
@terminal.Terminal::newline()LunarTUI defines the UI-side event model in src/base/event.mbt. The model is deliberately terminal-agnostic: LunarTUI does not read stdin, parse terminal byte streams, or depend on a concrete event backend.
Core event types include:
| Type | Purpose |
|---|---|
EventKind |
Identifies the event category: key, mouse, resize, focus, or text input. |
Event |
Wrapper carrying exactly one event payload. |
KeyEvent |
Key state, key code, optional text, modifiers, and repeat count. |
MouseEvent |
Mouse state, optional button, optional scroll direction, position, and modifiers. |
ResizeEvent |
Terminal width and height. |
FocusEvent |
Focus gained or lost. |
TextInputEvent |
Committed text input. |
EventResult |
Widget response: Ignored, Handled, or Redraw. |
Event constructors are available for building protocol values:
let key = @base.KeyEvent::new(
@base.KeyState::Down,
@base.KeyCode::Enter,
)
let event = @base.Event::key(key)The Widget trait includes event handling alongside sizing and rendering:
pub(open) trait Widget {
fn width(self : Self) -> Int
fn height(self : Self) -> Int
fn render(self : Self, frame : Frame) -> Unit
fn handle_event(self : Self, event : Event) -> EventResult
}The recommended application protocol is:
- Poll or receive a backend event from an external event source.
- Convert it into
@base.Event. - Route it to the focused widget or component tree.
- Inspect the returned
EventResult. - Redraw only when the result is
Redraw.
EventResult separates event consumption from rendering:
Ignored: the widget did not handle the event; parent or sibling routing may continue.Handled: the widget handled the event, but no immediate redraw is required.Redraw: the widget handled the event and the application should render again.
This keeps LunarTUI independent from a specific event loop, focus manager, or terminal backend.
LunarTUI does not depend on an event backend. The default recommended backend is LunarEvent, which provides a MoonBit event API backed by TerminalEvent's C FFI surface.
For LunarTUI applications, use the FrozenLemonTee/LunarEvent/lunartui adapter. It converts LunarEvent values into LunarTUI @base.Event values and exposes EventSource for polling:
let source = @lunartui.EventSource::new()
match source.poll_event(32) {
Some(event) => {
match widget.handle_event(event) {
@base.EventResult::Redraw => terminal.draw(widget)
@base.EventResult::Handled => ()
@base.EventResult::Ignored => ()
}
}
None => ()
}See TextEditor for an interactive demo that combines:
TerminalEvent -> LunarEvent -> LunarEvent/lunartui -> LunarTUI -> application redraw loop
The demo validates keyboard input, cursor movement, redraw requests, and saving text to disk.
moon check --target native
moon test --target nativeSome tests render terminal control sequences. Run them in a terminal environment that can tolerate alternate screen, cursor, and raw terminal output.
Apache-2.0. See LICENSE.