# Overview

> Install tui with go get (Go 1.25+), write a first Model with Init, Update and View, and see every package, its stability level and the supported platforms.

Web: https://tui.nizaami.com/overview/  
Source: https://github.com/ows4444/tui/blob/code/README.md

A terminal UI framework for Go built on the Elm Architecture (Model / Update /
View). It uses only the standard library: `go.mod` has no `require` directive,
and a test in `internal/archtest` fails if any package imports outside the
standard library and this module.

An application implements `tui.Model`, hands it to `tui.NewProgram` with the
options it wants, and calls `Run`. The Program reads keys, mouse events and
resizes, calls `Update` on each Msg, runs the Cmds `Update` returns, and redraws
`View` with a cell diff so only changed cells are written.

Guides, a cookbook and screens of every example are at
**[tui.nizaami.com](https://tui.nizaami.com)**; the API reference is on
[pkg.go.dev](https://pkg.go.dev/github.com/ows4444/tui).

## Install

Requires Go 1.25 or later.

```console
$ go get github.com/ows4444/tui
```

There is no tagged release yet, so this resolves to a pseudo-version of the
latest commit.

## Usage

A Model is any value with `Init`, `Update` and `View`. This is
`ExampleNewProgram` from [example_test.go](https://github.com/ows4444/tui/blob/code/example_test.go); it reads keys from a
string and discards the output, so it runs without a terminal:

```go
type counter struct{ n int }

func (c counter) Init() tui.Cmd { return nil }

func (c counter) Update(msg tui.Msg) (tui.Model, tui.Cmd) {
	if k, ok := msg.(tui.Key); ok && k.Type == tui.KeyRunes {
		switch k.Text {
		case "+":
			c.n++
		case "q":
			return c, tui.Quit()
		}
	}
	return c, nil
}

func (c counter) View() string { return fmt.Sprintf("count: %d", c.n) }

func ExampleNewProgram() {
	p := tui.NewProgram(counter{},
		tui.WithInput(strings.NewReader("++q")),
		tui.WithOutput(io.Discard),
	)
	final, err := p.Run()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(final.View())
	// Output: count: 2
}
```

Against a real terminal, leave out `WithInput` and `WithOutput`: they default
to `os.Stdin` and `os.Stdout`. [examples/counter](https://github.com/ows4444/tui/blob/code/examples/counter/main.go) does
exactly that:

```console
$ go run ./examples/counter
```

Every directory under [examples/](https://github.com/ows4444/tui/tree/code/examples) is a runnable program, for instance
`./examples/dashboard`, `./examples/form`, `./examples/signup`,
`./examples/agentshell` and `./examples/probe`.

## Packages

API reference is the godoc of each package. The layering and stability levels
below come from the package comment in [doc.go](https://github.com/ows4444/tui/blob/code/doc.go); a package may import
only the layers above it, and `go test ./internal/archtest` fails on an import
that points up.

Stability levels:

- **Core**: the root package `tui`, the primitives, codecs and `tuitest`.
- **Stable-ish**: `cellbuf`, the render helpers and most components.
- **Experimental**: packages whose own package comment says
  `Stability: experimental.` `go run ./internal/tools/doccheck` fails if that
  list and the list in `doc.go` disagree.

### Runtime

| Package | Purpose | Stability |
|---|---|---|
| `tui` | The Program and its event loop; owns terminal I/O through `term` and `internal/termio`. Re-exports 14 `input` types as aliases (`Key`, `MouseEvent`, `PasteEvent`, ...), each as stable as the type it names | Core |
| `cellbuf` | A retained grid of terminal cells, for widgets that draw straight into cells | Stable-ish |

### Primitives

| Package | Purpose | Stability |
|---|---|---|
| `ansi` | Styles, colour, escape encoding, width, graphemes, wrapping | Core |
| `layout` | A Node tree with flex, grid and overlay layout | Core |
| `theme` | Colours, glyph sets and states as data | Core |
| `term` | Raw mode and terminal size | Core |
| `motion` | Springs, transitions and the reduced-motion preference | Core |

### Codecs and ports

| Package | Purpose | Stability |
|---|---|---|
| `input` | Decodes terminal bytes into keys, mouse, paste and focus events | Core |
| `keymap` | A registry of key bindings that help widgets read | Core |
| `hittest` | Named click regions: which region a mouse event landed on | Core |

### Renderers and test harness

| Package | Purpose | Stability |
|---|---|---|
| `widgets` | Stateless render helpers: Badge, Box, CodeBlock, ChatMessage, DiffView, ProgressBar, ... | Stable-ish |
| `widgets/chart` | Sparkline, BarChart, LineChart, HeatMap, Gauge | Stable-ish |
| `markdown` | A CommonMark subset rendered to styled, width-aware text | Stable-ish |
| `focus` | A focus Ring over components, moved with Tab and Shift+Tab | Stable-ish |
| `tuitest` | A headless harness that runs a model against a virtual screen | Core |

### Components

One package per stateful widget, each a value-typed `Model` configured through
exported fields. Every widget has a `LayoutNode()` adapter, and
every widget implements `Linearize` for accessible mode; tests in
`internal/archtest` and `internal/tools/doccheck` fail when one is missing.

| Package | Purpose | Stability |
|---|---|---|
| `textinput` | Single-line text input | Stable-ish |
| `textarea` | Multi-line text input | Stable-ish |
| `passwordinput` | `textinput` that masks every character | Stable-ish |
| `maskedinput` | `textinput` that masks with a configurable rune | Stable-ish |
| `emailinput` | `textinput` that rejects whitespace | Stable-ish |
| `numberinput` | `textinput` restricted to digits and a leading `-` | Stable-ish |
| `taginput` | A list of short tags entered through a text input | Stable-ish |
| `autocomplete` | Text input with a filtered suggestion dropdown | Stable-ish |
| `form` | A column of labelled, validated single-line fields | Stable-ish |
| `confirm` | Yes/no prompt | Stable-ish |
| `picker` | Single-choice list | Stable-ish |
| `multiselect` | Multi-choice list | Stable-ish |
| `menu` | Nested-navigation list built on `picker` | Stable-ish |
| `menubar` | Horizontal bar of titled dropdown menus | Stable-ish |
| `contextmenu` | Popup menu opened at an anchor point | Stable-ish |
| `datatable` | `widgets.Table` plus row navigation | Stable-ish |
| `treeview` | Hierarchical expandable tree | Stable-ish |
| `filepicker` | Filesystem browse-and-select | Stable-ish |
| `datepicker` | Keyboard-navigable calendar on `time.Time` | Stable-ish |
| `colorpicker` | Palette swatches plus hex input | Stable-ish |
| `virtuallist` | Scrollable window onto a large uniform-height list | Stable-ish |
| `viewport` | Scrollable window onto content taller than it | Stable-ish |
| `logview` | Append-only scrolling log | Stable-ish |
| `scrollbar` | Track and thumb showing the visible part of some content | Stable-ish |
| `splitpane` | Two panes with a draggable divider | Stable-ish |
| `tabs` | Horizontal tab bar | Stable-ish |
| `accordion` | List of collapsible sections | Stable-ish |
| `wizard` | Step navigation for multi-step flows | Stable-ish |
| `dialog` | Modal box composited over the screen | Stable-ish |
| `drawer` | Overlay anchored to an edge of the screen | Stable-ish |
| `popover` | Overlay anchored near a point | Stable-ish |
| `toast` | Transient auto-dismissing notification | Stable-ish |
| `helpscreen` | Full-screen key-binding help overlay | Stable-ish |
| `spinner` | Animated loading indicator | Stable-ish |
| `loadingbar` | Indeterminate progress animation | Stable-ish |
| `skeleton` | Loading placeholder block | Stable-ish |
| `clockview` | Wall clock, stopwatch or countdown timer | Stable-ish |
| `appshell` | Header, input, scrollable content and key-hints footer composed from existing widgets | Experimental |
| `streamtext` | Text revealed a few characters at a time | Experimental |
| `toolapproval` | Gate-before-execution prompt for an agent tool call | Experimental |
| `notificationcenter` | Panel showing every queued notification at once | Experimental |
| `commandpalette` | Text input with a fuzzy-filtered list of Commands | Experimental |
| `errorretry` | An error with retry and dismiss keys | Experimental |
| `faces` | 50 animated Braille characters and a widget that plays them | Experimental |
| `imageview` | PNG through the kitty graphics protocol or Sixel, with a text placeholder otherwise | Experimental |
| `clipboard` | A "copy to clipboard" button that writes OSC 52 | Experimental |

Packages under `internal/` are not public API.

## Platforms and limitations

- **Platforms.** Linux, macOS (darwin), Windows, FreeBSD, OpenBSD, NetBSD and
  DragonFly BSD. Any other GOOS, including solaris and illumos, is unsupported:
  the build fails on purpose with an import named
  `tui_unsupported_platform_this_GOOS_is_not_supported_see_README`
  ([platform_unsupported.go](https://github.com/ows4444/tui/blob/code/platform_unsupported.go)). CI runs the tests on
  Linux, macOS, Windows and FreeBSD; OpenBSD, NetBSD and DragonFly BSD are only
  cross-compiled (`TestBuildsOnSupportedPlatforms`). `WithSuspendOnCtrlZ` does
  nothing on Windows, where Ctrl+Z stays an ordinary key.
- **Colour.** Without `WithColorProfile`, `NewProgram` detects the depth from
  the output and the environment (`ansi.DetectColorProfileFor`), in this order:
  `NO_COLOR` (non-empty) turns colour off; `CLICOLOR_FORCE` (non-empty, not
  `0`) colours output that is not a terminal; otherwise non-terminal output or
  `CLICOLOR=0` turns colour off; `TERM=dumb` turns colour off;
  `COLORTERM=truecolor` or `24bit`, `WT_SESSION`, and some `TERM_PROGRAM`
  values give 24-bit colour (`Apple_Terminal` gives 256); then the `TERM` name
  decides (an unrecognised name gets 16 colours; an empty `TERM` gets none,
  except 16 on Windows). A truecolor terminal that sets none of these (some SSH sessions,
  tmux with `TERM=screen`) is detected at a lower depth; pass
  `WithColorProfile(ansi.TrueColor)` to override. Passing `WithColorProfile`
  also overrides `NO_COLOR`.
- **Unicode.** Width, truncation and trimming treat an extended grapheme cluster
  (a ZWJ emoji sequence, a flag, a letter with combining marks) as one unit,
  following UAX #29. Set `TUI_NO_CLUSTERS=1` for a terminal that draws the
  parts separately. The width, grapheme and bidi tables are from Unicode
  17.0.0. Right-to-left text is reordered for display only with
  `WithBidi(true)`; it is off by default.
- **Accessibility.** `WithAccessible(true)` switches to append-only, unstyled
  output and renders the root model's `Linearize` instead of `View` when it has
  one. `WithAccessibleAuto` turns it on for `ACCESSIBLE=1` or `TERM=dumb`, and
  `TERM=dumb` alone also turns it on unless `WithAltScreen` or `WithAccessible`
  was given. VoiceOver, NVDA and Orca are unverified: no screen-reader run is
  recorded for this repository.

## Terminal probe results

[examples/probe](https://github.com/ows4444/tui/blob/code/examples/probe/main.go) reports what a terminal supports
(colour depth, focus reporting, OSC 11 background detection) and leaves a
`probe:` line in the scrollback. No probe run is recorded for any terminal yet,
so every row is unverified. The middle column is what `ansi/profile.go` detects
from the environment alone, not a measured result.

| Terminal | Detected colour depth | Probe result |
|---|---|---|
| Windows Terminal (`WT_SESSION`) | 24-bit | unverified |
| iTerm2 (`TERM_PROGRAM=iTerm.app`) | 24-bit | unverified |
| WezTerm (`TERM_PROGRAM=WezTerm`) | 24-bit | unverified |
| VS Code (`TERM_PROGRAM=vscode`) | 24-bit | unverified |
| Ghostty (`TERM_PROGRAM=ghostty`) | 24-bit | unverified |
| Hyper (`TERM_PROGRAM=Hyper`) | 24-bit | unverified |
| kitty (`TERM=xterm-kitty`) | 24-bit | unverified |
| Alacritty (`TERM=alacritty`) | 24-bit | unverified |
| Apple Terminal (`TERM_PROGRAM=Apple_Terminal`) | 256 colours | unverified |

## Documentation

- [docs/](https://tui.nizaami.com/overview/): topic guides, architecture and testing.
- [CONTRIBUTING.md](https://tui.nizaami.com/contributing/): conventions and the checks CI runs.
- [CHANGELOG.md](https://tui.nizaami.com/changelog/): user-visible changes.

## License

MIT; see [LICENSE](https://github.com/ows4444/tui/blob/code/LICENSE).
