tty

Low-level terminal primitives for MoonBit native targets, including tty state, platform stdio handles, VT command output, and host input decoding.

tty
terminal
ansi
vt
async
native
moon add moonbit-community/tty@0.3.0
Download zip
Version
0.3.0
License
Apache-2.0
Last updated
last month
Downloads
18K

Dependencies

README

#moonbit-community/tty

Low-level terminal primitives for MoonBit.

moonbit-community/tty provides a small foundation for programs that need to talk to a real terminal: tty detection, raw mode, terminal size queries, output commands, color values, and host input decoding. It is intentionally below a full TUI framework. It does not own a screen model, layout engine, widget tree, pane system, or scrollback buffer.

#Status

This module is early and native-target focused. Public APIs are still being shaped around small terminal programs and the examples in this repository.

#Packages

#moonbit-community/tty

The root package owns coordinated terminal handles and stateful terminal operations.

Use Tty when an operation needs a real terminal handle:

  • process stdio: Tty::stdio()
  • controlling terminal: Tty::open()
  • custom handles: Tty::new(input, output) with Reader and Writer traits for async files, stdio, and OS pipes
  • raw mode: Tty::get_state, State::make_raw, Tty::set_state, Tty::with_raw_mode
  • terminal size: Tty::window_size
  • cursor position report: Tty::query_cursor_position
  • kitty keyboard protocol support query: Tty::query_kitty_keyboard_support
  • dynamic color queries: Tty::query_default_foreground_color, Tty::query_default_background_color, and Tty::query_cursor_color
  • terminal events: Tty::read_event
  • output commands: cursor movement/visibility, line/display/scrollback erase, scroll margins, reverse index, alternate screen, bracketed paste mode, SGR mouse tracking, focus tracking, DEC auto wrap mode, foreground/background colors, text attributes, and style reset

Raw file and stdio byte I/O should use moonbitlang/async/fs and moonbitlang/async/stdio directly. The root package does not wrap them as stdin, stdout, or stderr.

VT/ANSI byte construction is an internal implementation detail. Downstream callers should use root Tty output command methods instead of importing byte-sequence helpers directly.

#moonbit-community/tty/input

User input event values.

The package contains key, paste, mouse, focus, modifier, and unknown-input values reported through root Tty::read_event. Terminal response traffic such as cursor position reports and kitty keyboard detection replies is consumed by root query methods and is not part of the public input event model. Root callers that are working with a terminal should use Tty::read_event so input, resize notifications, and terminal request/response traffic share the same coordinated terminal handle.

When bracketed paste mode is enabled, complete valid UTF-8 paste payloads are reported as one paste input event. When SGR mouse tracking is enabled, 1006 mouse reports are decoded as cell-coordinate mouse input events. When focus tracking is enabled, focus in and focus out reports are decoded as input events.

#moonbit-community/tty/color

Semantic terminal color values.

The package represents default, ANSI basic/bright, indexed 256-color, RGB truecolor values, and 16-bit RGB values returned by terminal color queries. Root Tty methods turn Color values into SGR output. Terminal dynamic color queries live on root Tty because they coordinate output requests with input-stream responses.

#Usage

Add the module and import the packages you need in moon.pkg:

import {
"moonbit-community/tty"
"moonbit-community/tty/color" @tty/color
"moonbit-community/tty/input" @tty/input
}

Write terminal commands through a Tty:

async fn main {
let tty = @tty.Tty::stdio()
tty.hide_cursor()
tty.set_cursor_position(1, 1)
tty.bold()
tty.set_foreground(Basic(Green))
tty.write("hello from tty\r\n")
tty.reset_style()
tty.show_cursor()
}

Run a scoped raw-mode input loop:

