Sign in

    moonetui

    A terminal UI toolkit for MoonBit: cell buffer, differential ANSI renderer, incremental key parser, layout, a CSS-like design system, thirty-two widgets, and drivers for terminals, Node, browsers and serial lines.

    tui
    terminal
    widget
    ansi
    Download zip
    Version
    0.3.0
    License
    Apache-2.0
    Last updated
    1 hour ago
    Downloads
    30

    No dependencies

    #MoonBit Easy Terminal UI Frame

    moonetui · a terminal UI toolkit for MoonBit

    Cell buffer · differential ANSI renderer · incremental key parser · layout · CSS-like design system · thirty-two widgets · drivers for terminals, Node, browsers and serial lines

    Check and Test Docs mooncakes licence

    #Highlights

    Synchronous coreNo async and no hidden event loop. The host calls one step(): poll, parse, dispatch, lay out, compose, diff, write.
    Differential rendererTwo frames become the spans that changed, and only the escape sequences needed to draw them.
    A CSS-like design systemSelectors over name, id, class and :focus, :hover, :disabled, stacked by specificity and cascade, with inheritance, borders, padding, margins, docks, grid spans and transitions.
    Unicode-awareEvery line is measured in terminal columns over a generated width table: wide CJK, double-width emoji, combining marks.
    Four backendsThe same code and the same test suite on wasm, wasm-gc, js and native.
    Any wireA modern terminal with true colour, a serial-era terminal with eight colours, a browser canvas, or a bare UART with no cursor addressing at all.

    #Quick start

    moon add moonbitstack/moonetui

    moon run --target native src/examples/gallery # every widget and every style, live moon run --target native src/examples/monitor # a complete app: tabs, timers, a command line moon run --target native src/examples/tui # a styled app in a real terminal moon run --target js src/examples/hello # under Node moon run --target native src/examples/uart # whole frames of plain text, as a serial line gets them

    The gallery runs in a browser too: moon build --target js, serve the repository root, and open src/examples/browser/index.html.

    A first application: a list, a field, and a stylesheet that frames the list and thickens the frame while it has focus.

    fn main raise {
    let sheet = @design.Stylesheet::new()
    sheet.add(
    @design.Sel::new(name="Header"),
    @design.Styles::new(text=@style.Style::new(bold=true, fg=@style.cyan)),
    )
    sheet.add(@design.Sel::new(classes=["framed"]), @design.Styles::new(border=Round))
    sheet.add(
    @design.Sel::new(classes=["framed"], focus=true),
    @design.Styles::new(border=Heavy),
    )
    let list = @widget.ListView::new(["alpha", "beta", "gamma"])
    let field = @widget.Input::new(placeholder="pick one")
    let line = @layout.Box::new(extent=Cells(1))
    let tree = @app.column(0, [
    @app.leaf(1, @widget.Header::new("moonetui"), box=line, name="Header"),
    @app.leaf(2, list, classes=["framed"]),
    @app.leaf(3, field, box=line),
    @app.leaf(4, @widget.Footer::new([("tab", "next"), ("enter", "choose")]), box=line),
    ])
    let ui = @app.App::new(@native.NativeTty::new(), tree, sheet~)
    ui.start()
    @native.run(() => {
    let alive = ui.step()
    for report in ui.drain() {
    if report is Selected(index~, ..) {
    field.set(list.items()[index])
    ui.refresh()
    }
    }
    alive
    })
    ui.stop()
    }

    Its moon.pkg imports app, design, driver/native, layout, style and widget from moonbitstack/moonetui, and declares supported_targets = "native".

    #The widget inventory

    GroupWidgets
    Text and displayStatic and label(), Header, Footer, Rule, Digits, Sparkline, Progress, LoadingIndicator, Placeholder
    Entry and logsInput, TextArea, Log, RichLog
    ChoicesSwitch, Checkbox, RadioButton, RadioSet, OptionList, SelectionList, Select
    Navigation and structureButton, ListView, Tabs, TabbedContent, Collapsible, Tree, DirectoryTree
    Data and noticesDataTable, Markdown, Tooltip, Toast

    Each one has its keyboard contract, message shape and tests, and a live demo on the documentation site beside its source.

    #Package map

    PackageWhat it does
    @geomInteger rectangle algebra: Offset, Size, Region, Spacing
    @styleColours (named, indexed, true colour), attributes, palette quantisation, differential SGR
    @stripOne rendered line: styled segments measured in terminal columns
    @eventKeys, mouse reports, paste, resize and focus as plain values
    @inputThe terminal byte stream turned into events
    @compThe compositor: placements to rows, rows to changed spans, spans to escape sequences
    @driverThe seam to a terminal, and a headless terminal for tests
    @vtA virtual terminal: the screen an escape stream draws, for the browser driver, tests and screenshots
    @layoutSizes along an axis, and the grid with spans and gutters
    @designRules over name, id, class and state; borders, padding, margins, alignment, docks, offsets, transitions
    @widgetThe widget contract and the inventory
    @appThe widget tree, focus ring, scroll containers, docks, grids, screens, timers and the frame loop
    @driver/native · @driver/node · @driver/webA real terminal through the C library; Node; a canvas in the browser

    #Standards

    • Unicode: rendering is measured in terminal columns over a generated width table.
    • ECMA-48 / ISO 6429: SGR (CSI … m) for attributes, CUP (CSI H) for addressing, ED and EL for erasing.
    • UTF-8: the input decoder is incremental, so a code point split across two reads arrives whole.

    #Tests

    moon check --target all --deny-warn moon build --target all moon test --target all moon fmt && moon info --target all # the diff must stay clean

    420 tests per backend, the same suite on wasm, wasm-gc, js and native, run on Ubuntu, macOS and Windows in CI. They cover 91% of the lines.

    #Licence

    Apache-2.0. See LICENSE.