pippa

A terminal UI framework for MoonBit, inspired by bubbletea (Go) and Elm architecture. Provides a Model-Update-View pattern with typed messages, ANSI rendering, and composable components.

tui
terminal
framework
elm
bubbletea
ansi
moon add brickfrog/pippa@0.1.0
Download zip
Author
Version
0.1.0
License
Apache-2.0
Last updated
2 months ago
Downloads
28

Dependencies

README

#Pippa

A terminal UI framework for MoonBit, inspired by bubbletea (Go) and the Elm Architecture.

Pippa showcase

#Overview

Pippa provides a Model–Update–View pattern where:

  • Model — application state is a plain struct.
  • Msg — updates are driven by typed messages (key presses, resize, timers, custom events).
  • View — the view function renders state to a string of ANSI-formatted output.

The runtime handles raw terminal mode, input parsing, and efficient diffing and re-rendering, so library users only think about state transitions and string output.

A component library ships alongside the core — spinners, text inputs, textareas, lists, selection lists, tables, paginators, viewports, timers, stopwatches, file pickers, and animated progress bars — each implemented as composable Models that can be embedded in larger applications.

The main @pippa package includes visual styling and layout helpers: hex/RGB colors, borders, padding, margins, joins, placement, and the col/row/lines/text/gap/hgap layout DSL. There is no separate styling package.

#Quick Start

moon test moon run src/examples/showcase

The showcase example exercises the runtime, components, styling, spring animation, focus handling, and viewport behavior.

#Keymaps and Help

Key bindings separate activation keys from display help. Prefer constructors over raw struct literals:

let quit = binding(keys=["q", "ctrl+c"], help_key="q", help="quit")
let model = help_model(
keymap=keymap([quit]),
show_full=false,
width=80,
)
let footer = help_view(model)

When migrating older code, replace flat Binding::{ keys, help } values with binding(keys=..., help_key=..., help=...). Replace HelpModel::{ bindings, show_full } with help_model(keymap=keymap(bindings),show_full=..., width=...). Disabled or unbound bindings do not match and are omitted from both short and full help.

#View Surface

The common Program::new(..., view=fn(model) { "..." }) shape remains a plain Model -> String function. Apps that need per-render terminal state can opt in with one extra builder call:

let program = Program::new(
init=my_init,
update=my_update,
view=my_string_view,
)
.with_structured_view(fn(model) {
View::text(my_string_view(model))
.with_window_title("Pippa")
.with_cursor_visible(true)
})

For now, the renderer uses View.content exactly like the old string view. The optional cursor, title, color, progress, and mouse-mode fields are carried with the frame for the next renderer pass.

#Project Structure

src/ # Source root (moon.mod.json → source: "src") ├── moon.pkg # Core library package ├── types.mbt # Cmd, UpdateResult, WindowSize, Program ├── message.mbt # KeyMsg, MouseMsg, InputEvent ├── command.mbt # Command helpers and composition ├── program.mbt # Program[Model, Msg] entry point ├── ansi.mbt # ANSI escape sequence helpers ├── style.mbt # Style, Color, borders, and placement ├── view.mbt # col/row/lines/text/gap/hgap layout DSL ├── component/ # Composable component sub-package │ ├── moon.pkg │ └── ... # Spinner, textarea, viewport, progress, etc. └── examples/ ├── hello/ # Minimal example app ├── structured-view/ # View.content plus per-frame terminal state └── showcase/ # Rich interactive demo

#Development

moon check # Type-check the project moon fmt # Format all source files moon info # Refresh generated package interfaces moon test # Run all tests moon test --update # Run tests and update snapshots moon run src/examples/hello # Run the hello example moon run src/examples/structured-view # Run the structured View example moon run src/examples/showcase # Run the showcase demo

#License

Apache-2.0

#
Align

pub(all) enum Align {
Left
Center
Right
} derive(Eq,
Debug
)

Horizontal alignment within a styled width.

#
Binding

pub(all) struct Binding {
keys : Array[String]
help_key : String
help : String
enabled : Bool
} derive(Eq,
Debug
)

A named key binding with activation keys, display help, and enabled state.

#
Border

pub(all) struct Border {
top_left : String
top_right : String
bottom_left : String
bottom_right : String
horizontal : String
vertical : String
} derive(Eq,
Debug
)

Border characters used to render a box around styled content.

#
CapabilityReply

pub(all) struct CapabilityReply {
name : String
value : String?
valid : Bool
} derive(Eq,
Debug
)

XTGETTCAP terminal capability reply payload.

#
ClipboardPayload

pub(all) enum ClipboardPayload {
DecodedClipboardText(String)
RawClipboardData(String)
} derive(Eq,
Debug
)

Payload returned by an OSC 52 clipboard reply.

#
ClipboardReply

pub(all) struct ClipboardReply {
selection : ClipboardSelection
payload : ClipboardPayload
} derive(Eq,
Debug
)

OSC 52 clipboard reply payload.

#
ClipboardSelection

pub(all) enum ClipboardSelection {
SystemClipboard
PrimarySelection
SecondarySelection
CutBuffer(Int)
NamedSelection(String)
} derive(Eq,
Debug
)

Selection target used by OSC 52 clipboard queries and replies.

#
Cmd

type Cmd[Msg]

A batch of I/O commands to be performed by the runtime.

Cmd[Msg] represents one-shot runtime effects that may later produce a message of type Msg and feed it back into the update loop.

Commands are additive: when several updates happen in one runtime turn, the resulting commands are all enqueued rather than the "last" one replacing the others. Use Cmd::none() for no-ops and Cmd::batch to combine several commands. A batch of one-shot runtime effects.
impl Debug for Cmd[Msg]

#
Cmd::after

fn[Msg] Cmd::after(interval_ms : Int, f : () -> Msg) -> Cmd[Msg]

Schedule f() after interval_ms milliseconds. The runtime evaluates f when the timer fires, not when the command is created.

#
Cmd::batch