async fn main {
let tty = @tty.Tty::stdio()
tty.with_raw_mode(() => {
tty.write("press q to quit\r\n")
while true {
match tty.read_event() {
Input(Key(key)) =>
match key.code {
Char('q') => break
_ => ()
}
Input(Paste(_)) => ()
Input(Mouse(_)) => ()
Input(FocusIn) => ()
Input(FocusOut) => ()
Input(Unknown(_)) => ()
Resize(_) => ()
}
}
})
}

#Examples

Examples live in a separate workspace member under examples/.

moon run examples/raw moon run examples/input moon run examples/color moon run examples/cursor moon run examples/pager -- README.md moon run examples/agent

The examples are manual validation tools, not framework APIs:

  • examples/raw checks raw mode behavior.
  • examples/input prints decoded key, paste, mouse, focus, and resize events as they arrive.
  • examples/color prints a color specimen.
  • examples/cursor draws with cursor movement and erase sequences.
  • examples/pager demonstrates primary-screen paging with a fixed status row.
  • examples/agent demonstrates a Codex-like primary-screen transcript, input composer, delayed queued input, and shell command execution.

#Design Boundaries

  • tty owns terminal handles, platform state, raw mode, terminal size, cursor position queries, dynamic color queries, and command-style terminal operations.
  • internal VT helpers only build byte sequences for root Tty methods.
  • input contains user input event values.
  • internal input decoding turns host input bytes into root terminal events and private terminal response events.
  • color only represents semantic color values.
  • Higher-level editing, layout, widgets, screen rendering, and terminal-emulator state belong outside this module unless the project plan changes.

See docs/architecture.md and docs/plan.md for the current design notes and execution board.

#Development

Common validation commands:

moon fmt moon test . moon test moon check moon info

Review pkg.generated.mbti after moon info; it is the public API surface.

pub(open) trait Fd {
fn fd(Self) -> Int
}

A handle that exposes an OS file descriptor.
impl Fd for File
impl Fd for PipeRead
impl Fd for PipeWrite
impl Fd for Input
impl Fd for Output

#
Reader

pub(open) trait Reader :
Reader
+ Fd {
fn close(Self) -> Unit
}

Terminal input handle.

A Reader is an async byte reader that also exposes the terminal file descriptor used by stateful terminal operations.
impl Reader for File
impl Reader for PipeRead
impl Reader for Input

#
Writer

pub(open) trait Writer :
Writer
+ Fd {
fn close(Self) -> Unit
}

Terminal output handle.

A Writer is an async byte writer that also exposes the terminal file descriptor used by output-side terminal queries.
impl Writer for File
impl Writer for PipeWrite
impl Writer for Output

#
CursorPosition

pub struct CursorPosition {
row : Int
col : Int
} derive(Eq)

Cursor position reported by the terminal.

Rows and columns are 1-based terminal coordinates.

#
Event

pub(all) enum Event {
Input(
InputEvent
)
Resize(WindowSize)
} derive(Eq)

Terminal event reported by the coordinated root Tty handle.

#
KeyboardEnhancementFlags

type KeyboardEnhancementFlags derive(Eq)

Kitty keyboard protocol progressive enhancement flags.

#
KeyboardEnhancementFlags::bits

Return the raw kitty protocol bitset.

#
KeyboardEnhancementFlags::disambiguate

Enhancement flags for unambiguous key reports.

#
KeyboardEnhancementFlags::disambiguate_escape_codes

fn KeyboardEnhancementFlags::disambiguate_escape_codes(self : KeyboardEnhancementFlags) -> Bool

Return whether disambiguated escape-code reports are requested.

#
KeyboardEnhancementFlags::full

Enhancement flags for full kitty keyboard protocol reports.

#
KeyboardEnhancementFlags::new

fn KeyboardEnhancementFlags::new(disambiguate_escape_codes? : Bool, report_event_types? : Bool, report_alternate_keys? : Bool, report_all_keys_as_escape_codes? : Bool, report_associated_text? : Bool) -> KeyboardEnhancementFlags

Construct kitty keyboard protocol progressive enhancement flags.

#
KeyboardEnhancementFlags::report_all_keys_as_escape_codes

