(WIP) Odin TUI Cell Renderer
  • Odin 96.7%
  • Nushell 3.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nasonov V 2f6fa54aa8
Merge pull request #1 from qxuken/terminal-lifecycle-and-event-loop
Own the terminal lifecycle, replace the fixed-rate loop with blocking input
2026-09-06 21:32:55 +03:00
examples docs: rewrite the examples around the blocking loop 2026-09-06 21:31:36 +03:00
tui feat(events): blocking poll with a resumable VT parser 2026-09-06 21:31:24 +03:00
.editorconfig choe: add build script 2025-01-20 17:39:37 +03:00
.gitignore chore: add readme 2025-06-04 22:09:46 +03:00
build.nu docs: rewrite the examples around the blocking loop 2026-09-06 21:31:36 +03:00
collections.json choe: add build script 2025-01-20 17:39:37 +03:00
docs.md feat(events): add example for debug 2025-02-02 01:08:11 +03:00
odinfmt.json choe: add build script 2025-01-20 17:39:37 +03:00
ols.json choe: add build script 2025-01-20 17:39:37 +03:00
README.md docs: rewrite the examples around the blocking loop 2026-09-06 21:31:36 +03:00
tasks.md docs: rewrite the examples around the blocking loop 2026-09-06 21:31:36 +03:00

Odin TUI

Work in progress, but usable.

A small terminal user interface toolkit written in Odin. It fills the gap core leaves for full-screen terminal programs:

  • tui:term_sys – raw mode, alternate screen, mouse reporting, bracketed paste and focus events, with the terminal restored on exit, on panics, on fatal signals and around Ctrl-Z / fg.
  • tui:events – a blocking poll(timeout) that turns terminal input into typed events: keys with modifiers, SGR mouse, resize, paste, focus.
  • tui:renderer – a cell grid with text wrapping (grapheme aware, wide characters), boxes, borders, frames, scissors, colours and styles, serialised to one ANSI string per frame.

Supported: macOS, Linux, the BSDs (via core:sys/posix) and Windows 10+ (console API + virtual terminal sequences).

The loop

There is no frame rate. The process sleeps inside events.poll until the user does something or the window changes, then redraws once:

term_sys.init()
defer term_sys.shutdown()
context.assertion_failure_proc = term_sys.assertion_failure_proc
events.init()
defer events.destroy()

for running {
    size, _ := term_sys.get_size()
    renderer.clean_renderer_cycle(&ren, {size.cols, size.rows})
    // ... draw calls ...
    term_sys.push_begin_sync_update(&frame)
    term_sys.push_cursor_home(&frame)
    renderer.render_to_builder(&ren, &frame)
    term_sys.push_end_sync_update(&frame)
    term_sys.flush_builder(&frame)

    for evt in events.poll(events.FOREVER) {   // or a timeout for periodic redraws
        if events.is_char(evt, 'q') do running = false
    }
}

Examples

file shows
examples/minimal.odin the smallest complete program
examples/events.odin every decoded event, suspend/resume with Ctrl-Z
examples/full.odin renderer features and a once-a-second clock via a timeout
nu build.nu run-example minimal.odin --run

or without Nushell:

odin run examples/minimal.odin -file -collection:tui=./tui

Tests

nu build.nu test

runs the input parser and text wrapping tests (odin test on each package).

Requirements

  • A recent Odin nightly (the core:os rewrite from 2026 is required)
  • (optional) Nushell for build.nu

Not done yet

  • Kitty keyboard protocol (disambiguated modifiers for plain letters)
  • Diffing frames instead of re-sending the whole grid
  • Cursor positioning API for text input widgets