fn[Msg] Cmd::batch(cmds : Array[Cmd[Msg]]) -> Cmd[Msg]

Batch multiple commands so each effect is enqueued independently.

#
Cmd::clear_screen

fn[Msg] Cmd::clear_screen() -> Cmd[Msg]

Clear the screen and move the cursor to the home position.

#
Cmd::clear_window_title

fn[Msg] Cmd::clear_window_title() -> Cmd[Msg]

Clear the terminal window title at runtime.

#
Cmd::disable_bracketed_paste

fn[Msg] Cmd::disable_bracketed_paste() -> Cmd[Msg]

Disable terminal bracketed paste mode at runtime.

#
Cmd::disable_focus_reporting

fn[Msg] Cmd::disable_focus_reporting() -> Cmd[Msg]

Disable terminal focus reporting at runtime.

#
Cmd::disable_mouse

fn[Msg] Cmd::disable_mouse() -> Cmd[Msg]

Disable terminal mouse reporting at runtime.

#
Cmd::enable_bracketed_paste

fn[Msg] Cmd::enable_bracketed_paste() -> Cmd[Msg]

Enable terminal bracketed paste mode at runtime.

#
Cmd::enable_focus_reporting

fn[Msg] Cmd::enable_focus_reporting() -> Cmd[Msg]

Enable terminal focus reporting at runtime.

#
Cmd::enable_mouse_all_motion

fn[Msg] Cmd::enable_mouse_all_motion() -> Cmd[Msg]

Enable all terminal mouse motion reporting at runtime.

#
Cmd::enable_mouse_cell_motion

fn[Msg] Cmd::enable_mouse_cell_motion() -> Cmd[Msg]

Enable terminal mouse press, release, wheel, and button-held drag reporting at runtime.

#
Cmd::enter_alt_screen

fn[Msg] Cmd::enter_alt_screen() -> Cmd[Msg]

Enter the terminal alternate screen buffer at runtime.

#
Cmd::every

fn[Msg] Cmd::every(interval_ms : Int, f : (TimerTick) -> Msg) -> Cmd[Msg]

Schedule f once on the next aligned interval boundary.

Alignment is measured against the runtime's monotonic millisecond clock: the deadline is the next strict multiple of interval_ms. The callback receives a typed elapsed-time payload, and apps that want recurrence should return another Cmd::every from update.

#
Cmd::exec_process

fn[Msg] Cmd::exec_process(process : ExecProcess, to_msg : (ExecResult) -> Msg) -> Cmd[Msg]

Run an external process while the runtime releases the terminal.

#
Cmd::exit_alt_screen

fn[Msg] Cmd::exit_alt_screen() -> Cmd[Msg]

Exit the terminal alternate screen buffer at runtime.

#
Cmd::force_redraw

fn[Msg] Cmd::force_redraw() -> Cmd[Msg]

Invalidate the renderer cache so the next render fully repaints.

#
Cmd::hide_cursor

fn[Msg] Cmd::hide_cursor() -> Cmd[Msg]

Hide the terminal cursor at runtime.

#
Cmd::interrupt

fn[Msg] Cmd::interrupt() -> Cmd[Msg]

Ask the runtime to stop as interrupted.

#
Cmd::is_none

fn[Msg] Cmd::is_none(self : Cmd[Msg]) -> Bool

Returns true when the command carries no actions (equivalent to Cmd::none()).

#
Cmd::map

fn[A, B] Cmd::map(f : (A) -> B, cmd : Cmd[A]) -> Cmd[B]

Map the message type of a command, transforming runtime-produced messages.

#
Cmd::msg

fn[Msg] Cmd::msg(msg : Msg) -> Cmd[Msg]

Enqueue a message to be delivered immediately by the runtime.

#
Cmd::none

fn[Msg] Cmd::none() -> Cmd[Msg]

#
Cmd::perform

fn[Msg] Cmd::perform(f : () -> Msg?) -> Cmd[Msg]

Execute a runtime effect immediately after the current update cycle.

The runtime runs f on the event loop thread and feeds the resulting message back into update when Some(msg) is returned.

#
Cmd::print_above

fn[Msg] Cmd::print_above(message : String) -> Cmd[Msg]

Print unmanaged text above the program in inline terminal mode.

The printed text is not part of the managed view and may persist in terminal scrollback across later renders. The runtime prints each newline-delimited line on its own terminal line, then repaints the current view below it. When the alternate screen is active the request is discarded, matching Bubble Tea's Println / Printf behavior.

#
Cmd::printf

fn[Msg] Cmd::printf(template : String, args? : Array[String]) -> Cmd[Msg]

Format unmanaged text and print it above the program in inline terminal mode.

Pippa accepts pre-rendered string arguments and replaces %s placeholders in template from left to right; %% emits a literal percent sign. The result is then handled like Cmd::println, including the alt-screen no-op behavior.

#
Cmd::println

fn[Msg] Cmd::println(message : String) -> Cmd[Msg]

Print unmanaged text above the program in inline terminal mode.

This is the Pippa equivalent of Bubble Tea's Println: unlike writing from a view, the line is outside the managed render area and is ignored while the alternate screen is active.

#
Cmd::quit

fn[Msg] Cmd::quit() -> Cmd[Msg]

Ask the runtime to quit cleanly.

#
Cmd::repaint

fn[Msg] Cmd::repaint() -> Cmd[Msg]

Invalidate the renderer cache so the next render fully repaints.

#
Cmd::request_background_color

fn[Msg] Cmd::request_background_color() -> Cmd[Msg]

Request the current default background color (OSC 11).

#
Cmd::request_capability

fn[Msg] Cmd::request_capability(name : String) -> Cmd[Msg]

Request a terminal capability with XTGETTCAP.

#
Cmd::request_clipboard

fn[Msg] Cmd::request_clipboard(selection : ClipboardSelection) -> Cmd[Msg]

Request clipboard contents with OSC 52.

#
Cmd::request_cursor_position

fn[Msg] Cmd::request_cursor_position() -> Cmd[Msg]