fn KeyboardEnhancementFlags::report_all_keys_as_escape_codes(self : KeyboardEnhancementFlags) -> Bool

Return whether all keys should be reported as escape codes.

#
KeyboardEnhancementFlags::report_alternate_keys

fn KeyboardEnhancementFlags::report_alternate_keys(self : KeyboardEnhancementFlags) -> Bool

Return whether alternate key-code reports are requested.

#
KeyboardEnhancementFlags::report_associated_text

fn KeyboardEnhancementFlags::report_associated_text(self : KeyboardEnhancementFlags) -> Bool

Return whether associated text reports are requested.

#
KeyboardEnhancementFlags::report_event_types

fn KeyboardEnhancementFlags::report_event_types(self : KeyboardEnhancementFlags) -> Bool

Return whether key repeat and release reports are requested.

#
MouseTrackingMode

pub(all) enum MouseTrackingMode {
Click
Drag
Motion
} derive(Eq)

Mouse tracking scope for SGR mouse reports.

#
State

type State

Opaque terminal state snapshot.

Capture a state with Tty::get_state, derive raw mode with State::make_raw, and restore it with Tty::set_state or Tty::leave_raw_mode.

#
State::make_raw

fn State::make_raw(self : State) -> State

Derive a raw-mode state from this captured terminal state.

#
Tty

type Tty

Coordinated terminal input/output handle.

Tty is for terminal protocols that need to write a request and read the response from the same terminal input stream. It does not model a screen.

#
Tty::bold

async fn Tty::bold(self : Tty) -> Unit

Set bold or increased intensity.

#
Tty::close

fn Tty::close(self : Tty) -> Unit

Close the underlying input and output handles.

Closing stdio-backed handles is a no-op.

#
Tty::cursor_back

async fn Tty::cursor_back(self : Tty, n : Int) -> Unit

Cursor Back (CUB), ECMA-48.

#
Tty::cursor_forward

async fn Tty::cursor_forward(self : Tty, n : Int) -> Unit

Cursor Forward (CUF), ECMA-48.

#
Tty::cursor_up

async fn Tty::cursor_up(self : Tty, n : Int) -> Unit

Cursor Up (CUU), ECMA-48.

#
Tty::disable_auto_wrap

async fn Tty::disable_auto_wrap(self : Tty) -> Unit

Disable DEC auto wrap mode (DECAWM).

#
Tty::disable_bracketed_paste

async fn Tty::disable_bracketed_paste(self : Tty) -> Unit

Disable bracketed paste mode.

#
Tty::disable_focus_tracking

async fn Tty::disable_focus_tracking(self : Tty) -> Unit

Disable focus tracking.

#
Tty::disable_mouse

async fn Tty::disable_mouse(self : Tty) -> Unit

Disable SGR mouse tracking.

#
Tty::enable_auto_wrap

async fn Tty::enable_auto_wrap(self : Tty) -> Unit

Enable DEC auto wrap mode (DECAWM).

#
Tty::enable_bracketed_paste

async fn Tty::enable_bracketed_paste(self : Tty) -> Unit

Enable bracketed paste mode.

#
Tty::enable_focus_tracking

async fn Tty::enable_focus_tracking(self : Tty) -> Unit

Enable focus tracking.

#
Tty::enable_mouse

async fn Tty::enable_mouse(self : Tty, mode : MouseTrackingMode) -> Unit

Enable SGR mouse tracking.

#
Tty::enter_alt_screen

async fn Tty::enter_alt_screen(self : Tty) -> Unit

Enter the alternate screen buffer.

#
Tty::enter_raw_mode

fn Tty::enter_raw_mode(self : Tty) -> State raise
OSError

Enter raw mode and return the previous terminal state.

#
Tty::erase_display

async fn Tty::erase_display(self : Tty) -> Unit

Erase the visible display (ED 2), ECMA-48.

#
Tty::erase_line_all

