Sign in

    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
    Download zip
    Version
    0.4.0
    License
    Apache-2.0
    Last updated
    8 days ago
    Downloads
    26K

    Dependencies

    #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.set_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 raise
    }

    A handle that exposes an OS file descriptor.
    impl Fd for File
    impl Fd for PipeRead
    impl Fd for PipeWrite
    impl Fd for RawFdStream
    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

    Command

    pub(all) enum Command {
    Print(StringView)
    EnterAltScreen
    LeaveAltScreen
    BeginSynchronizedUpdate
    EndSynchronizedUpdate
    EnableBracketedPaste
    DisableBracketedPaste
    EnableFocusTracking
    DisableFocusTracking
    EnableAutoWrap
    DisableAutoWrap
    PushKeyboardEnhancementFlags(KeyboardEnhancementFlags)
    PopKeyboardEnhancementFlags
    HideCursor
    ShowCursor
    CursorUp(Int)
    CursorDown(Int)
    CursorForward(Int)
    CursorBack(Int)
    CursorNextLine(Int)
    CursorPrevLine(Int)
    CursorHorizontalAbsolute(Int)
    CursorPosition(Int, Int)
    EraseLineAll
    EraseLineLeft
    EraseLineRight
    EraseDisplay
    EraseScrollback
    SetTopBottomMargins(Int, Int)
    ResetTopBottomMargins
    ReverseIndex
    SetForeground(
    Color
    )
    SetBackground(
    Color
    )
    ResetForeground
    ResetBackground
    SetBold
    ResetBold
    SetItalic
    ResetItalic
    SetUnderline
    ResetUnderline
    SetReverse
    ResetReverse
    ResetStyle
    }

    Terminal output command.

    A Command is inert data. It only writes bytes when passed to Tty::execute.

    CursorPosition

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

    Cursor position reported by the terminal.

    Rows and columns are 1-based terminal coordinates.

    CursorPosition::equal

    CursorPosition::not_equal

    fn CursorPosition::not_equal(x : CursorPosition, y : CursorPosition) -> Bool

    Event

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

    Terminal event reported by the coordinated root Tty handle.

    Event::equal

    fn Event::equal(Event, Event) -> Bool

    Event::not_equal

    fn Event::not_equal(x : Event, y : Event) -> Bool

    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::equal

    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::not_equal

    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.

    MouseTrackingMode::equal

    MouseTrackingMode::not_equal

    fn MouseTrackingMode::not_equal(x : MouseTrackingMode, y : MouseTrackingMode) -> Bool

    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

    Tty::begin_synchronized_update

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

    Begin a synchronized update (DEC private mode 2026). Output written until the matching end_synchronized_update is buffered by the terminal and presented as one atomic frame, so a clear-then-repaint is never shown half-drawn. Prefer with_synchronized_update, which always emits the matching end.

    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::end_synchronized_update

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

    End a synchronized update (DEC private mode 2026): flush and atomically present everything written since begin_synchronized_update.

    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::execute

    async fn Tty::execute(self : Tty, commands : Array[Command]) -> Unit

    Execute terminal output commands with one serialized write.

    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::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

    fn[I : Reader +
    Reader
    + Fd, O : Writer +
    Writer
    + Fd] Tty::new(input : I, output : O) -> Tty raise

    Create a coordinated terminal handle from input and output handles.

    Tty::open

    async fn Tty::open() -> Tty

    Open the controlling terminal (/dev/tty) as one coordinated terminal handle. The single bidirectional handle is wrapped as a raw stream and used for both input and output.

    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_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_bold

    #alias(bold, deprecated="`bold` is deprecated, use `set_bold` instead")
    async fn Tty::set_bold(self : Tty) -> Unit

    Set bold or increased intensity.

    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_italic

    #alias(italic, deprecated="`italic` is deprecated, use `set_italic` instead")
    async fn Tty::set_italic(self : Tty) -> Unit

    Set italic text.

    Tty::set_reverse

    #alias(reverse, deprecated="`reverse` is deprecated, use `set_reverse` instead")
    async fn Tty::set_reverse(self : Tty) -> Unit

    Set reverse video.

    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::set_underline

    #alias(underline, deprecated="`underline` is deprecated, use `set_underline` instead")
    async fn Tty::set_underline(self : Tty) -> Unit

    Set underlined text.

    Tty::show_cursor

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

    Show the terminal cursor.

    Tty::stdio

    fn Tty::stdio() -> Tty raise

    Wrap process stdio as one coordinated terminal handle.

    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, error, or cancellation.

    Tty::with_synchronized_update

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

    Run f inside a synchronized update so the frame it draws is presented atomically (no flicker from a visible clear-then-repaint). The end is always emitted, even if f raises, so the terminal is never left with synchronized output stuck on. Terminals without mode 2026 ignore the markers and f draws as before.

    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.

    The write is serialized through an internal lock, so a single call lands on the terminal atomically even when multiple tasks write concurrently. To make a multi-call sequence atomic, assemble it into one buffer and issue a single write.

    Tty::write_string

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

    Write a string to the terminal output handle.

    The write is serialized through an internal lock, so a single call lands on the terminal atomically even when multiple tasks write concurrently. To make a multi-call sequence atomic, assemble it into one string and issue a single write_string.

    WindowSize

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

    Terminal window size in rows and columns.

    WindowSize::equal

    fn WindowSize::equal(WindowSize, WindowSize) -> Bool

    WindowSize::not_equal

    fn WindowSize::not_equal(x : WindowSize, y : WindowSize) -> Bool

    isatty

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

    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