Request the current cursor position (DSR CPR) from the terminal.

#
Cmd::request_device_attributes

fn[Msg] Cmd::request_device_attributes() -> Cmd[Msg]

Request primary device attributes (DA1) from the terminal.

#
Cmd::request_foreground_color

fn[Msg] Cmd::request_foreground_color() -> Cmd[Msg]

Request the current default foreground color (OSC 10).

#
Cmd::request_secondary_device_attributes

fn[Msg] Cmd::request_secondary_device_attributes() -> Cmd[Msg]

Request secondary device attributes / terminal version (DA2).

#
Cmd::request_window_size

fn[Msg] Cmd::request_window_size(f : (WindowSize) -> Msg) -> Cmd[Msg]

Ask the runtime to deliver the current terminal size as a typed message.

#
Cmd::resume_program

fn[Msg] Cmd::resume_program() -> Cmd[Msg]

Ask the runtime to deliver a resume lifecycle message.

#
Cmd::runtime_error

fn[Msg] Cmd::runtime_error(message : String) -> Cmd[Msg]

Stop the runtime with a typed runtime error result.

#
Cmd::sequence

fn[Msg] Cmd::sequence(cmds : Array[Cmd[Msg]]) -> Cmd[Msg]

Run commands in order.

Each command starts only after the prior command's immediate effects have been drained by the runtime. Delayed timers from earlier steps continue to run independently once scheduled.

#
Cmd::set_clipboard

fn[Msg] Cmd::set_clipboard(text : String) -> Cmd[Msg]

Set system clipboard contents with OSC 52.

#
Cmd::set_clipboard_for

fn[Msg] Cmd::set_clipboard_for(selection : ClipboardSelection, text : String) -> Cmd[Msg]

Set clipboard contents for an OSC 52 selection.

#
Cmd::set_cursor_style

fn[Msg] Cmd::set_cursor_style(style : CursorStyle) -> Cmd[Msg]

Set the real terminal cursor shape/blink style at runtime.

#
Cmd::set_window_title

fn[Msg] Cmd::set_window_title(title : String) -> Cmd[Msg]

Set the terminal window title at runtime.

#
Cmd::show_cursor

fn[Msg] Cmd::show_cursor() -> Cmd[Msg]

Show the terminal cursor at runtime.

#
Cmd::suspend

fn[Msg] Cmd::suspend() -> Cmd[Msg]

Ask the runtime to release and then re-acquire the terminal.

#
Cmd::tick

fn[Msg] Cmd::tick(interval_ms : Int) -> Cmd[Msg]

Schedule a one-shot tick — runtime calls on_tick when the timer fires.

Prefer Cmd::after when the timer should directly produce a specific message. Cmd::tick is mainly for app-wide compatibility with Program's on_tick hook.

#
Color

pub(all) enum Color {
Ansi(Int)
RGB(Int, Int, Int)
Hex(String)
Adaptive(Color, Color)
} derive(Eq,
Debug
)

Rich color representation used by the styling APIs.

#
CursorPositionReply

pub(all) struct CursorPositionReply {
row : Int
col : Int
} derive(Eq,
Debug
)

1-based cursor location reported by a terminal CPR reply.

#
CursorStyle

pub(all) enum CursorStyle {
Default
BlinkBlock
SteadyBlock
BlinkUnderline
SteadyUnderline
BlinkBar
SteadyBar
} derive(Eq,
Debug
)

Real terminal cursor shape/blink styles for DECSCUSR.

#
EnhancedKeyCode

pub(all) enum EnhancedKeyCode {
UnicodeKey(Int)
FunctionalKey(Int)
} derive(Eq,
Debug
)

Numeric key identity reported by enhanced terminal keyboard protocols.

#
EnhancedKeyEventType

pub(all) enum EnhancedKeyEventType {
KeyPress
KeyRepeat
KeyRelease
} derive(Eq,
Debug
)

Press, repeat, or release phase for an enhanced keyboard event.

#
EnhancedKeyModifier

pub(all) enum EnhancedKeyModifier {
KeyShift
KeyAlt
KeyCtrl
KeySuper
KeyHyper
KeyMeta
KeyCapsLock
KeyNumLock
} derive(Eq,
Debug
)

Canonical modifiers reported by enhanced terminal keyboard protocols.

#
EnhancedKeyMsg

pub(all) struct EnhancedKeyMsg {
code : EnhancedKeyCode
shifted : EnhancedKeyCode?
base_layout : EnhancedKeyCode?
modifiers : Array[EnhancedKeyModifier]
event_type : EnhancedKeyEventType
text_codepoints : Array[Int]
} derive(Eq,
Debug
)

Keyboard event data from Kitty / CSI-u enhanced keyboard reports.

#
ExecProcess

pub(all) struct ExecProcess {
command : String
args : Array[String]
} derive(Eq,
Debug
)

External process request run by Cmd::exec_process.

#
ExecProcess::new

fn ExecProcess::new(command~ : String, args? : Array[String]) -> ExecProcess

Create an external process request.

#
ExecResult

pub(all) enum ExecResult {
ExecCompleted(Int)
ExecCancelled
ExecSignaled(Int)
ExecError(String)
} derive(Eq,
Debug
)

Result of an external process run.

#
HelpModel

pub(all) struct HelpModel {
keymap : KeyMap
show_full : Bool
width : Int
} derive(Eq,
Debug
)

A help model for rendering keybinding hints at the bottom of a view.

#
InputEvent

pub(all) enum InputEvent {
Key(KeyMsg)
EnhancedKey(EnhancedKeyMsg)
Mouse(MouseMsg)
Focus
Blur
Paste(String)
TerminalReply(TerminalReplyMsg)
Unknown(Bytes)
} derive(Eq,
Debug
)

A parsed terminal input event.

#
InternalMsg

pub(all) enum InternalMsg {
KeyPress(Array[Byte])
WindowResize(WindowSize)
Lifecycle(LifecycleMsg)
QuitRequested
} derive(Eq,
Debug
)