async fn Tty::erase_line_all(self : Tty) -> Unit

Erase the entire current line (EL 2), ECMA-48.

#
Tty::erase_scrollback

async fn Tty::erase_scrollback(self : Tty) -> Unit

Erase saved lines in the terminal scrollback (ED 3), xterm extension.

#
Tty::get_state

Capture the current terminal state for this handle.

#
Tty::hide_cursor

async fn Tty::hide_cursor(self : Tty) -> Unit

Hide the terminal cursor.

#
Tty::italic

async fn Tty::italic(self : Tty) -> Unit

Set italic text.

#
Tty::leave_alt_screen

async fn Tty::leave_alt_screen(self : Tty) -> Unit

Leave the alternate screen buffer.

#
Tty::leave_raw_mode

fn Tty::leave_raw_mode(self : Tty, state : State) -> Unit raise
OSError

Restore a terminal state captured before or during raw mode.

#
Tty::new

Create a coordinated terminal handle from input and output handles.

#
Tty::open

async fn Tty::open() -> Tty

Open the controlling terminal as one coordinated terminal handle.

#
Tty::pop_keyboard_enhancement_flags

async fn Tty::pop_keyboard_enhancement_flags(self : Tty) -> Unit

Pop one kitty keyboard protocol progressive enhancement flag stack entry.

#
Tty::push_keyboard_enhancement_flags

async fn Tty::push_keyboard_enhancement_flags(self : Tty, flags : KeyboardEnhancementFlags) -> Unit

Push kitty keyboard protocol progressive enhancement flags.

#
Tty::query_cursor_color

async fn Tty::query_cursor_color(self : Tty, timeout_ms? : Int) ->
Rgb16
?

Query the terminal text cursor color.

The returned RGB components are 16-bit values in the range 0..65535. User input received while waiting for the terminal response is preserved for later Tty::read_event calls.

#
Tty::query_cursor_position

async fn Tty::query_cursor_position(self : Tty, timeout_ms? : Int) -> CursorPosition?

Query the terminal cursor position.

The returned row and column are 1-based. User input received while waiting for the terminal response is preserved for later Tty::read_event calls.

#
Tty::query_default_background_color

async fn Tty::query_default_background_color(self : Tty, timeout_ms? : Int) ->
Rgb16
?

Query the terminal default background color.

The returned RGB components are 16-bit values in the range 0..65535. User input received while waiting for the terminal response is preserved for later Tty::read_event calls.

#
Tty::query_default_foreground_color

async fn Tty::query_default_foreground_color(self : Tty, timeout_ms? : Int) ->
Rgb16
?

Query the terminal default foreground color.

The returned RGB components are 16-bit values in the range 0..65535. User input received while waiting for the terminal response is preserved for later Tty::read_event calls.

#
Tty::query_kitty_keyboard_support

async fn Tty::query_kitty_keyboard_support(self : Tty, timeout_ms? : Int) -> Bool

Query whether the terminal supports the kitty keyboard protocol.

User input received while waiting for terminal responses is preserved for later Tty::read_event calls.

#
Tty::read_event

async fn Tty::read_event(self : Tty, esc_timeout_ms? : Int) -> Event

Read the next root terminal event.

esc_timeout_ms controls the short wait used to distinguish a standalone Escape key from the start of a longer escape sequence.

#
Tty::reset_background

async fn Tty::reset_background(self : Tty) -> Unit

Reset the background color to the terminal default.

#
Tty::reset_bold

async fn Tty::reset_bold(self : Tty) -> Unit

Reset bold by restoring normal intensity.

#
Tty::reset_foreground

async fn Tty::reset_foreground(self : Tty) -> Unit

Reset the foreground color to the terminal default.

#
Tty::reset_italic

async fn Tty::reset_italic(self : Tty) -> Unit

Reset italic text.

#
Tty::reset_reverse

async fn Tty::reset_reverse(self : Tty) -> Unit

Reset reverse video.

#
Tty::reset_style

async fn Tty::reset_style(self : Tty) -> Unit

Reset all SGR style attributes.

#
Tty::reset_top_bottom_margins

async fn Tty::reset_top_bottom_margins(self : Tty) -> Unit

Reset Top and Bottom Margins (DECSTBM).

#
Tty::reset_underline

async fn Tty::reset_underline(self : Tty) -> Unit

Reset underlined text.

#
Tty::reverse

async fn Tty::reverse(self : Tty) -> Unit

Set reverse video.

#
Tty::reverse_index

async fn Tty::reverse_index(self : Tty) -> Unit

Reverse Index (RI), ECMA-48.

#
Tty::set_background

async fn Tty::set_background(self : Tty, color :
Color
) -> Unit

Set the background color.

#
Tty::set_cursor_position

async fn Tty::set_cursor_position(self : Tty, row : Int, col : Int) -> Unit

Move the terminal cursor to a 1-based row and column.

#
Tty::set_foreground

async fn Tty::set_foreground(self : Tty, color :
Color
) -> Unit

Set the foreground color.

#
Tty::set_state

fn Tty::set_state(self : Tty, state : State) -> State raise
OSError

Apply a terminal state and return the previous state.

#
Tty::set_top_bottom_margins

async fn Tty::set_top_bottom_margins(self : Tty, top : Int, bottom : Int) -> Unit

Set Top and Bottom Margins (DECSTBM).

#
Tty::show_cursor

async fn Tty::show_cursor(self : Tty) -> Unit

Show the terminal cursor.

#
Tty::stdio

fn Tty::stdio() -> Tty

Wrap process stdio as one coordinated terminal handle.

#
Tty::underline

async fn Tty::underline(self : Tty) -> Unit

Set underlined text.

#
Tty::window_size

Query the visible terminal window size.

#
Tty::with_alt_screen

async fn[T] Tty::with_alt_screen(self : Tty, f : async () -> T) -> T

Run f with the output in the alternate screen buffer.

#
Tty::with_bracketed_paste

async fn[T] Tty::with_bracketed_paste(self : Tty, f : async () -> T) -> T

Run f with bracketed paste mode enabled.

#
Tty::with_focus_tracking

async fn[T] Tty::with_focus_tracking(self : Tty, f : async () -> T) -> T

Run f with focus tracking enabled.

#
Tty::with_keyboard_enhancements

async fn[T] Tty::with_keyboard_enhancements(self : Tty, flags : KeyboardEnhancementFlags, f : async () -> T) -> T

Run f with kitty keyboard protocol enhancements enabled.

#
Tty::with_kitty_keyboard

async fn[T] Tty::with_kitty_keyboard(self : Tty, f : async () -> T) -> T

Run f with full kitty keyboard protocol reports enabled.

#
Tty::with_mouse

async fn[T] Tty::with_mouse(self : Tty, mode : MouseTrackingMode, f : async () -> T) -> T

Run f with SGR mouse tracking enabled.

#
Tty::with_raw_mode

async fn[T] Tty::with_raw_mode(self : Tty, f : async () -> T) -> T

Run an async callback with raw mode enabled.

The captured terminal state is restored on success or error.

#
Tty::with_top_bottom_margins

async fn[T] Tty::with_top_bottom_margins(tty : Tty, top : Int, bottom : Int, body : async () -> T) -> T

#
Tty::write

async fn Tty::write(self : Tty, data : &
Data
) -> Unit

Write bytes to the terminal output handle.

#
Tty::write_string

async fn Tty::write_string(self : Tty, string : String) -> Unit

Write a string to the terminal output handle.

#
WindowSize

pub struct WindowSize {
rows : Int
cols : Int
} derive(Eq)

Terminal window size in rows and columns.

#
isatty

fn[T : Fd] isatty(fd : T) -> Bool raise
OSError

Return whether a handle refers to a terminal.

Valid non-terminal handles return false; OS-level failures raise OSError.

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io