Built-in messages that the Pippa runtime can emit.

Users extend their own Msg enum to wrap InternalMsg when they need to react to terminal events.

#
KeyMap

pub(all) struct KeyMap {
groups : Array[KeyMapGroup]
} derive(Eq,
Debug
)

Ordered groups of key bindings for matching and help rendering.

#
KeyMapGroup

pub(all) struct KeyMapGroup {
title : String
bindings : Array[Binding]
} derive(Eq,
Debug
)

A named group of bindings, rendered as a section in full help.

#
KeyMsg

pub(all) enum KeyMsg {
Char(Char)
Text(String)
Special(String)
Modified(String, String)
} derive(Eq,
Debug
)

Represents a keyboard event.

#
LifecycleMsg

pub(all) enum LifecycleMsg {
Quit
Interrupt
Suspend
Resume
} derive(Eq,
Debug
)

Lifecycle messages emitted by the runtime and lifecycle commands.

#
MouseAction

pub(all) enum MouseAction {
MousePress
MouseRelease
MouseMotion
MouseWheel
} derive(Eq,
Debug
)

Mouse event action.

#
MouseButton

pub(all) enum MouseButton {
MouseLeft
MouseMiddle
MouseRight
MouseNoButton
MouseWheelUp
MouseWheelDown
MouseWheelLeft
MouseWheelRight
} derive(Eq,
Debug
)

Mouse button, no-button motion sentinel, or wheel scroll direction.

#
MouseModifier

pub(all) enum MouseModifier {
MouseShift
MouseAlt
MouseCtrl
} derive(Eq,
Debug
)

Keyboard modifiers attached to a mouse event.

#
MouseMsg

pub(all) struct MouseMsg {
button : MouseButton
action : MouseAction
modifiers : Array[MouseModifier]
col : Int
row : Int
} derive(Eq,
Debug
)

Represents a mouse event.

#
Nothing

pub(all) struct Nothing {
} derive(Eq,
Debug
)

Sentinel type for commands that carry no payload.

#
ParseResult

pub(all) enum ParseResult[T] {
Ok(T, Int)
Incomplete
Invalid(String)
} derive(Eq,
Debug
)

Result of attempting to parse one event from a byte buffer.

#
PatchOp

pub(all) struct PatchOp {
row : Int
col : Int
text : String
} derive(Eq,
Debug
)

A single patch operation: move to (row, col) and replace the line with text.

#
Program

pub(all) struct Program[Model, Msg] {
init : () -> UpdateResult[Model, Msg]
update : (Model, Msg) -> UpdateResult[Model, Msg]
view : (Model) -> String
structured_view : (Model) -> View?
sub : (InputEvent) -> Msg?
on_tick : () -> Msg?
mouse : Bool
alt_screen : Bool
focus_reporting : Bool
bracketed_paste : Bool
hide_cursor : Bool
synchronized_output : Bool
rendering : Bool
read_stdin : Bool
input : () -> Array[InputEvent]?
raw_input : () -> Bytes?
cancelled : () -> Bool?
fps : Int
window_width : Int
window_height : Int
output : (String) -> Unit
on_window_size : (WindowSize) -> Msg?
on_resize : (Int, Int) -> Msg?
should_quit : (Model) -> Bool
catch_interrupt : Bool
on_lifecycle : (LifecycleMsg) -> Msg?
exec_runner : (ExecProcess) -> ExecResult
suspend_runner : () -> Unit
}

Configuration for a Pippa application.

#
Program::headless

fn[Model, Msg] Program::headless(self : Program[Model, Msg]) -> Program[Model, Msg]

#
Program::new

fn[Model, Msg] Program::new(init~ : () -> UpdateResult[Model, Msg], update~ : (Model, Msg) -> UpdateResult[Model, Msg], view~ : (Model) -> String, sub? : (InputEvent) -> Msg?, on_tick? : () -> Msg?, mouse? : Bool, alt_screen? : Bool, focus_reporting? : Bool, bracketed_paste? : Bool, hide_cursor? : Bool, synchronized_output? : Bool, rendering? : Bool, read_stdin? : Bool, input? : () -> Array[InputEvent]?, raw_input? : () -> Bytes?, cancelled? : () -> Bool?, fps? : Int, window_width? : Int, window_height? : Int, output? : (String) -> Unit, on_resize? : (Int, Int) -> Msg?, should_quit? : (Model) -> Bool, catch_interrupt? : Bool, on_window_size? : (WindowSize) -> Msg?, on_lifecycle? : (LifecycleMsg) -> Msg?, exec_runner? : (ExecProcess) -> ExecResult, suspend_runner? : () -> Unit) -> Program[Model, Msg]

Create a new Program from init, update, and view functions.

#
Program::run

fn[Model, Msg] Program::run(program : Program[Model, Msg]) -> RunResult[Model]

Run the program.

On native and llvm targets this enters raw terminal mode and drives the main event loop. On other targets this initializes the model, exposes an already-closed handle to run_with_handle, and returns RunCompleted.

#
Program::run_with_handle

fn[Model, Msg] Program::run_with_handle(program : Program[Model, Msg], on_start : (ProgramHandle[Msg]) -> Unit) -> RunResult[Model]

Run the program and expose a handle that can inject messages while it is running.

#
Program::with_alt_screen

fn[Model, Msg] Program::with_alt_screen(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_bracketed_paste

fn[Model, Msg] Program::with_bracketed_paste(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_cancelled

fn[Model, Msg] Program::with_cancelled(self : Program[Model, Msg], cancelled : () -> Bool) -> Program[Model, Msg]

#
Program::with_catch_interrupt

fn[Model, Msg] Program::with_catch_interrupt(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_exec_runner

fn[Model, Msg] Program::with_exec_runner(self : Program[Model, Msg], exec_runner : (ExecProcess) -> ExecResult) -> Program[Model, Msg]

#
Program::with_focus_reporting

fn[Model, Msg] Program::with_focus_reporting(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_fps

fn[Model, Msg] Program::with_fps(self : Program[Model, Msg], fps : Int) -> Program[Model, Msg]

#
Program::with_hide_cursor

fn[Model, Msg] Program::with_hide_cursor(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_input

fn[Model, Msg] Program::with_input(self : Program[Model, Msg], input : () -> Array[InputEvent]) -> Program[Model, Msg]

#
Program::with_lifecycle

fn[Model, Msg] Program::with_lifecycle(self : Program[Model, Msg], on_lifecycle : (LifecycleMsg) -> Msg?) -> Program[Model, Msg]

#
Program::with_output

fn[Model, Msg] Program::with_output(self : Program[Model, Msg], output : (String) -> Unit) -> Program[Model, Msg]

#
Program::with_raw_input

fn[Model, Msg] Program::with_raw_input(self : Program[Model, Msg], raw_input : () -> Bytes) -> Program[Model, Msg]

#
Program::with_rendering

fn[Model, Msg] Program::with_rendering(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_stdin

fn[Model, Msg] Program::with_stdin(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_structured_view

fn[Model, Msg] Program::with_structured_view(self : Program[Model, Msg], structured_view : (Model) -> View) -> Program[Model, Msg]

Install an opt-in structured view while preserving the plain string view as a fallback for callers that do not need per-frame terminal state.

#
Program::with_suspend_runner

fn[Model, Msg] Program::with_suspend_runner(self : Program[Model, Msg], suspend_runner : () -> Unit) -> Program[Model, Msg]

#
Program::with_synchronized_output

fn[Model, Msg] Program::with_synchronized_output(self : Program[Model, Msg], enabled : Bool) -> Program[Model, Msg]

#
Program::with_window_size

fn[Model, Msg] Program::with_window_size(self : Program[Model, Msg], width : Int, height : Int) -> Program[Model, Msg]

#
ProgramHandle

type ProgramHandle[Msg]

Handle for sending messages to a running program.

send is intended for the same MoonBit runtime thread. Native cross-thread delivery of generic MoonBit values is not supported by the runtime today.

#
ProgramHandle::is_closed

fn[Msg] ProgramHandle::is_closed(self : ProgramHandle[Msg]) -> Bool

Returns true after the program has exited and the handle can no longer send.

#
ProgramHandle::release_terminal

fn[Msg] ProgramHandle::release_terminal(self : ProgramHandle[Msg]) -> Bool

Temporarily restore the terminal to its outer shell state.

#
ProgramHandle::restore_terminal

fn[Msg] ProgramHandle::restore_terminal(self : ProgramHandle[Msg]) -> Bool

Re-enter Pippa's managed terminal state after release_terminal.

#
ProgramHandle::send

fn[Msg] ProgramHandle::send(self : ProgramHandle[Msg], msg : Msg) -> Bool

Enqueue a message for delivery to the running program.

#
RunResult

pub(all) enum RunResult[Model] {
RunCompleted(Model)
RunQuit(Model)
RunInterrupted(Model)
RunRuntimeError(String)
RunExec(Model, ExecResult)
} derive(
Debug
)

Typed result returned by Program::run.

#
Style

pub(all) struct Style {
fg : Int?
bg : Int?
fg_ext : Color?
bg_ext : Color?
border_fg : Color?
border_bg : Color?
bold : Bool
dim : Bool
italic : Bool
underline : Bool
blink : Bool
reverse : Bool
strikethrough : Bool
border : Border?
border_top : Bool
border_right : Bool
border_bottom : Bool
border_left : Bool
padding_top : Int
padding_bottom : Int
padding_left : Int
padding_right : Int
margin_top : Int
margin_bottom : Int
margin_left : Int
margin_right : Int
width : Int
height : Int?
min_width : Int
min_height : Int
max_width : Int?
max_height : Int?
align : Align
} derive(Eq,
Debug
)

A composable style description for terminal strings.

#
Style::align

fn Style::align(self : Style, a : Align) -> Style

#
Style::background

fn Style::background(self : Style, color : Color) -> Style

#
Style::background_hex

fn Style::background_hex(self : Style, hex : String) -> Style

#
Style::background_rgb

fn Style::background_rgb(self : Style, r : Int, g : Int, b : Int) -> Style

#
Style::bg_color

fn Style::bg_color(self : Style, n : Int) -> Style

fn Style::blink(self : Style) -> Style

#
Style::bold

fn Style::bold(self : Style) -> Style

#
Style::border

fn Style::border(self : Style, b : Border) -> Style

#
Style::border_background

fn Style::border_background(self : Style, color : Color) -> Style

#
Style::border_foreground

fn Style::border_foreground(self : Style, color : Color) -> Style

#
Style::border_sides

fn Style::border_sides(self : Style, top : Bool, right : Bool, bottom : Bool, left : Bool) -> Style

#
Style::dim

fn Style::dim(self : Style) -> Style

#
Style::faint

fn Style::faint(self : Style) -> Style

#
Style::fg_color

fn Style::fg_color(self : Style, n : Int) -> Style

#
Style::foreground

fn Style::foreground(self : Style, color : Color) -> Style

#
Style::foreground_hex

fn Style::foreground_hex(self : Style, hex : String) -> Style

#
Style::foreground_rgb

fn Style::foreground_rgb(self : Style, r : Int, g : Int, b : Int) -> Style

#
Style::height

fn Style::height(self : Style, h : Int) -> Style

#
Style::italic

fn Style::italic(self : Style) -> Style

#
Style::margin

fn Style::margin(self : Style, top : Int, right : Int, bottom : Int, left : Int) -> Style

#
Style::max_height

fn Style::max_height(self : Style, height : Int) -> Style

#
Style::max_width

fn Style::max_width(self : Style, width : Int) -> Style

#
Style::min_height

fn Style::min_height(self : Style, height : Int) -> Style

#
Style::min_width

fn Style::min_width(self : Style, width : Int) -> Style

#
Style::padding

fn Style::padding(self : Style, top : Int, right : Int, bottom : Int, left : Int) -> Style

#
Style::render

fn Style::render(self : Style, content : String) -> String

#
Style::render_with

fn Style::render_with(self : Style, content : String, ctx :
RenderContext
) -> String

#
Style::reverse

fn Style::reverse(self : Style) -> Style

#
Style::strikethrough

fn Style::strikethrough(self : Style) -> Style

#
Style::underline

fn Style::underline(self : Style) -> Style

#
Style::width

fn Style::width(self : Style, w : Int) -> Style

#
TerminalColorReply

pub(all) enum TerminalColorReply {
ForegroundColor(String)
BackgroundColor(String)
PaletteColor(Int, String)
} derive(Eq,
Debug
)

Color value reported by OSC terminal color queries.

#
TerminalReplyMsg

pub(all) enum TerminalReplyMsg {
CursorPosition(CursorPositionReply)
DeviceAttributes(Array[Int])
SecondaryDeviceAttributes(Array[Int])
Color(TerminalColorReply)
Capability(CapabilityReply)
Clipboard(ClipboardReply)
UnknownReply(Bytes)
} derive(Eq,
Debug
)

Replies emitted by terminal query sequences.

#
TimerTick

pub(all) struct TimerTick {
elapsed_ms : Int64
} derive(Eq,
Debug
)

Typed payload delivered to scheduler callbacks.

#
UpdateResult

pub(all) struct UpdateResult[Model, Msg] {
model : Model
cmd : Cmd[Msg]
} derive(
Debug
)

The result of an update call: the new model and an optional command.

#
UpdateResult::new

fn[Model, Msg] UpdateResult::new(model~ : Model, cmd~ : Cmd[Msg]) -> UpdateResult[Model, Msg]

Create an UpdateResult from a model and a command.

#
VerticalAlign

pub(all) enum VerticalAlign {
Top
Middle
Bottom
} derive(Eq,
Debug
)

Vertical alignment for layout helpers.

#
View

pub(all) struct View {
content : String
cursor_position : ViewCursorPosition?
cursor_visible : Bool?
window_title : String?
cursor_style : CursorStyle?
foreground_color : Color?
background_color : Color?
progress : ViewProgress?
mouse_mode : ViewMouseMode?
} derive(Eq,
Debug
)

Structured render output for a single frame.

content is rendered exactly like the string returned by a traditional view function. The optional fields describe terminal state that should travel with the frame; emitting escape sequences for those fields is handled by later render plumbing.

#
View::new

fn View::new(content~ : String, cursor_position? : ViewCursorPosition?, cursor_visible? : Bool?, window_title? : String?, foreground_color? : Color?, background_color? : Color?, progress? : ViewProgress?, mouse_mode? : ViewMouseMode?, cursor_style? : CursorStyle?) -> View

Create a structured View with explicit optional terminal state.

#
View::text

fn View::text(content : String) -> View

Wrap plain string content as a structured View.

#
View::with_background_color

fn View::with_background_color(self : View, color : Color) -> View

Set the default background color for this frame.

#
View::with_cursor_position

fn View::with_cursor_position(self : View, row~ : Int, col~ : Int) -> View

Set the cursor position for this frame.

#
View::with_cursor_style

fn View::with_cursor_style(self : View, style : CursorStyle) -> View

Set the real terminal cursor shape/blink style for this frame.

#
View::with_cursor_visible

fn View::with_cursor_visible(self : View, visible : Bool) -> View

Set cursor visibility for this frame.

#
View::with_foreground_color

fn View::with_foreground_color(self : View, color : Color) -> View

Set the default foreground color for this frame.

#
View::with_mouse_mode

fn View::with_mouse_mode(self : View, mouse_mode : ViewMouseMode) -> View

Set the mouse reporting mode for this frame.

#
View::with_progress

fn View::with_progress(self : View, progress : ViewProgress) -> View

Set terminal progress state for this frame.

#
View::with_window_title

fn View::with_window_title(self : View, title : String) -> View

Set the terminal window title for this frame.

#
ViewCursorPosition

pub(all) struct ViewCursorPosition {
row : Int
col : Int
} derive(Eq,
Debug
)

Cursor position requested by a structured View.

#
ViewCursorPosition::new

fn ViewCursorPosition::new(row~ : Int, col~ : Int) -> ViewCursorPosition

Create a 1-based cursor position, clamping invalid terminal coordinates to 1.

#
ViewMouseMode

pub(all) enum ViewMouseMode {
ViewMouseOff
ViewMousePress
ViewMouseCellMotion
ViewMouseAllMotion
} derive(Eq,
Debug
)

Mouse reporting mode requested by a structured View.

#
ViewProgress

pub(all) enum ViewProgress {
ViewProgressNone
ViewProgressIndeterminate
ViewProgressPercent(Int)
ViewProgressPaused(Int?)
ViewProgressError(Int?)
} derive(Eq,
Debug
)

Per-render terminal progress state carried by a structured View.

#
WindowSize

pub(all) struct WindowSize {
width : Int
height : Int
} derive(Eq,
Debug
)

A window resize event payload.

#
CSI

let CSI : String

CSI prefix (ESC [).

#
DCS

let DCS : String

DCS prefix (ESC P).

#
ESC

let ESC : String

Escape character.

#
OSC

let OSC : String

OSC prefix (ESC ]).
let ST : String

String terminator (ESC \).

#
begin_sync_update

fn begin_sync_update() -> String

Begin synchronized output mode (CSI ? 2026 h).

#
bg_color

fn bg_color(n : Int) -> String

Set the background color (256-color mode: CSI 48 ; 5 ; n m).

#
bg_rgb

fn bg_rgb(r : Int, g : Int, b : Int) -> String

Set the background to an RGB color (CSI 48 ; 2 ; r ; g ; b m).

#
binding

fn binding(keys? : Array[String], help_key? : String, help? : String) -> Binding

fn blink() -> String

Blinking text (CSI 5 m).

#
bold

fn bold() -> String

Bold text (CSI 1 m).

#
border_ascii

fn border_ascii() -> Border

#
border_double

fn border_double() -> Border

#
border_normal

fn border_normal() -> Border

#
border_rounded

fn border_rounded() -> Border

#
border_thick

fn border_thick() -> Border

#
center

fn center(s : String, width : Int) -> String

Center s within a field of width visible characters by padding with spaces on both sides. Returns s unchanged if its visible width is already ≥ width.

#
clear_line

fn clear_line() -> String

Clear from cursor to end of line (CSI K).

#
clear_screen

fn clear_screen() -> String

Clear the entire screen (CSI 2 J).

#
clear_screen_below

fn clear_screen_below() -> String

Clear from cursor to end of screen (CSI J).

#
clear_terminal_progress

fn clear_terminal_progress() -> String

Clear terminal progress using OSC 9;4 state 0.

#
clear_window_title

fn clear_window_title() -> String

Clear the terminal window title using OSC 2 with an empty title.

#
col

fn col(children : Array[String]) -> String

Vertical layout — stacks children separated by newlines. Designed for use with the <| operator:

col <| [ "header", row <| ["a", " ", "b"], "footer", ]

#
color_adaptive

fn color_adaptive(light : Color, dark : Color) -> Color

#
color_ansi

fn color_ansi(n : Int) -> Color

#
color_hex

fn color_hex(hex : String) -> Color

#
color_rgb

fn color_rgb(r : Int, g : Int, b : Int) -> Color

#
diff_views

fn diff_views(old_view : String, new_view : String) -> Array[PatchOp]

Compare two view strings line-by-line and return the minimal set of PatchOps needed to transform the old view into the new view.

  • Changed or added lines → emit a PatchOp with the new line text.
  • Lines present in old_view but absent in new_view → emit a PatchOp with text = "" to clear the stale line.

#
dim

fn dim() -> String

Dim / faint text (CSI 2 m).

#
disable_bracketed_paste

fn disable_bracketed_paste() -> String

Disable bracketed paste mode (CSI ?2004l).

#
disable_focus_reporting

fn disable_focus_reporting() -> String

Disable focus reporting (CSI ?1004l).

#
disable_mouse

fn disable_mouse() -> String

Disable all mouse reporting (CSI ?1000l, CSI ?1002l, CSI ?1003l, and CSI ?1006l).

#
down_binding

fn down_binding() -> Binding

#
enable_bracketed_paste

fn enable_bracketed_paste() -> String

Enable bracketed paste mode (CSI ?2004h).

#
enable_focus_reporting

fn enable_focus_reporting() -> String

Enable focus reporting (CSI ?1004h).

#
enable_mouse

fn enable_mouse() -> String

Enable normal mouse reporting with SGR coordinates (CSI ?1000h CSI ?1006h).

#
enable_mouse_cell_motion

fn enable_mouse_cell_motion() -> String

Enable mouse button-event reporting with SGR coordinates (CSI ?1002h CSI ?1006h).

#
enable_mouse_motion

fn enable_mouse_motion() -> String

Enable mouse motion reporting with SGR coordinates (CSI ?1003h CSI ?1006h).

#
end_sync_update

fn end_sync_update() -> String

End synchronized output mode (CSI ? 2026 l).

#
enter_alt_screen

fn enter_alt_screen() -> String

Enter the alternate screen buffer (CSI ? 1049 h).

#
enter_binding

fn enter_binding() -> Binding

#
escape_binding

fn escape_binding() -> Binding

#
exit_alt_screen

fn exit_alt_screen() -> String

Exit the alternate screen buffer and restore the primary screen (CSI ? 1049 l).

#
fg_color

fn fg_color(n : Int) -> String

Set the foreground color (256-color mode: CSI 38 ; 5 ; n m).

#
fg_rgb

fn fg_rgb(r : Int, g : Int, b : Int) -> String

Set the foreground to an RGB color (CSI 38 ; 2 ; r ; g ; b m).

#
gap

fn gap(n? : Int) -> String

Produces n blank lines for vertical spacing inside a col. gap() is equivalent to "" — a single blank line in vertical layout. gap(n=2) produces two blank lines ("\n"), and so on.

#
help_binding

fn help_binding() -> Binding

#
help_model

fn help_model(keymap~ : KeyMap, width? : Int, show_full? : Bool) -> HelpModel

#
help_view

fn help_view(model : HelpModel) -> String

#
hgap

fn hgap(n? : Int) -> String

Produces n spaces for horizontal spacing inside a row.

#
hide_cursor

fn hide_cursor() -> String

Hide the cursor (CSI ? 25 l).
fn hyperlink(url : String, label : String) -> String

Wrap a label in an OSC 8 hyperlink with empty parameters.

#
join_horizontal

fn join_horizontal(blocks : Array[String], valign? : VerticalAlign) -> String

#
join_vertical

fn join_vertical(blocks : Array[String], align? : Align) -> String

#
keymap

fn keymap(bindings : Array[Binding]) -> KeyMap

#
keymap_bindings

fn keymap_bindings(keymap : KeyMap) -> Array[Binding]

#
keymap_from_groups

fn keymap_from_groups(groups : Array[KeyMapGroup]) -> KeyMap

#
keymap_group

fn keymap_group(title : String, bindings : Array[Binding]) -> KeyMapGroup

#
left_binding

fn left_binding() -> Binding

#
line_count

fn line_count(s : String) -> Int

Return the number of lines in s (equivalent to split_lines(s).length()).

#
lines

fn lines(children : Array[String]) -> String

Simple newline join — no width normalization or alignment. Lighter than col when blocks are already uniform width.

#
matches

fn matches(binding : Binding, key : KeyMsg) -> Bool

Check if a KeyMsg matches an enabled Binding.

#
max_line_width

fn max_line_width(s : String) -> Int

Return the maximum visible width across all lines in s. Returns 0 for an empty string.

#
move_cursor

fn move_cursor(row : Int, col : Int) -> String

Move cursor to (row, col), 1-based (CSI row ; col H).

#
pad_left

fn pad_left(s : String, width : Int) -> String

Left-pad s with spaces until its visible width equals width. Returns s unchanged if its visible width is already ≥ width.

#
pad_right

fn pad_right(s : String, width : Int) -> String

Right-pad s with spaces until its visible width equals width. Returns s unchanged if its visible width is already ≥ width.

#
parse_all

fn parse_all(bv : BytesView) -> (Array[InputEvent], Bytes)

Drain all complete InputEvents from bv.

Returns a tuple of the parsed events and any leftover bytes that formed an incomplete sequence at the end of the buffer.

#
parse_input

fn parse_input(bv : BytesView) -> ParseResult[InputEvent]

Parse one InputEvent from the front of bv.

Returns Ok(event, bytes_consumed) on success, Incomplete when more bytes are needed to complete the current sequence, or Invalid when a CSI sequence contains a definitively malformed parameter byte. Unrecognised but structurally valid sequences are returned as Ok(Unknown(...), n) so callers can distinguish protocol errors from merely unknown sequences.

#
place

fn place(width : Int, height : Int, content : String, align? : Align, valign? : VerticalAlign) -> String

#
quit_binding

fn quit_binding() -> Binding

#
render_full

fn render_full(view : String, prev_lines : Int) -> String

Render a complete view string to the terminal using cursor positioning.

Splits view on newlines and emits move_cursor + clear_line + text for every line, then clears any remaining lines below down to prev_lines. This avoids writing raw \n bytes in raw terminal mode.

#
render_patch

fn render_patch(ops : Array[PatchOp]) -> String

Convert a list of PatchOps into a single ANSI escape string that, when written to the terminal, applies all the patches in order.

Each op moves the cursor to (row, col), clears to end-of-line, then writes text. Returns "" when ops is empty.

#
request_background_color

fn request_background_color() -> String

Request the current default background color (OSC 11).

#
request_capability

fn request_capability(name : String) -> String

Request an XTGETTCAP capability by name.

#
request_clipboard

fn request_clipboard(selection : ClipboardSelection) -> String

Request clipboard contents from an OSC 52 selection.

#
request_cursor_position

fn request_cursor_position() -> String

Request the current cursor position (DSR CPR).

#
request_device_attributes

fn request_device_attributes() -> String

Request primary device attributes (DA1).

#
request_foreground_color

fn request_foreground_color() -> String

Request the current default foreground color (OSC 10).

#
request_secondary_device_attributes

fn request_secondary_device_attributes() -> String

Request secondary device attributes / terminal version (DA2).

#
reset

fn reset() -> String

Reset all attributes (CSI 0 m).

#
reset_background_color

fn reset_background_color() -> String

Reset the background color to the terminal default (CSI 49 m).

#
reset_foreground_color

fn reset_foreground_color() -> String

Reset the foreground color to the terminal default (CSI 39 m).

#
reverse

fn reverse() -> String

Reverse video (CSI 7 m).

#
right_binding

fn right_binding() -> Binding

#
row

fn row(children : Array[String]) -> String

Horizontal layout — places children side by side. Designed for use with the <| operator:

row <| [ stat_card("PHASE", label), " ", stat_card("TICKS", count), ]

#
set_clipboard

fn set_clipboard(selection : ClipboardSelection, text : String) -> String

Set clipboard contents for an OSC 52 selection.

#
set_cursor_style

fn set_cursor_style(style : CursorStyle) -> String

Set the real terminal cursor shape/blink using DECSCUSR (CSI Ps SP q).

#
set_disabled

fn set_disabled(binding : Binding, disabled : Bool) -> Binding

#
set_help

fn set_help(binding : Binding, help_key : String, help : String) -> Binding

#
set_keys

fn set_keys(binding : Binding, keys : Array[String]) -> Binding

#
set_terminal_progress

fn set_terminal_progress(state : Int, value : Int) -> String

Set terminal progress using the ConEmu / Windows Terminal OSC 9;4 protocol.

#
set_window_title

fn set_window_title(title : String) -> String

Set the terminal window title using OSC 2.

#
show_cursor

fn show_cursor() -> String

Show the cursor (CSI ? 25 h).

#
split_lines

fn split_lines(s : String) -> Array[String]

Split s on newline characters.

"a\nb\n"["a", "b", ""] ""[""]

#
strikethrough

fn strikethrough() -> String

Strikethrough text (CSI 9 m).

#
strip_ansi

fn strip_ansi(s : String) -> String

Remove recognized ANSI escape sequences from s.

Handles CSI sequences (ESC [ … letter), OSC sequences (ESC ] … BEL/ST), and bare two-char sequences (ESC followed by any single non-[/] character).

#
style

fn style() -> Style

#
text

fn text(s : String) -> String

Identity function — returns the input string unchanged. Exists for readability in DSL expressions where a bare string literal would be unclear.

#
toggle_help

fn toggle_help(model : HelpModel) -> HelpModel

#
truncate

fn truncate(s : String, max_width : Int) -> String

Truncate s so that its visible width is at most max_width.

ANSI-aware: escape sequences are never split mid-sequence and do not themselves consume any visible width.

#
unbind

fn unbind(binding : Binding) -> Binding

#
underline

fn underline() -> String

Underlined text (CSI 4 m).

#
up_binding

fn up_binding() -> Binding

#
visible_width

fn visible_width(s : String) -> Int

Return the visible terminal-cell width of s, ignoring ANSI escape sequences and treating wide/combining/grapheme clusters as terminal cells.

#
word_wrap

fn word_wrap(text : String, width : Int) -> String

Wrap text to width visible terminal cells at word boundaries.

CSI, OSC, and bare two-byte ESC sequences follow visible_width's zero-width escape handling, and printable text is walked on @internal.grapheme_boundaries, so hard breaks never split a grapheme cluster. Newlines already present in text delimit independent paragraphs. A non-positive width returns text unchanged.