tui

package module
v0.0.0-...-64e189b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2026 License: MIT Imports: 31 Imported by: 0

README

tui

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; the API reference is on pkg.go.dev.

Install

Requires Go 1.25 or later.

$ 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; it reads keys from a string and discards the output, so it runs without a terminal:

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 does exactly that:

$ go run ./examples/counter

Every directory under 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; 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). 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 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

License

MIT; see LICENSE.

Documentation

Overview

Package tui is a zero-dependency terminal UI framework built on the Elm Architecture (Model / Update / View), in the same spirit as Bubble Tea or Ink, using only the Go standard library.

An application implements Model, hands it to NewProgram with the options it wants, and calls Run:

p := tui.NewProgram(myModel{}, tui.WithAltScreen(true))
if _, err := p.Run(); err != nil { ... }

The Program reads keys, mouse events and resizes, calls Model.Update on each Msg, runs the Cmds Update returns, and redraws Model.View with a cell diff so only changed cells are written.

Cmds returned together by Batch run concurrently and their messages reach Update in no particular order; use Sequence when order matters, which runs its Cmds one at a time and delivers their messages in order. WithMaxConcurrentCmds caps how many Cmds run at once.

Package tui owns the event loop (stability: core). Terminal I/O is performed by package term and internal/termio, which tui drives. Package cellbuf, a retained grid of terminal cells for widgets that draw straight into cells, sits beside it (stability: stable-ish). The other packages are building blocks that tui and applications share, in layers that each import only the ones above them in this list (an import that points up is a test failure, see docs/architecture/overview.md):

Primitives, with no dependency on the rest (stability: core):

ansi    styles, colour, escape encoding, width, graphemes, wrapping
layout  a Node tree with flex, grid and overlay layout
theme   colours, glyph sets and states as data
term    raw mode and terminal size
motion  springs, transitions and the reduced-motion preference

Codecs and ports (core):

input   the byte-to-event decoder (keys, mouse, paste, focus)
keymap  key bindings for help text
hittest named click regions

Input aliases (stability: core, by alias). The root package re-exports 14 types from input as aliases: Key, KeyType, KeyAction, MouseEvent, MouseButton, MouseAction, PasteEvent, FocusEvent, ChordDef, ChordMsg, BackgroundColorEvent, PaletteColorEvent, ReplyEvent and BackgroundUnknownMsg. They are part of the root API, so each is exactly as stable as the input type it names: it changes only when input changes, never on its own.

Renderers and test harness (core for tuitest, stable-ish for the rest):

widgets       stateless render helpers: Badge, Box, CodeBlock, ChatMessage, ...
widgets/chart Sparkline, BarChart, LineChart, HeatMap, Gauge
markdown      a CommonMark subset rendered to styled text
focus         a focus ring over components
tuitest       a headless harness that runs a model against a virtual screen

Components, one package per stateful widget (textinput, viewport, datatable, ...), each a value-typed Model configured through exported fields. Most are stable-ish; the experimental ones are listed below and say so in their own package comment. The README lists every package by level.

Experimental: appshell, streamtext, toolapproval, notificationcenter, commandpalette, errorretry, faces, imageview, clipboard.

Terminals other than the local one (an SSH session, a test) plug in through the Terminal and Clock ports (WithTerminal, WithClock), plus ResizeNotifier for resizes; a wrapper model exposes the optional interfaces of the model it wraps with Unwrapper.

docs/architecture/overview.md describes how the packages layer.

Guides, a cookbook and screens of every example are at tui.nizaami.com.

Index

Examples

Constants

View Source
const (
	// DefaultInspectorKey toggles the info panel.
	DefaultInspectorKey = "f12"
	// DefaultInspectorLayoutKey toggles the layout outlines.
	DefaultInspectorLayoutKey = "f11"
	// DefaultInspectorMessagesKey toggles the message pane.
	DefaultInspectorMessagesKey = "f10"
	// DefaultInspectorModelKey toggles the model pane.
	DefaultInspectorModelKey = "f9"
)

The default keys of the inspector panes, used for an InspectorKeys field left as "".

View Source
const (
	KeyPress   = input.KeyPress
	KeyRepeat  = input.KeyRepeat
	KeyRelease = input.KeyRelease
)

The key actions, aliased from input. They appear in KeyRepeatMsg.Key and KeyReleaseMsg.Key when WithKeyboard included KeyboardReportEvents.

View Source
const (
	KeyRunes              = input.KeyRunes
	KeyUp                 = input.KeyUp
	KeyDown               = input.KeyDown
	KeyLeft               = input.KeyLeft
	KeyRight              = input.KeyRight
	KeyEnter              = input.KeyEnter
	KeyEsc                = input.KeyEsc
	KeyTab                = input.KeyTab
	KeyBackspace          = input.KeyBackspace
	KeyDelete             = input.KeyDelete
	KeySpace              = input.KeySpace
	KeyHome               = input.KeyHome
	KeyEnd                = input.KeyEnd
	KeyPgUp               = input.KeyPgUp
	KeyPgDown             = input.KeyPgDown
	KeyF1                 = input.KeyF1
	KeyF2                 = input.KeyF2
	KeyF3                 = input.KeyF3
	KeyF4                 = input.KeyF4
	KeyF5                 = input.KeyF5
	KeyF6                 = input.KeyF6
	KeyF7                 = input.KeyF7
	KeyF8                 = input.KeyF8
	KeyF9                 = input.KeyF9
	KeyF10                = input.KeyF10
	KeyF11                = input.KeyF11
	KeyF12                = input.KeyF12
	KeyF13                = input.KeyF13
	KeyF14                = input.KeyF14
	KeyF15                = input.KeyF15
	KeyF16                = input.KeyF16
	KeyF17                = input.KeyF17
	KeyF18                = input.KeyF18
	KeyF19                = input.KeyF19
	KeyF20                = input.KeyF20
	KeyF21                = input.KeyF21
	KeyF22                = input.KeyF22
	KeyF23                = input.KeyF23
	KeyF24                = input.KeyF24
	KeyCtrlC              = input.KeyCtrlC
	KeyCtrl               = input.KeyCtrl
	KeyUnknown            = input.KeyUnknown
	KeyInsert             = input.KeyInsert
	KeyMediaPlay          = input.KeyMediaPlay
	KeyMediaPause         = input.KeyMediaPause
	KeyMediaPlayPause     = input.KeyMediaPlayPause
	KeyMediaReverse       = input.KeyMediaReverse
	KeyMediaStop          = input.KeyMediaStop
	KeyMediaFastForward   = input.KeyMediaFastForward
	KeyMediaRewind        = input.KeyMediaRewind
	KeyMediaTrackNext     = input.KeyMediaTrackNext
	KeyMediaTrackPrevious = input.KeyMediaTrackPrevious
	KeyMediaRecord        = input.KeyMediaRecord
	KeyMediaVolumeDown    = input.KeyMediaVolumeDown
	KeyMediaVolumeUp      = input.KeyMediaVolumeUp
	KeyMediaMute          = input.KeyMediaMute
)

The key types, aliased from input (see input.KeyType) so the common case needs only the root import.

View Source
const (
	MouseButtonNone      = input.MouseButtonNone
	MouseButtonLeft      = input.MouseButtonLeft
	MouseButtonMiddle    = input.MouseButtonMiddle
	MouseButtonRight     = input.MouseButtonRight
	MouseButtonWheelUp   = input.MouseButtonWheelUp
	MouseButtonWheelDown = input.MouseButtonWheelDown

	MouseButtonWheelLeft  = input.MouseButtonWheelLeft
	MouseButtonWheelRight = input.MouseButtonWheelRight
	MouseButtonBack       = input.MouseButtonBack
	MouseButtonForward    = input.MouseButtonForward
	MouseButton10         = input.MouseButton10
	MouseButton11         = input.MouseButton11

	MouseActionPress   = input.MouseActionPress
	MouseActionRelease = input.MouseActionRelease
	MouseActionMotion  = input.MouseActionMotion
)

The mouse buttons and actions, aliased from input (see input.MouseButton and input.MouseAction).

View Source
const AnnounceTTL = announce.TTL

AnnounceTTL is how long WithAnnounceRegion keeps an announcement visible.

View Source
const DefaultCapabilityProbeTimeout = 500 * time.Millisecond

DefaultCapabilityProbeTimeout is used when WithCapabilityProbe is given a non-positive timeout.

View Source
const InspectorOff = "off"

InspectorOff, as a field of InspectorKeys, leaves that pane off.

Variables

View Source
var ErrInterrupted = errors.New("tui: interrupted by signal")

ErrInterrupted is returned by Run when SIGTERM or SIGHUP arrived and WithExitOnSignal(false) is set. The terminal has been restored.

View Source
var ErrProgramReused = errors.New("tui: Program.Run called more than once; build a new Program with NewProgram")

ErrProgramReused is returned by Run when the Program has already run (or is running). A Program is single-use: its context is cancelled and its message queue drained when Run returns, so build a new one with NewProgram.

Functions

func DrawChild

func DrawChild(buf *cellbuf.Buffer, r cellbuf.Rect, child Model)

DrawChild draws child into the region r of buf: through its DrawCells when it implements CellDrawer, else by drawing its View string with DrawView. The child's DrawCells sees a buffer clipped to r whose origin is r's corner, and the rectangle 0,0,r.W,r.H. It is how a CellDrawer mixes CellDrawer and string children, producing the same screen as if all were strings.

func DrawView

func DrawView(buf *cellbuf.Buffer, r cellbuf.Rect, view string)

DrawView draws the styled string view, as a View result would be shown, into the region r of buf: line i of view is row r.Y+i, clipped to r. It is how a CellDrawer composes a child that only has a View string. A line the cell grid cannot represent (see cellbuf.ErrUnsupported) is drawn as plain text with its escape sequences removed.

func LogToFile

func LogToFile(path, prefix string) (*os.File, error)

LogToFile redirects the standard library logger to the file at path (created if missing, appended to otherwise) and sets its prefix. Nothing is written to the terminal, so logging is safe while a Program owns the screen. The caller should Close the returned file when done.

Types

type AnnounceLevel

type AnnounceLevel int

AnnounceLevel is how urgently an announcement is spoken.

const (
	// Polite announcements are de-duplicated: the same text sent again within
	// one second is written once.
	Polite AnnounceLevel = iota
	// Assertive announcements always write, interrupting any de-duplication.
	Assertive
)

type BackgroundColorEvent

type BackgroundColorEvent = input.BackgroundColorEvent

BackgroundColorEvent is the terminal's reply to ansi.QueryBackgroundColor.

type BackgroundUnknownMsg

type BackgroundUnknownMsg = input.BackgroundUnknownMsg

BackgroundUnknownMsg is sent once, to Update, when WithBackgroundDetection is on and the terminal did not answer the background-colour query within the timeout: the terminal doesn't support OSC 11 (or is too slow), so the app should keep its default theme. It is never sent after a reply arrived.

type BindingsProvider

type BindingsProvider interface {
	Bindings() []keymap.Binding
}

BindingsProvider is implemented by a root Model that can say which key bindings are active right now. A composite model returns the bindings of the widget that has focus (and its own), so the list follows focus and follows a widget's KeyMap when the app rebinds it; a widget's own Bindings method has this shape, so a model that embeds one directly already satisfies it.

type Capabilities

type Capabilities struct {
	// SyncOutput: DECRQM reported mode 2026 (synchronized output) as
	// supported.
	SyncOutput bool
	// GraphemeClusters: DECRQM reported mode 2027 as supported.
	GraphemeClusters bool
	// KittyKeyboard: the terminal answered the kitty keyboard flags query.
	KittyKeyboard bool
	// KittyGraphics: the terminal answered the kitty graphics query with OK.
	KittyGraphics bool
	// Sixel: the terminal's DA1 reply listed feature 4, Sixel graphics. Prefer
	// KittyGraphics when both are set.
	Sixel bool
	// StyledUnderline: extended underlines (SGR 4:n) are believed to render.
	// It is inferred, not queried: true when the terminal spoke a kitty
	// protocol or its XTVERSION names a terminal known to support them.
	StyledUnderline bool
	// Notifications: the terminal answered the kitty desktop-notification
	// query (OSC 99 with p=?), so Assertive announcements may also be sent as
	// notifications (see WithAnnounceRegion).
	Notifications bool
	// XTVersion is the XTVERSION reply ("kitty(0.35.2)"), or "".
	XTVersion string
}

Capabilities is what the terminal reported to the startup probe enabled by WithCapabilityProbe. The zero value means "nothing was confirmed", which is also what a terminal that never answers yields.

type CapabilitiesMsg

type CapabilitiesMsg struct {
	Capabilities Capabilities
}

CapabilitiesMsg is delivered to Update exactly once when the probe finishes: on the DA1 sentinel reply, or on timeout with every capability false.

type CellDrawer

type CellDrawer interface {
	DrawCells(buf *cellbuf.Buffer, r cellbuf.Rect)
}

CellDrawer is an optional interface for a Model (or a child composed into one) that draws straight into a cell grid instead of building an ANSI string. It is the fast path of the cell renderer: when the root Model of a Program implements CellDrawer, each frame is drawn by DrawCells into the frame's grid and diffed from there, and the View string is neither built nor parsed.

DrawCells draws the part of the frame in r (in buf's coordinates) into buf. For the root, buf is the whole frame, as wide and as tall as the terminal, and r is buf.Bounds(); trailing rows left blank in the default style are not part of an inline frame, as trailing empty lines are not in a View string. An implementation should stay inside r (draw through buf.Sub(r) to have it clipped). It must not keep buf, which is reused for the next frame, and starts each frame blank.

View remains the primary contract (Model requires it). A CellDrawer root should still return a View that shows the same screen: the Program uses it when the direct path is unavailable, namely in accessible mode, with the inspector or an announcement region on, when the colour profile is below TrueColor or the terminal lacks styled underlines (those conversions are done on strings), when the terminal height is unknown, when an inline frame has already scrolled rows into history, and when a cell cannot be drawn by the cell renderer (it then falls back as a string frame does).

A CellDrawer root is redrawn on every render: the Program has no View string to compare with the last frame, so an unchanged frame is still diffed (and writes nothing but the frame's framing bytes).

type ChordDef

type ChordDef = input.ChordDef

ChordDef configures one multi-key chord for WithChords: Name is what ChordMsg reports and Keys is the ordered sequence of Key.String() forms (for example "g", "g" or "ctrl+x", "ctrl+s").

type ChordMsg

type ChordMsg = input.ChordMsg

ChordMsg is delivered to Update when the keys of a configured chord have been pressed in sequence within the chord timeout. It replaces the Key events for those keys. It only arrives when the Program was started with WithChords.

type ClipboardMsg

type ClipboardMsg struct {
	// Text is the clipboard contents the terminal reported.
	Text string
	// Err is non-nil when the terminal's answer could not be decoded.
	Err error
}

ClipboardMsg is delivered to Update with the answer to a ReadClipboard.

type Clock

type Clock interface {
	// NewTicker returns a channel that receives the time every d and a
	// func that stops it.
	NewTicker(d time.Duration) (<-chan time.Time, func())
}

Clock supplies the tickers behind Every. The default is the wall clock; tuitest's FakeClock is a Clock a test advances by hand.

type Cmd

type Cmd func() Msg

Cmd is a unit of work run outside the render loop (I/O, timers, ...) whose result is fed back into Update as a Msg. Returning nil means "no side effect."

A Cmd made by FromCtx, Tick or a motion wait needs the Program's context, so calling it directly does not wait: it returns an internal message that the Program recognises and runs. Hand such a Cmd to the Program (return it from Init or Update, or put it in Batch or Sequence), or call RunCmd, as a test does; do not wrap its result in a Msg of your own, which hides it from the Program. Printed, the internal message says so.

func Announce

func Announce(text string) Cmd

Announce returns a Cmd that, in accessible mode, writes text to the transcript as its own line, so a screen reader speaks it once. Outside accessible mode it does nothing: the visible UI is expected to carry the same information.

func AnnounceWith

func AnnounceWith(text string, level AnnounceLevel) Cmd

AnnounceWith is Announce with an explicit politeness level.

func Batch

func Batch(cmds ...Cmd) Cmd

Batch runs several commands concurrently. Their resulting messages are each delivered to Update as they complete, in no particular order.

func ClearScreen

func ClearScreen() Cmd

ClearScreen returns a Cmd that erases the screen and repaints the view.

func EnableMouse

func EnableMouse(mode MouseMode) Cmd

EnableMouse returns a Cmd that switches mouse reporting to mode at run time (MouseOff turns it off).

func EnterAltScreen

func EnterAltScreen() Cmd

EnterAltScreen returns a Cmd that switches to the alternate screen. It is a no-op in accessible mode or if already there.

func Eprintln

func Eprintln(text string) Cmd

Eprintln is Println for the error stream (os.Stderr by default, or WithErrOutput's override): the same permanent-scrollback commit, using the same cursor-up/erase-then-write sequence anchored to the live region's position, just written to a different destination. Terminal cursor position belongs to the terminal, not to whichever file descriptor a write goes through, so a raw, uncoordinated write to stderr while the live region is up can land mid-repaint the same way an uncoordinated stdout write would — Eprintln avoids that the same way Println does.

func Every

func Every(d time.Duration, fn func(time.Time) Msg) (Cmd, func())

Every returns a Cmd that produces fn(t) every d until the returned cancel func is called, plus that cancel func. After cancel returns, no further tick is delivered to Update, including one already in flight. cancel is safe to call more than once and from any goroutine. A non-positive d produces no ticks.

func ExitAltScreen

func ExitAltScreen() Cmd

ExitAltScreen returns a Cmd that leaves the alternate screen.

func FromCtx

func FromCtx(fn CtxCmd) Cmd

FromCtx adapts fn to a Cmd. The Program runs fn on its own goroutine and passes it Program.Context, which is cancelled as Run returns (by any path, including cancellation of the context given to WithContext), so fn can stop early. A Cmd that fn itself calls does not see Run returning; use the context instead. A nil fn gives a nil Cmd.

func Go

func Go(fn func(ctx context.Context) Msg) Cmd

Go returns a Cmd that runs fn with Program.Context and delivers its result. The context is cancelled when Run returns or the context given to WithContext is cancelled, so fn can stop early. It is FromCtx for a plain function: Sequence waits for it and RunCmd runs it the same way.

func PasteCopied

func PasteCopied() Cmd

PasteCopied returns a Cmd that delivers the text of the Program's last WriteClipboard to Update as a PasteEvent, the Msg a bracketed paste arrives as. Nothing is delivered when nothing has been copied. It does not read the system clipboard; ReadClipboard asks the terminal for that.

func Println

func Println(text string) Cmd

Println returns a Cmd that commits text to the terminal's real, permanent scrollback — above the live region that the Model keeps redrawing — the same way Bubbletea's tea.Println or InkUI's Static works. text may contain embedded newlines; each resulting line is written terminated with a real "\r\n" so the terminal's own scrolling absorbs it into history, distinct from a live-region repaint.

func Quit

func Quit() Cmd

Quit returns a Cmd that ends the program's event loop.

func ReadClipboard

func ReadClipboard() Cmd

ReadClipboard returns a Cmd that asks the terminal for the system clipboard through OSC 52 and delivers a ClipboardMsg with the decoded text. It is best effort: many terminals refuse to reveal the clipboard (it is a privacy risk they leave off by default), and under tmux the query needs allow-passthrough. When the terminal does not answer, no message arrives, so do not wait on one.

func Sequence

func Sequence(cmds ...Cmd) Cmd

Sequence runs cmds one at a time, in order: each Cmd's message is delivered to the event loop before the next Cmd starts. A nil Cmd is skipped, and a Cmd that returns nil produces no message. If a Cmd produces QuitMsg the remaining Cmds are not run.

func SetCursorShape

func SetCursorShape(s CursorShape) Cmd

SetCursorShape returns a Cmd that changes the hardware cursor's shape (CSI n SP q). The Program puts the terminal's default shape back on every exit path. It affects the cursor a CursorPlacer shows, or any cursor the terminal draws; terminals that do not know DECSCUSR ignore it.

func SetWindowTitle

func SetWindowTitle(title string) Cmd

SetWindowTitle returns a Cmd that sets the terminal window title. The first use pushes the terminal's current title (CSI 22;0t) so the Program can pop it (CSI 23;0t) on the way out, on every exit path: the window keeps the title it had before the program ran. Terminals without a title stack ignore both sequences and keep the last title set.

func Suspend

func Suspend(fn func() error) Cmd

Suspend returns a Cmd that temporarily hands the real terminal back — undoing Run's raw-mode/alt-screen/mouse/bracketed-paste/cursor setup, running fn with the terminal in its normal (non-raw) state, then restoring everything and forcing a fresh repaint — for handing off to an external interactive program (an editor, a shell prompt) that needs the terminal to itself, the way InkUI's suspendTerminal does. fn is responsible for the external program itself (spawning it with inherited stdio and waiting for it to exit); its returned error, if any, is delivered to Update via SuspendMsg once resumed — Suspend never quits the Program on fn's behalf.

func Tick

func Tick(d time.Duration, fn func(time.Time) Msg) Cmd

Tick returns a Cmd that waits for d and then produces a Msg from fn, useful for animations, spinners, or polling. It is TickCtx wrapped with FromCtx: the Program runs it with its context, so Run returning ends the wait, fn is not called and no Msg is sent, and no goroutine outlives Run waiting on a long timer. The Cmd does nothing useful when called directly; the Program runs it.

Example

Tick returns a Cmd that waits and then produces a Msg. A Program runs it on its own goroutine with its context; RunCmd runs it the same way for a test or an example.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/ows4444/tui"
)

func main() {
	cmd := tui.Tick(time.Millisecond, func(time.Time) tui.Msg { return "tick" })
	fmt.Println(tui.RunCmd(context.Background(), cmd))
}
Output:
tick

func WriteClipboard

func WriteClipboard(text string) Cmd

WriteClipboard returns a Cmd that copies text: the Program writes the OSC 52 sequence that sets the system clipboard to its own output, and keeps text as its copy buffer for PasteCopied. The buffer belongs to the Program, so two Programs in one process do not see each other's copies. Whether the system clipboard changes is up to the terminal; see package clipboard.

type Component

type Component[T any] interface {
	Update(Msg) (T, Cmd)
	View() string
}

Component is the documented contract for an embeddable widget: one composed inside another Model or Component, rather than run directly as a Program's root. Every stateful leaf widget in this library (textinput, viewport, datatable, and the rest) follows it structurally already; this interface just gives that existing convention a name and a place to assert conformance, instead of leaving it as something only visible by reading each widget's source.

Two differences from Model:

  1. No Init. A parent's own Init is responsible for initializing or composing its children — an embeddable widget's "starting state" is just whatever its constructor (conventionally New) returns.
  2. Update returns T, the widget's own concrete type, not Component itself. Go has no covariant return types, so a method that returned Component literally couldn't also return the widget's own type for callers that need to read a field back out of it (as every widget's Update already does: `m, cmd := w.Update(msg)` keeps m as the concrete widget type). Parameterizing on T keeps that natural, already-universal style, while still making the shape explicit.

A widget package proves it satisfies this contract by declaring, once, something like:

var _ tui.Component[Model] = Model{}

which fails to compile the moment Update or View's signature drifts from this shape — real verification, not just a comment asserting it.

type CtxCmd

type CtxCmd func(context.Context) Msg

CtxCmd is a Cmd that receives the Program's context. See FromCtx.

func TickCtx

func TickCtx(d time.Duration, fn func(time.Time) Msg) CtxCmd

TickCtx is Tick as a CtxCmd, for composing inside a FromCtx Cmd of your own. The wait ends when the Program's context is cancelled (Run returned).

type CursorPlacer

type CursorPlacer interface {
	CursorPos() (x, y int, ok bool)
}

CursorPlacer is an optional interface for a Model that wants the real terminal cursor shown, for an IME or a screen magnifier to anchor to. After each frame the Program asks it for a cell: x and y are 0-based, relative to the top-left of the View, and ok false keeps the cursor hidden, as it is for a Model that does not implement this. A position outside the View is clamped to it. The cursor is drawn by the terminal, so the View should not also draw a fake one at the same cell if the app wants only one.

type CursorProvider

type CursorProvider interface {
	CursorCell() (x, y int, ok bool)
}

CursorProvider is the optional interface of a focused widget, such as textinput or textarea, that knows its own cursor cell. A Model (or a model in its Unwrap chain) implementing it gets the hardware cursor placed with the same rules as CursorPlacer, so an IME anchors without app wiring. ok is false while the widget is not focused. CursorPlacer takes precedence when a model has both.

type CursorShape

type CursorShape int

CursorShape is a hardware cursor shape for SetCursorShape (DECSCUSR).

const (
	// CursorShapeDefault is the terminal's own configured shape.
	CursorShapeDefault CursorShape = iota
	CursorShapeBlinkingBlock
	CursorShapeSteadyBlock
	CursorShapeBlinkingUnderline
	CursorShapeSteadyUnderline
	CursorShapeBlinkingBar
	CursorShapeSteadyBar
)

The cursor shapes, numbered as DECSCUSR's parameter.

type FocusEvent

type FocusEvent = input.FocusEvent

FocusEvent reports the terminal window gaining or losing focus. It only arrives if the terminal has focus reporting (DECSET 1004) enabled. A repeat of the state last delivered is dropped by the Program.

type FocusInspector

type FocusInspector interface {
	FocusedID() string
}

FocusInspector is implemented by a root Model that can name the widget that has focus, for the inspector overlay.

type InputErrorMsg

type InputErrorMsg struct {
	Err error
}

InputErrorMsg is delivered once if reading terminal input fails after startup (including io.EOF when the input is closed). Input delivery stops after it — no further Key/Mouse/Paste Msgs arrive — but the Program keeps running; Update decides whether to react, typically by returning tui.Quit().

type InspectorKeys

type InspectorKeys struct {
	// Panel toggles the info panel over the top-right of the view: the named
	// rectangles of the tree (the root Model implements LayoutInspector), the
	// focused id (FocusInspector), and the last frame's size in bytes and time
	// to draw. Default f12.
	Panel string
	// Layout outlines every named rectangle of the tree with a box whose top
	// edge carries the node's name and size ("name WxH"), drawn over the view
	// without covering its content. Default f11. When the panel is also on, it
	// is drawn over the outlines.
	Layout string
	// Messages draws, over the top-left of the view, the most recent messages
	// Update received, newest last, each with its type, value and how long
	// Update took. The last 200 are kept, and are only recorded while this pane
	// is on; the keys that toggle inspector panes are not recorded. Default f10.
	Messages string
	// Model draws, over the bottom-left of the view, a %#v dump of the root
	// Model, wrapped and cut to fit. Default f9.
	Model string
}

InspectorKeys names the key that toggles each inspector pane, as Key.String reports it ("f12", "ctrl+i"). A field left as "" takes that pane's default key (DefaultInspectorKey and its siblings); InspectorOff leaves the pane out.

type Key

type Key = input.Key

Key and KeyType are aliased from tui/input so callers only need to import the root package for the common case.

type KeyAction

type KeyAction = input.KeyAction

KeyAction is an alias of input.KeyAction; see Key.Action.

type KeyReleaseMsg

type KeyReleaseMsg struct{ Key Key }

KeyReleaseMsg reports a kitty key release (WithKeyboard with KeyboardReportEvents). It is delivered instead of a Key so that existing `case tui.Key` code only ever sees presses.

type KeyRepeatMsg

type KeyRepeatMsg struct{ Key Key }

KeyRepeatMsg reports a kitty key auto-repeat (WithKeyboard with KeyboardReportEvents). It is delivered instead of a Key; handle it to react to held keys.

type KeyType

type KeyType = input.KeyType

KeyType is an alias of input.KeyType.

type KeyboardFlags

type KeyboardFlags int

KeyboardFlags is a bitmask of kitty keyboard protocol progressive enhancement flags; see WithKeyboard.

const (
	// KeyboardDisambiguate (flag 1) is what WithKittyKeyboard(true) requests.
	KeyboardDisambiguate KeyboardFlags = 1
	// KeyboardReportEvents (flag 2) asks the terminal to report key repeat
	// and release. Update then receives them as KeyRepeatMsg and
	// KeyReleaseMsg, never as Key. Without this flag they never arrive.
	KeyboardReportEvents KeyboardFlags = 2
	// KeyboardReportAlternates (flag 4) asks for alternate key codes. They
	// are consumed and ignored by the decoder.
	KeyboardReportAlternates KeyboardFlags = 4
	// KeyboardReportAllKeys (flag 8) asks for every key as an escape code.
	KeyboardReportAllKeys KeyboardFlags = 8
)

The kitty keyboard flags WithKeyboard accepts. Values match the protocol.

type LayoutInspector

type LayoutInspector interface {
	InspectLayout() layout.Node
}

LayoutInspector is implemented by a root Model that draws through the layout package and can hand the inspector the tree it drew, so the overlay can list the named rectangles. Name nodes with layout.Named.

type Linearizer

type Linearizer interface {
	Linearize() string
}

Linearizer is an optional interface for a root Model. When the Program runs with WithAccessible(true) it renders Linearize() instead of View(): plain text, one self-contained line per item, state spoken in words rather than glyphs, no alignment padding or box drawing. A Model that doesn't implement it falls back to View() with ANSI sequences stripped. Composite models delegate to their children (e.g. datatable.Model and treeview.Model each have a Linearize method).

type Model

type Model interface {
	// Init runs once, before the first render, to kick off any initial I/O.
	Init() Cmd
	// Update handles one Msg and returns the next Model state plus an
	// optional Cmd to run.
	Update(Msg) (Model, Cmd)
	// View renders the current state as a plain string; embedded ANSI
	// styling (via the ansi package) is fine, but there should be no
	// cursor-movement codes — the renderer owns cursor positioning.
	View() string
}

Model is the Elm-architecture contract every application implements.

type MouseAction

type MouseAction = input.MouseAction

MouseAction is an alias of input.MouseAction.

type MouseButton

type MouseButton = input.MouseButton

MouseButton is an alias of input.MouseButton.

type MouseEvent

type MouseEvent = input.MouseEvent

MouseEvent, MouseButton, and MouseAction are aliased from tui/input the same way Key is. MouseEvent Msgs only arrive when the Program was started with WithMouse.

type MouseMode

type MouseMode int

MouseMode selects how much mouse activity the terminal reports; see WithMouse.

const (
	// MouseOff (the default) reports no mouse activity at all.
	MouseOff MouseMode = iota
	// MouseClick reports only button press/release, no drag or movement.
	MouseClick
	// MouseCellMotion additionally reports motion while a button is held.
	MouseCellMotion
	// MouseAllMotion reports every mouse movement, a button held or not.
	MouseAllMotion
)

type Msg

type Msg any

Msg is any value delivered to Model.Update: a key press, a resize, a timer tick, or an application-defined event returned by a Cmd.

func RunCmd

func RunCmd(ctx context.Context, cmd Cmd) Msg

RunCmd runs cmd the way a Program does and returns the Msg it produces, for tests that call a Cmd directly. A Cmd made by FromCtx, Go, Tick or the motion package waits on a context, which a plain cmd() call cannot supply: RunCmd runs it with ctx, so cancelling ctx ends the wait and gives a nil Msg. A nil cmd gives a nil Msg. Batch and Sequence are not expanded; they are the Program's to run.

type Overlay

type Overlay[T any] interface {
	Open() bool
	Update(Msg) (T, Cmd)
	Render(base string) string
}

Overlay is the documented contract for a widget that renders by compositing onto an existing frame rather than standing alone — dialog.Model, drawer.Model, helpscreen.Model, popover.Model, and toast.Model all follow it structurally already, independently arrived at before this interface existed; Overlay just gives that convention a name and a place to assert conformance, the same way Component did for the plain embeddable-widget shape.

Update returns T, not Overlay itself, for the same covariant-return-type reason Component's doc comment explains.

Show and Hide (which flip Open's backing state) are deliberately NOT part of this interface, for two reasons: every implementation uses a pointer receiver for them (*Model, so they can mutate in place), which puts them outside a value type's method set and therefore outside an interface satisfied by a value the way Open/Update/Render are; and toast.Model.Show additionally returns a Cmd (it kicks off an auto-dismiss timer) where every other implementation's Show returns nothing — a real, legitimate difference this interface doesn't try to paper over by forcing a shape that doesn't fit. Open/Update/Render are this contract's actual common shape: enough for a caller to check visibility and composite an overlay onto a base frame generically, without knowing which concrete overlay it's holding.

A widget package proves it satisfies this contract by declaring, once, something like:

var _ tui.Overlay[Model] = Model{}

type PaletteColorEvent

type PaletteColorEvent = input.PaletteColorEvent

PaletteColorEvent is the terminal's reply to one slot of ansi.QueryPalette (OSC 4). theme.Palette.Observe consumes it.

type PanicError

type PanicError struct {
	Value any
	Stack []byte
}

PanicError is returned by Run when WithRecover(true) is set and the model (Init, Update, View) or a Cmd panicked. Value is the recovered value and Stack the goroutine stack at the panic.

func (*PanicError) Error

func (e *PanicError) Error() string

Error reports the recovered value.

type PasteEvent

type PasteEvent = input.PasteEvent

PasteEvent carries the full text of a bracketed paste as one Msg, rather than as individual Key events. It arrives unless the Program was started with WithBracketedPaste(false).

type Program

type Program struct {
	// contains filtered or unexported fields
}

Program drives a Model: it owns the terminal, the event loop, and the renderer.

func NewProgram

func NewProgram(m Model, opts ...ProgramOption) *Program

NewProgram returns a Program running m, writing to stdout in the alternate screen buffer by default; opts override those defaults.

Example

A Program runs a Model. Here the keys come from a string and the output is discarded, so no terminal is needed; Run returns the final model.

package main

import (
	"fmt"
	"io"
	"strings"

	"github.com/ows4444/tui"
)

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 main() {
	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

func (*Program) Accessible

func (p *Program) Accessible() bool

Accessible reports whether accessible output is on (see WithAccessible). Valid immediately after NewProgram.

func (*Program) Capabilities

func (p *Program) Capabilities() Capabilities

Capabilities returns the probe result. Before the probe has finished (or when WithCapabilityProbe is off) it is the zero value; use ok to tell "nothing supported" from "not known yet".

func (*Program) CapabilitiesKnown

func (p *Program) CapabilitiesKnown() bool

CapabilitiesKnown reports whether the probe has finished.

func (*Program) Clock

func (p *Program) Clock() Clock

Clock returns the Clock given to WithClock, or nil for the wall clock.

func (*Program) ColorProfile

func (p *Program) ColorProfile() ansi.Profile

ColorProfile reports the colour profile applied to output (see WithColorProfile); detected from the environment by default.

func (*Program) Context

func (p *Program) Context() context.Context

Context returns the Program's run-scoped context.Context, derived from the context passed to WithContext (or context.Background(), if none was given). It's valid immediately — call it any time after NewProgram, in particular before Run, so the value can be threaded into the model — and is cancelled when Run returns, by whatever path (see WithContext).

func (*Program) Keymap

func (p *Program) Keymap() *keymap.Registry

Keymap returns a registry of the key bindings the root model reports through BindingsProvider, as of the last frame drawn (or of the initial model before the first). It is built fresh on each call, so it always reflects the focused widget's current KeyMap; a model that is not a BindingsProvider gives an empty registry. The registry is the list helpscreen.FromRegistry and widgets.KeyHints read, so help text cannot drift from behaviour. It is safe to call from any goroutine, provided the model's Bindings is a pure function of the model, as a value-typed Model's is.

func (*Program) Measurer

func (p *Program) Measurer() ansi.Measurer

Measurer reports how this Program's terminal measures grapheme clusters: the zero Measurer (the process-wide ansi.SetClusterWidth setting) until a capability probe (WithCapabilityProbe) has decided, then the probed answer. It is the same value a ResizeMsg carries. Safe to call from any goroutine.

func (*Program) Quit

func (p *Program) Quit()

Quit asks Run to return, as if the model had returned the Quit command. Messages already sent are handled first. It is safe to call from any goroutine and a no-op if Run is not active.

func (*Program) ReducedMotion

func (p *Program) ReducedMotion() bool

ReducedMotion reports the Program's reduced-motion preference — the value passed to WithReducedMotion, or motion.Detect().Reduced() if that option was never used. Valid immediately after NewProgram, the same as Context, so it can be threaded into the model before Run.

func (*Program) Run

func (p *Program) Run() (Model, error)

Run puts the terminal in raw mode, starts the event loop, and blocks until the model quits (via tui.Quit) or an unrecoverable error occurs. It also returns when the context given to WithContext is done, with that context's error, and when a write to the output fails, with an error wrapping the write error. The terminal is always restored before Run returns. A Program runs once: a second call returns ErrProgramReused without touching the terminal.

func (*Program) Send

func (p *Program) Send(msg Msg)

Send delivers msg to the model's Update, as if it had come from the terminal. It is safe to call from any goroutine; messages from one goroutine arrive in call order. Before Run starts, msg is queued and delivered once Run begins (if the queue fills first, Send waits for Run to start). While the queue is full during Run, Send waits for room. After Run has returned, Send drops msg and returns immediately. It never panics.

Send must not be called from Update or View: the event loop is the only drainer, so a full queue would block it forever. Use TrySend there, or return a Cmd.

func (*Program) TrySend

func (p *Program) TrySend(msg Msg) bool

TrySend is like Send but never blocks: it reports whether msg was queued. It returns false if the queue is full or Run has already returned. Before Run starts it queues msg like Send. It is safe to call from any goroutine, including from Update.

type ProgramOption

type ProgramOption func(*Program)

ProgramOption configures a Program at construction time.

func WithAccessible

func WithAccessible(enabled bool) ProgramOption

WithAccessible turns on accessible (linearized) output for screen-reader and plain-text use; it is off by default and changes nothing when unset. While on, the Program never uses the alternate screen (even if WithAltScreen(true) is also given) and emits no cursor-movement, clear-line, synchronized-output, styling (SGR) or OSC sequences. Output is append-only: each frame prints only the lines that changed since the previous one, once, in order, so a screen reader reads new text as it arrives instead of re-reading a repainted screen. The text comes from the root model's Linearize method if it implements Linearizer, and otherwise from View() with ANSI sequences stripped.

Accessible() implies ReducedMotion(): WithReducedMotion(false) cannot override it. Program does not own the theme, so dropping decorative borders stays cooperative: apps call theme.Theme.Plain() when Accessible() is true.

func WithAccessibleAuto

func WithAccessibleAuto() ProgramOption

WithAccessibleAuto turns accessible mode on when the environment asks for it: ACCESSIBLE=1 or TERM=dumb. Otherwise it leaves accessible mode off. Like every option, the last of WithAccessible / WithAccessibleAuto wins.

func WithAltScreen

func WithAltScreen(enabled bool) ProgramOption

WithAltScreen enables or disables the terminal's alternate screen buffer (on by default) so the app doesn't scroll the user's normal history.

func WithAnnounceRegion

func WithAnnounceRegion(rows int) ProgramOption

WithAnnounceRegion reserves rows rows below the view (inline: appended below it; alternate screen: the view is cut to leave them) in which the most recent announcements made with Announce or AnnounceWith are shown for AnnounceTTL each, outside accessible mode. The region is blank when there is nothing to show, and is redrawn once an announcement expires. The model's View is not changed. rows <= 0 turns it off (the default); accessible mode ignores it, since announcements there go to the transcript.

When a capability probe (WithCapabilityProbe) reports OSC 99 support (Capabilities.Notifications), Assertive announcements are also sent as a desktop notification, whether or not a region is configured.

func WithBackgroundDetection

func WithBackgroundDetection(timeout time.Duration) ProgramOption

WithBackgroundDetection asks the terminal for its background colour (OSC 11, ansi.QueryBackgroundColor) once, at startup. A supporting terminal answers and Update receives a BackgroundColorEvent, which theme.ForBackground turns into a Light or Dark theme. If no answer arrives within timeout, Update receives a single BackgroundUnknownMsg instead, so the app can stop waiting. Detection also queries ANSI colours 0-15 (OSC 4, ansi.QueryPalette); each answer reaches Update as a PaletteColorEvent, which theme.Palette.Observe records so contrast checks use the real palette. A timeout <= 0 leaves detection off (the default): nothing is sent.

func WithBidi

func WithBidi(enabled bool) ProgramOption

WithBidi turns on display reordering per the Unicode Bidirectional Algorithm (UAX #9) for terminals that draw text in logical order. Each line of the View is reordered by itself, taking its base direction from its first strong character, with every character keeping its style and link and brackets in right-to-left runs mirrored. It changes only what is drawn: Update still sees logical text, the cursor and the models' own editing are not remapped, and a frame drawn straight from a cell grid is not reordered. A line with no right-to-left character is written exactly as without the option, and with the option off (the default) every frame is byte-identical to one without it. Terminals that run the algorithm themselves must leave it off, or the text is reordered twice. The tables are from Unicode 17.0.0.

func WithBracketedPaste

func WithBracketedPaste(enabled bool) ProgramOption

WithBracketedPaste sets bracketed-paste mode (DEC 2004). It is on by default: a paste is delivered to Update as a single PasteEvent carrying the whole pasted text, rather than as a flood of individual Key events that a pasted newline could turn into Enter presses. Pass false to opt out and receive pasted text as keys.

func WithCapabilityProbe

func WithCapabilityProbe(timeout time.Duration) ProgramOption

WithCapabilityProbe makes Run send, at startup, DECRQM for modes 2026 and 2027, the kitty keyboard and kitty graphics queries, the OSC 99 desktop notification query and XTVERSION, followed by DA1 as a sentinel. The first DA1 reply ends the probe: Update receives one CapabilitiesMsg and Program.Capabilities reports the result. If DA1 is not answered within timeout (non-positive: DefaultCapabilityProbeTimeout) the message carries every capability false. While probing is on, mode 2026 is only used, and styled underlines are only emitted, when the terminal reported support. When the terminal reports mode 2027 as settable the probe also sends ESC[?2027h (reset on exit) and turns cluster widths on; otherwise widths are per codepoint. Either way the answer becomes Program.Measurer and is delivered to Update as a second ResizeMsg carrying it; the process-wide ansi.SetClusterWidth setting is left alone. Off by default; ignored under WithAccessible.

func WithCellRenderer

func WithCellRenderer(enabled bool) ProgramOption

WithCellRenderer selects the cell-buffer renderer, which is the default (WithCellRenderer(false) selects the line renderer). Each frame's View is parsed once into a grid of cells and only the cells that changed are written, with relative cursor movement, which writes fewer bytes than rewriting whole changed rows. The screen it produces is the same as the line renderer's, with one difference: rows are style-independent, so a style a View row leaves open does not carry into the next row. Tabs are expanded. A row it cannot represent (control characters, escapes other than SGR and OSC 8, unknown SGR codes) is rewritten whole, as the line renderer would; the rest of the frame is still diffed. The whole frame is drawn by the line renderer when the view is taller than the terminal, or when an unrepresentable row appears in a frame that has wide characters.

func WithChordTimeout

func WithChordTimeout(d time.Duration) ProgramOption

WithChordTimeout sets how long a chord prefix waits for its next key (default input.DefaultChordTimeout, 500ms). It has no effect without WithChords.

func WithChords

func WithChords(defs ...ChordDef) ProgramOption

WithChords makes the Program recognise multi-key chords such as "g" "g" or "ctrl+x" "ctrl+s". Every Key passes through a chord matcher before Update: when keys complete a chord, Update receives one ChordMsg instead of those Keys; a key that cannot start or continue any chord is delivered at once, unchanged; and if a prefix turns out not to be a chord (or the timeout, WithChordTimeout, passes with nothing more typed) its keys are delivered in order, exactly as if chords were off. A key that is the start of some chord is therefore held back for up to the timeout, which is inherent to chords. Without this option keys are delivered as before.

func WithClock

func WithClock(c Clock) ProgramOption

WithClock makes Every read its ticks from c instead of the wall clock.

func WithColorProfile

func WithColorProfile(p ansi.Profile) ProgramOption

WithColorProfile makes the Program rewrite the colours in everything it paints to what p can display: truecolor and 256-colour escapes are mapped to the nearest colour p supports, and under ansi.NoColor all colour is removed while bold, underline and other attributes stay. It applies to frames and to text committed with Println and Eprintln, so it works with every widget without their cooperation. Typical use:

tui.NewProgram(m, tui.WithColorProfile(ansi.TrueColor))

Without this option NewProgram uses ansi.DetectColorProfileFor on the output, which reads, in order: NO_COLOR (non-empty gives ansi.NoColor, per https://no-color.org); CLICOLOR_FORCE (non-empty, not "0") to colour a non-terminal; otherwise output that is not a terminal, or CLICOLOR=0, gives ansi.NoColor; TERM=dumb gives ansi.NoColor; COLORTERM (truecolor or 24bit), WT_SESSION and TERM_PROGRAM (iTerm.app, WezTerm, vscode, ghostty, Hyper) give ansi.TrueColor, and TERM_PROGRAM=Apple_Terminal gives ansi.ANSI256; then the TERM name (xterm-kitty, xterm-ghostty, alacritty and wezterm give ansi.TrueColor; 256color gives ansi.ANSI256; empty gives ansi.NoColor, or ansi.ANSI16 on Windows; anything else ansi.ANSI16). Truecolor terminals that export none of these (some SSH sessions, tmux with TERM=screen) are detected as lower depth; pass WithColorProfile(ansi.TrueColor) to opt out. Passing this option, even ansi.TrueColor, overrides NO_COLOR.

func WithContext

func WithContext(ctx context.Context) ProgramOption

WithContext supplies the parent context.Context for the Program's run (default context.Background(), also used for a nil ctx). Cancelling ctx ends Run, which restores the terminal and returns ctx.Err(). Program derives its own cancelable context from it and cancels that derived context when Run returns — on a normal quit, an error, a panic (via the same defer chain that restores the terminal), or a SIGTERM/SIGHUP — so a Cmd built with the context returned by (*Program).Context can observe shutdown instead of leaking past it. This never changes the Cmd func() Msg signature: a Cmd that wants cancellation just closes over that context itself, e.g.:

p := tui.NewProgram(model{})
ctx := p.Context()
// thread ctx into the model (a field, a closure) before p.Run()

func (m model) Init() tui.Cmd {
    return func() tui.Msg {
        req, _ := http.NewRequestWithContext(m.ctx, http.MethodGet, url, nil)
        resp, err := http.DefaultClient.Do(req)
        // ...
    }
}

func WithErrOutput

func WithErrOutput(w io.Writer) ProgramOption

WithErrOutput sets where Eprintln writes to (default os.Stderr). Given an *os.File it is the terminal's error stream; any other io.Writer, a bytes.Buffer in a test or a log, receives the text as it is.

func WithEscTimeout

func WithEscTimeout(d time.Duration) ProgramOption

WithEscTimeout sets how long the input parser waits for the rest of an escape sequence after a lone ESC byte before reporting a bare Escape key. d <= 0 keeps the default (input.DefaultEscTimeout).

func WithExitOnSignal

func WithExitOnSignal(enabled bool) ProgramOption

WithExitOnSignal chooses what happens on SIGTERM or SIGHUP. The terminal is always restored first. When enabled (the default) the process then exits with status 1. When disabled, Run cancels its context and returns ErrInterrupted so the application can run its own shutdown.

func WithFocusReporting

func WithFocusReporting(enabled bool) ProgramOption

WithFocusReporting turns terminal focus reporting (DECSET 1004) on for the life of the Program, so Update receives a FocusEvent whenever the terminal window gains or loses focus. It is re-enabled after Suspend and switched off again when the Program restores the terminal. A FocusEvent whose state equals the last one delivered is dropped, so Update sees only changes; the first event is always delivered. Off by default: without it no focus sequence is emitted.

func WithFrameLog

func WithFrameLog(w io.Writer) ProgramOption

WithFrameLog writes one line to w for every frame the renderer draws, so a flicker, an over-eager repaint or a slow frame can be seen without touching the terminal: point it at a file and tail it while the app runs.

n=7 t=1042 kind=diff rows=24 changed=2 bytes=118 us=61

The fields are, in order: n, the frame number from 1; t, milliseconds since the event loop started; kind, "diff" for a frame diffed against the previous one, "full" for one drawn with no previous frame to diff against (the first frame, the repaint after a resize, and the frame after a Suspend or a Println) and "accessible" for a WithAccessible transcript write; rows, the height of the live region (the lines written, in accessible mode); changed, how many of those rows were rewritten (cleared rows count, and an unchanged frame reports 0); bytes, what the frame wrote to the terminal, including the synchronized-output wrapper; and us, the microseconds spent building and writing it. With WithCellRenderer, a frame the cell renderer could not draw and handed to the line renderer is kind=fallback (whatever it would otherwise have been) and the line ends with a further field, reason=<slug>, for example "reason=view_taller_than_terminal"; no other kind has that field. A row the cell renderer cannot represent (a control character, an unknown escape) is drawn alone with the line strategy and the frame stays diff or full; the line then ends with fallback_rows=<row>:<slug>[,...] (0-based rows drawn that way this frame), for example "fallback_rows=3:control_character".

The line is written from the event loop after the frame, so it cannot reorder terminal output, and the terminal receives exactly the same bytes as without the option. An error from w is ignored: the log is a diagnostic and never stops the Program. w must not be the terminal's own output, which it would corrupt; a nil w disables the log.

func WithInput

func WithInput(r io.Reader) ProgramOption

WithInput sets where the program reads keys from (default os.Stdin). Given an *os.File, the Program reads it as a terminal: raw mode when it is one, and a read that Quit can cancel. Given any other io.Reader, Run does not require a terminal and does not switch raw mode, so a program can be driven from a strings.Reader in a test; a file that should be read that way can be wrapped (struct{ io.Reader }{f}). When the reader reaches io.EOF the Program quits, as if the model had returned Quit, once the events before EOF have been handled; any other read error is delivered to Update as an InputErrorMsg. Cancelling a blocked read is not possible for a plain io.Reader: if r blocks forever, Run still returns on Quit, but the reading goroutine stays parked in r.Read. A reader that is also an io.Closer is closed when the Program quits, unless WithInputCloser names another. Of several WithInput options, the last wins.

func WithInputCloser

func WithInputCloser(c io.Closer) ProgramOption

WithInputCloser registers c to be closed, exactly once, when the Program quits. Closing it unblocks a read parked in a WithInput source that is not a file (an io.Pipe reader, say), so Run returns without waiting out the reader stop timeout. A WithInput source that itself implements io.Closer is closed the same way without this option; c takes precedence over it.

func WithInspector

func WithInspector(keys InspectorKeys) ProgramOption

WithInspector turns on the devtool overlays named by keys: a zero InspectorKeys turns on all four at their default keys. Pressing a pane's key shows it and pressing it again removes it; the key is consumed by the Program and never reaches Update. Without this option nothing is tracked and nothing is drawn.

func WithKeyboard

func WithKeyboard(flags KeyboardFlags) ProgramOption

WithKeyboard opts into the kitty keyboard protocol with the given flags (OR them together). Bits outside 1/2/4/8 are dropped. Adding KeyboardReportEvents makes key repeat and release arrive as KeyRepeatMsg and KeyReleaseMsg (never as Key, so `case tui.Key` sees presses only and chords ignore them); without it a release is dropped as before and existing Update code never sees either. The flags are pushed on start and Resume after Suspend, and popped on quit, panic and Suspend. WithKittyKeyboard(true) is equivalent to WithKeyboard(KeyboardDisambiguate) and the two combine by OR. A terminal without the protocol ignores it.

func WithKittyKeyboard

func WithKittyKeyboard(enabled bool) ProgramOption

WithKittyKeyboard opts into the kitty keyboard protocol's "disambiguate escape codes" enhancement (CSI ?u), letting input.Key events distinguish modifier combinations the legacy xterm CSI-modifier encoding can't represent — Shift+Enter vs. plain Enter, Ctrl+Shift+<letter> vs. Ctrl+<letter> — without changing how arrows, Home/End, or function keys are decoded. A terminal that doesn't support the protocol ignores the enable/disable sequences entirely and every key continues to decode via the existing legacy parsing, unaffected. Off by default: enabling it costs nothing on a supporting terminal, but it's opt-in rather than always-on so a Program's observable input behavior doesn't change silently between runs based on what terminal happens to be attached.

func WithLinearizeFullLine

func WithLinearizeFullLine() ProgramOption

WithLinearizeFullLine makes accessible mode write a transcript line in full again, as a new line, when it grows, instead of only the appended suffix. Default: only the suffix.

func WithMaxConcurrentCmds

func WithMaxConcurrentCmds(n int) ProgramOption

WithMaxConcurrentCmds limits how many Cmds run at the same time to n. Cmds beyond the limit wait and start in the order the event loop received them (a Batch's Cmds count in their argument order). n <= 0, the default, means no limit. The limit applies to Cmds the loop dispatches; the steps of a Sequence and Every timers run outside it.

func WithMaxFPS

func WithMaxFPS(fps int) ProgramOption

WithMaxFPS caps how often render() repaints in response to ordinary Msgs, coalescing a burst of rapid updates (e.g. a fast tui.Tick) into fewer terminal writes. When WithMaxFPS is never called a default cap of 60 fps applies to non-input Msgs only (a trailing flush still draws the latest state; input Msgs render immediately). Calling WithMaxFPS with fps <= 0 turns every cap off: every Msg renders. A positive fps caps all ordinary Msgs, input included. Update always still runs for every Msg regardless of the cap — only the repaint is skipped, so a skipped render's Msg still advances the model, and the next render paints its current, not stale, state. A printMsg (tui.Println) or QuitMsg always renders immediately, bypassing the cap, so a permanent scrollback commit or the final quit frame is never delayed or dropped. A repaint the cap skipped is not lost: if no later Msg arrives, the latest View is drawn once the frame interval has passed, so the screen never keeps a stale frame after a burst.

func WithMouse

func WithMouse(mode MouseMode) ProgramOption

WithMouse enables mouse reporting at the given MouseMode, delivered to Update as MouseEvent. Off (the default) reports nothing.

func WithOutput

func WithOutput(w io.Writer) ProgramOption

WithOutput sets where the program renders to (default os.Stdout). Give it an *os.File for a terminal; the Program queries its size and switches its modes. Any other io.Writer, such as a bytes.Buffer in a test, has no terminal size: the Program assumes 80x24 (a ResizeMsg can still deliver another size) and never queries or resizes it. Mode sequences (alternate screen, cursor, synchronized output) are written to w like any other output.

func WithRecorder

func WithRecorder(w io.Writer) ProgramOption

WithRecorder records the session to w as an asciicast v2 file (https://docs.asciinema.org/manual/asciicast/v2/): a header line, then one JSON event per line, "o" for each write to the terminal (a frame, or a mode change) and "i" for each chunk of input bytes read. The file plays in asciinema unmodified. Pair it with WithRecorderSidecar and tuitest.Replay to reproduce the session byte-for-byte in a test.

Recording turns the frame cap off (as WithMaxFPS(0)): a capped Program paces non-input frames by the wall clock, so what it draws would not be a function of the messages it received. Input bytes are recorded as typed, so a recording of a password prompt contains the password. A write error on w stops the recording quietly and never fails the Program.

func WithRecorderSidecar

func WithRecorderSidecar(w io.Writer) ProgramOption

WithRecorderSidecar records, to w, the facts Replay needs that an asciicast cannot hold: the exact input bytes (base64), terminal sizes, the Every tickers that fired, a mark per message handed to Update, and a SHA-256 of each write to the terminal. It is JSON lines, written one whole line per Write call. It is independent of WithRecorder, but the pair is what tuitest.Replay reads. Like WithRecorder it turns the frame cap off.

Only what flows through the Program's own seams is recorded: input bytes, sizes and Every ticks. Messages from Program.Send, or Cmd results that depend on something outside the model (the network, the wall clock), are not, so a model that needs them does not replay.

func WithRecover

func WithRecover(enabled bool) ProgramOption

WithRecover makes Run recover panics from Init, Update, View and Cmds, restore the terminal, and return a *PanicError. Without it (the default) the terminal is restored and the panic continues.

func WithReducedMotion

func WithReducedMotion(enabled bool) ProgramOption

WithReducedMotion overrides the Program's reduced-motion preference, read back via (*Program).ReducedMotion. Without this option, the preference defaults to motion.Detect() (the NO_ANIMATION and REDUCE_MOTION environment variables), so existing motion.Preference-aware code keeps working whether or not a Program explicitly opts in.

This is a signal, not automatic enforcement: Program has no way to know which of a widget's returned Cmds are animation-driven, or which part of its View() output is decorative rather than content, so it can't suppress either on its own. An app that wants to honor reduced motion combines ReducedMotion() with the library's existing cooperative pieces — motion.Preference-aware widgets (spinner.Model, skeleton.Model, streamtext.Model's Skip) for animation, and theme.Theme.Plain for box-drawing decoration — the same "framework provides the signal, caller decides" pattern WithContext already uses for cancellation.

func WithResizePoll

func WithResizePoll(d time.Duration) ProgramOption

WithResizePoll sets how often the terminal size is polled where polling is used to detect resizes (Windows; Unix platforms rely on SIGWINCH and ignore it). d <= 0 keeps the default of 250ms.

func WithSuspendOnCtrlZ

func WithSuspendOnCtrlZ(enabled bool) ProgramOption

WithSuspendOnCtrlZ makes Ctrl+Z suspend the process to the background, as a shell job would, instead of reaching Update. Off by default: without this option Ctrl+Z is an ordinary Key event (Type KeyCtrl, Code 'z').

On Ctrl+Z (a press; a kitty release or repeat is not one) Run restores the terminal exactly as Suspend does (modes off, cooked mode back), stops the process, and blocks until the shell continues it (SIGCONT, `fg`). It then re-enters raw mode and every mode the options asked for (including WithKeyboard flags), restarts the input reader, re-reads the terminal size (Update receives a ResizeMsg if it changed) and repaints the whole frame. Update sees neither the Ctrl+Z nor a SuspendMsg. Messages sent with Send while stopped are handled after the resume.

The process stops itself with SIGSTOP, which cannot be caught or ignored, so it works even in an orphaned process group where SIGTSTP is discarded; job control reports it as stopped either way. Only the process stops, not its whole process group.

On Windows, which has no job control, and on other platforms without SIGSTOP, this option is a no-op and Ctrl+Z stays an ordinary Key event.

func WithTerminal

func WithTerminal(t Terminal) ProgramOption

WithTerminal replaces the terminal the Program controls. The default is the OS terminal behind the input and output files. See Terminal.

func WithTheme

func WithTheme(auto theme.Auto) ProgramOption

WithTheme makes the Program own the theme. Before Init (and so before the first frame) a model that implements Themeable gets auto.Dark through SetTheme; the Program then asks the terminal for its background colour (OSC 11, as WithBackgroundDetection does, with a 300ms timeout unless that option set one) and, if the terminal answers, calls SetTheme again with auto.Light or auto.Dark to match. A terminal that does not answer keeps auto.Dark. The BackgroundColorEvent and BackgroundUnknownMsg still reach Update.

type QuitMsg

type QuitMsg struct{}

QuitMsg, once it reaches the event loop, ends Program.Run. Applications don't construct it directly — return tui.Quit() from Update instead.

type ReplyEvent

type ReplyEvent = input.ReplyEvent

ReplyEvent is a terminal string-sequence reply (OSC/DCS/SOS/PM/APC) that is not otherwise decoded.

type ResizeMsg

type ResizeMsg struct {
	Width, Height int
	Measurer      ansi.Measurer
}

ResizeMsg is sent once at startup and again on every terminal resize (SIGWINCH).

Measurer says how this Program's terminal draws grapheme clusters, which a capability probe (WithCapabilityProbe) can change after start-up; the Program then sends the same size again with the new Measurer. It is the zero value, which follows the process-wide ansi.SetClusterWidth setting, until a probe says otherwise. A component that measures text for this terminal uses msg.Measurer instead of the package-level ansi.Width, so two Programs in one process (an SSH server, parallel tests) never measure for each other.

type ResizeNotifier

type ResizeNotifier interface {
	Resizes() <-chan struct{}
}

ResizeNotifier is an optional interface for a Terminal that can say when its size changed, so a remote session (SSH, a websocket) delivers resizes through the same port as everything else instead of calling Send(ResizeMsg) itself.

Run calls Resizes once, and each value received means "the size may have changed": the Program re-reads Size and delivers one ResizeMsg. A burst of values while the Program is busy is coalesced into one message carrying the last size, so a sender should not block on the channel; a buffered channel of one, written with a non-blocking send, is enough. The channel may be closed when the session ends. A Terminal without this interface is watched the OS way (SIGWINCH on unix, console events on Windows) and keeps working with Send(ResizeMsg).

type Rewrapper

type Rewrapper interface {
	Rewrap(inner Model) Model
}

Rewrapper is the optional companion of Unwrapper for a wrapper that can be rebuilt around a different inner Model. Rewrap returns a copy of the receiver that wraps inner, the way Update returns the next model.

type SuspendMsg

type SuspendMsg struct{ Err error }

SuspendMsg is delivered to Update once a Suspend's fn has returned and the terminal has been restored, carrying fn's error, if any. When fn succeeded but raw mode could not be re-entered, Err is that error and the terminal is still in its normal mode.

type Terminal

type Terminal interface {
	// IsTerminal reports whether the input is an interactive terminal. Run
	// returns an error without it, unless WithInput was used, in which
	// case Run does not ask.
	IsTerminal() bool
	// Size reports the size in cells. ok is false when it is unknown; a
	// Program then keeps the size it has (80x24 for a plain writer) and
	// follows tui.ResizeMsg.
	Size() (w, h int, ok bool)
	// MakeRaw puts the input in raw mode. mouse is true when mouse reporting
	// is on; Windows turns QuickEdit off for it, other platforms ignore it.
	MakeRaw(mouse bool) (restore func() error, err error)
	// EnableOutputVT turns on the output's virtual-terminal processing.
	// restore may be nil when there was nothing to enable.
	EnableOutputVT() (restore func() error, err error)
}

Terminal is the control plane of the terminal a Program runs on: its size, raw mode and output virtual-terminal processing. Bytes still flow through the reader and writer given with WithInput and WithOutput, so a remote session supplies all three: a Terminal that answers for the far end, plus the connection as reader and writer. A Terminal that also implements ResizeNotifier reports resizes through the same port.

Every restore func undoes exactly the change its call made, and is called at most once. Methods may be called from more than one goroutine (a signal handler restores while the loop runs).

type ThemeSetter

type ThemeSetter[T any] interface {
	SetTheme(theme.Theme) T
}

ThemeSetter is the contract for a widget that takes a theme.Theme and can be re-themed after construction. SetTheme returns the widget's own type T, the same way Component.Update does, so a value-type widget can implement it (tui.Themeable cannot: it returns Model).

A package proves it satisfies the contract with

var _ tui.ThemeSetter[Model] = Model{}

A root model that implements Themeable (see WithTheme) forwards the theme to its widgets with their SetTheme methods.

type Themeable

type Themeable interface {
	SetTheme(theme.Theme) Model
}

Themeable is implemented by a Model that wants the Program's theme (see WithTheme). SetTheme returns the model with the theme applied, the way Update returns the next model; a root model forwards it to its widgets.

type Unwrapper

type Unwrapper interface {
	// Unwrap returns the Model this one wraps, or nil if there is none.
	Unwrap() Model
}

Unwrapper is implemented by a root Model that wraps another Model and adds behaviour around it (a test harness, a logger, a layout shell) without re-implementing the optional interfaces of what it wraps.

When the Program looks for an optional interface (CursorPlacer, Linearizer, Themeable, BindingsProvider) it asks the root Model first, then the Model returned by Unwrap, and so on down the chain; the first match wins. A wrapper therefore needs only Init, Update, View and Unwrap, however many optional interfaces the model it wraps has.

A wrapper that also implements Rewrapper takes part in Themeable: the Program re-themes the wrapped Model and puts the result back through Rewrap. Without Rewrap a theme set through the wrapper is not applied, because the wrapper cannot say how to hold the new Model.

Directories

Path Synopsis
Package accordion is a list of collapsible sections — InkUI's "Accordion".
Package accordion is a list of collapsible sections — InkUI's "Accordion".
Package ansi provides the raw terminal escape sequences and a small styling builder used to render colored, styled text.
Package ansi provides the raw terminal escape sequences and a small styling builder used to render colored, styled text.
Package appshell composes a common interactive-CLI-tool layout — a header, a full-width input, a scrollable content area, and an optional key-hints footer — out of existing widgets, without introducing any new rendering primitive of its own.
Package appshell composes a common interactive-CLI-tool layout — a header, a full-width input, a scrollable content area, and an optional key-hints footer — out of existing widgets, without introducing any new rendering primitive of its own.
Package autocomplete is a text input with a filtered suggestion dropdown — InkUI's "Autocomplete".
Package autocomplete is a text input with a filtered suggestion dropdown — InkUI's "Autocomplete".
Package cellbuf is a retained grid of terminal cells: the fast path for a widget that wants to draw straight into cells instead of building a styled string that the renderer parses back every frame (spec S04).
Package cellbuf is a retained grid of terminal cells: the fast path for a widget that wants to draw straight into cells instead of building a styled string that the renderer parses back every frame (spec S04).
Package clipboard is an interactive "copy to clipboard" button — Enter or Space activates it, writing an OSC52 clipboard-set escape sequence for the configured Text and showing a timed "Copied!" confirmation that reverts automatically.
Package clipboard is an interactive "copy to clipboard" button — Enter or Space activates it, writing an OSC52 clipboard-set escape sequence for the configured Text and showing a timed "Copied!" confirmation that reverts automatically.
Package clockview is a wall-clock / stopwatch / countdown-timer widget.
Package clockview is a wall-clock / stopwatch / countdown-timer widget.
Package colorpicker is a palette-swatch + hex-input color picker.
Package colorpicker is a palette-swatch + hex-input color picker.
Package commandpalette is a text input with a fuzzy-filtered dropdown of Commands — a "Ctrl+K" style command palette.
Package commandpalette is a text input with a fuzzy-filtered dropdown of Commands — a "Ctrl+K" style command palette.
Package confirm is a yes/no prompt widget — InkUI's "Confirm".
Package confirm is a yes/no prompt widget — InkUI's "Confirm".
Package contextmenu is a popup menu opened at an anchor point, typically on a right-click or a key press.
Package contextmenu is a popup menu opened at an anchor point, typically on a right-click or a key press.
Package datatable is widgets.Table plus row navigation — InkUI's "DataTable".
Package datatable is widgets.Table plus row navigation — InkUI's "DataTable".
Package datepicker is a keyboard-navigable calendar widget operating directly on time.Time (stdlib time only, no external date library).
Package datepicker is a keyboard-navigable calendar widget operating directly on time.Time (stdlib time only, no external date library).
Package dialog is a modal overlay — InkUI's "Dialog": a bordered box with a title and message, composited on top of the rest of the screen via layout.Overlay rather than replacing it, dismissed with Enter/Esc.
Package dialog is a modal overlay — InkUI's "Dialog": a bordered box with a title and message, composited on top of the rest of the screen via layout.Overlay rather than replacing it, dismissed with Enter/Esc.
Package drawer is a click/key-triggered overlay holding arbitrary (possibly multi-line) content, anchored to an edge of the base view (left/right/top/bottom) rather than centered over it (unlike dialog.Model) or anchored to a point (unlike popover.Model).
Package drawer is a click/key-triggered overlay holding arbitrary (possibly multi-line) content, anchored to an edge of the base view (left/right/top/bottom) rather than centered over it (unlike dialog.Model) or anchored to a point (unlike popover.Model).
Package emailinput is a thin wrapper over textinput.Model that rejects whitespace runes as typed input.
Package emailinput is a thin wrapper over textinput.Model that rejects whitespace runes as typed input.
Package errorretry is a small interactive widget for showing an error with a retry/dismiss keybinding — Enter or 'r' retries (up to MaxRetries), Esc always dismisses.
Package errorretry is a small interactive widget for showing an error with a retry/dismiss keybinding — Enter or 'r' retries (up to MaxRetries), Esc always dismisses.
examples
agentshell command
Command agentshell is a template for an agent CLI.
Command agentshell is a template for an agent CLI.
asyncload command
Command asyncload demonstrates the async-Cmd-resolves-into-a-Msg pattern: a tui.Cmd is just a func() tui.Msg, and Program.dispatch (see program.go) runs each Cmd on its own goroutine and feeds whatever Msg it returns back into the event loop.
Command asyncload demonstrates the async-Cmd-resolves-into-a-Msg pattern: a tui.Cmd is just a func() tui.Msg, and Program.dispatch (see program.go) runs each Cmd on its own goroutine and feeds whatever Msg it returns back into the event loop.
buildlog command
Command buildlog demonstrates tui.Println (see cmds.go): a Cmd that commits text permanently to the terminal's real scrollback, above the live-updating region, rather than being part of an ordinary View() repaint.
Command buildlog demonstrates tui.Println (see cmds.go): a Cmd that commits text permanently to the terminal's real scrollback, above the live-updating region, rather than being part of an ordinary View() repaint.
canvas command
Command canvas is a custom widget that draws straight into the cell grid through DrawCells: a colour gradient with a moving marker.
Command canvas is a custom widget that draws straight into the cell grid through DrawCells: a colour gradient with a moving marker.
chat command
Command chat is a manual showcase of the InkUI-parity widgets added after the original dashboard/form/list examples: Gauge, CodeBlock, DiffView, markdown.Render, streamtext (Typewriter), TokenCounter and layout.Column and layout.Row.
Command chat is a manual showcase of the InkUI-parity widgets added after the original dashboard/form/list examples: Gauge, CodeBlock, DiffView, markdown.Render, streamtext (Typewriter), TokenCounter and layout.Column and layout.Row.
counter command
cursorfield command
Command cursorfield shows a text input whose cursor is the terminal's real cursor: the model implements tui.CursorPlacer by forwarding textinput.Model.CursorCell, so an IME or a screen magnifier can follow the field.
Command cursorfield shows a text input whose cursor is the terminal's real cursor: the model implements tui.CursorPlacer by forwarding textinput.Model.CursorCell, so an IME or a screen magnifier can follow the field.
dashboard command
faces command
Command faces is a gallery for the faces package.
Command faces is a gallery for the faces package.
focus command
Command focus is the canonical, minimal reference for coordinating Focus()/Blur() across mixed widget types in this repo.
Command focus is the canonical, minimal reference for coordinating Focus()/Blur() across mixed widget types in this repo.
form command
inlinebuild command
Command inlinebuild is an inline-mode (no alternate screen) build log.
Command inlinebuild is an inline-mode (no alternate screen) build log.
inlinechat command
Command inlinechat is an inline-mode (no alternate screen) chat prompt.
Command inlinechat is an inline-mode (no alternate screen) chat prompt.
inlinespinners command
Command inlinespinners is an inline-mode (no alternate screen) list of tasks: finished tasks show a check, the running one a spinner, the rest wait.
Command inlinespinners is an inline-mode (no alternate screen) list of tasks: finished tasks show a check, the running one a spinner, the rest wait.
inlinetall command
Command inlinetall is an inline-mode (no alternate screen) transcript that grows past the terminal height.
Command inlinetall is an inline-mode (no alternate screen) transcript that grows past the terminal height.
inspector command
list command
login command
Command login is a credential sign-in screen built on package form, with a live design-system panel beside it for trying every theming control the library has.
Command login is a credential sign-in screen built on package form, with a live design-system panel beside it for trying every theming control the library has.
loginflow command
Command loginflow is the canonical reference for a login-shaped screen composed entirely from the existing widget catalog — widgets.BigText for the title, widgets.Banner for an announcement, and picker.Model configured as a numbered-select account menu.
Command loginflow is the canonical reference for a login-shaped screen composed entirely from the existing widget catalog — widgets.BigText for the title, widgets.Banner for an announcement, and picker.Model configured as a numbered-select account menu.
mixedscreen command
Command mixedscreen composes a string child and a cell child into one screen with DrawChild and DrawView.
Command mixedscreen composes a string child and a cell child into one screen with DrawChild and DrawView.
pager command
probe command
Command probe shows what your terminal supports, using the library's opt-in capability options: colour depth (with NO_COLOR, COLORTERM and TERM honoured), focus in/out reporting, and OSC 11 background detection.
Command probe shows what your terminal supports, using the library's opt-in capability options: colour depth (with NO_COLOR, COLORTERM and TERM honoured), focus in/out reporting, and OSC 11 background detection.
procstream command
Command procstream demonstrates streaming a child process's stdout into a scrolling widget (logview.Model) line by line, as it arrives, rather than buffering the whole thing and rendering it once the process exits.
Command procstream demonstrates streaming a child process's stdout into a scrolling widget (logview.Model) line by line, as it arrives, rather than buffering the whole thing and rendering it once the process exits.
router command
Command router is the canonical reference for switching between independent top-level screens in this repo, the way examples/focus is the canonical reference for Focus/Blur coordination.
Command router is the canonical reference for switching between independent top-level screens in this repo, the way examples/focus is the canonical reference for Focus/Blur coordination.
settings command
setupflow command
Command setupflow follows the same composition-only shape as examples/welcomescreen and examples/loginflow: widgets.BigText for the title, widgets.Alert for a status message, and picker.Model as a numbered-select menu of setup steps.
Command setupflow follows the same composition-only shape as examples/welcomescreen and examples/loginflow: widgets.BigText for the title, widgets.Alert for a status message, and picker.Model as a numbered-select menu of setup steps.
signup command
Command signup is a small account form built on package form: text, secret, select and checkbox fields with validation, Tab and Shift+Tab to move between them, the arrow keys and Space to change a choice, Enter to submit (Esc quits).
Command signup is a small account form built on package form: text, secret, select and checkbox fields with validation, Tab and Shift+Tab to move between them, the arrow keys and Space to change a choice, Enter to submit (Esc quits).
splashscreen command
Command splashscreen follows the same composition-only shape as examples/welcomescreen and examples/loginflow: widgets.BigText for the logo and widgets.Header for a tagline, dismissed by any keypress.
Command splashscreen follows the same composition-only shape as examples/welcomescreen and examples/loginflow: widgets.BigText for the logo and widgets.Header for a tagline, dismissed by any keypress.
table command
Command table is a dedicated, standalone example for datatable.Model's interactive row selection: Up/Down move the cursor and Enter confirms the row under it via SelectedMsg.
Command table is a dedicated, standalone example for datatable.Model's interactive row selection: Up/Down move the cursor and Enter confirms the row under it via SelectedMsg.
welcomescreen command
Command welcomescreen is the canonical reference for a static informational splash composed entirely from the existing widget catalog — widgets.Panel, widgets.BigText, widgets.Header and widgets.KeyValue side by side in a layout.Row.
Command welcomescreen is the canonical reference for a static informational splash composed entirely from the existing widget catalog — widgets.Panel, widgets.BigText, widgets.Header and widgets.KeyValue side by side in a layout.Row.
Package faces is a catalog of 50 animated characters drawn in Braille dots, and a widget that plays them.
Package faces is a catalog of 50 animated characters drawn in Braille dots, and a widget that plays them.
Package filepicker is a real filesystem browse-and-select widget — InkUI has no direct equivalent, but it follows the single-choice-list shape of picker.Model, browsing one directory at a time.
Package filepicker is a real filesystem browse-and-select widget — InkUI has no direct equivalent, but it follows the single-choice-list shape of picker.Model, browsing one directory at a time.
Package focus is a small focus-traversal helper: a Ring tracks which of n items has focus and moves it with Tab and Shift+Tab, wrapping at the ends and skipping disabled items.
Package focus is a small focus-traversal helper: a Ring tracks which of n items has focus and moves it with Tab and Shift+Tab, wrapping at the ends and skipping disabled items.
Package form is a validating form: a column of single-line fields, each with a label and optional validators, and a Submit that checks them all.
Package form is a validating form: a column of single-line fields, each with a label and optional validators, and a Submit that checks them all.
Package helpscreen is a full-screen key-binding help overlay: a bordered box listing widgets.Hint entries one per line via widgets.KeyHint, unlike widgets.KeyHints which joins them into a single line.
Package helpscreen is a full-screen key-binding help overlay: a bordered box listing widgets.Hint entries one per line via widgets.KeyHint, unlike widgets.KeyHints which joins them into a single line.
Package hittest does mouse hit-testing: which region of the screen did a click land on? A Rect is a rectangle of terminal cells in the same 0-indexed coordinates as input.MouseEvent (column X, row Y, origin top-left), and a Map is an ordered set of named Rects that answers "what is at this cell".
Package hittest does mouse hit-testing: which region of the screen did a click land on? A Rect is a rectangle of terminal cells in the same 0-indexed coordinates as input.MouseEvent (column X, row Y, origin top-left), and a Map is an ordered set of named Rects that answers "what is at this cell".
Package imageview draws a PNG with the kitty graphics protocol when the terminal supports it, with Sixel when it supports only that, and a bordered text placeholder otherwise.
Package imageview draws a PNG with the kitty graphics protocol when the terminal supports it, with Sixel when it supports only that, and a bordered text placeholder otherwise.
Package input decodes raw terminal bytes into structured key events, including multi-byte ANSI escape sequences (arrows, function keys, etc.) and UTF-8 runes, without any external terminfo/termcap dependency.
Package input decodes raw terminal bytes into structured key events, including multi-byte ANSI escape sequences (arrows, function keys, etc.) and UTF-8 runes, without any external terminfo/termcap dependency.
internal
a11y
Package a11y holds small helpers shared by the widgets' Linearize methods.
Package a11y holds small helpers shared by the widgets' Linearize methods.
announce
Package announce is the state behind a Program's announcements: the on-screen region that shows recent ones for a few seconds, the de-duplicating queue that feeds the accessible-mode transcript, and the sanitising and OSC 99 notification encoding both share.
Package announce is the state behind a Program's announcements: the on-screen region that shows recent ones for a few seconds, the de-duplicating queue that feeds the accessible-mode transcript, and the sanitising and OSC 99 notification encoding both share.
archtest
Package archtest holds the module's import-direction rule: imports go down the tiers below, never up.
Package archtest holds the module's import-direction rule: imports go down the tiers below, never up.
basetypes
Package basetypes holds the small value types that theme and layout share (the Border), so theme can stay a dependency leaf that does not import layout.
Package basetypes holds the small value types that theme and layout share (the Border), so theme can stay a dependency leaf that does not import layout.
bidi
Package bidi implements the Unicode Bidirectional Algorithm (UAX #9) for one line of text: it resolves the embedding level of every character and returns the visual order, for display only.
Package bidi implements the Unicode Bidirectional Algorithm (UAX #9) for one line of text: it resolves the embedding level of every character and returns the visual order, for display only.
braille
Package braille is the dot-cell drawing shared by the arc and line charts (widgets/chart) and the progress circle (widgets): the 2x4 braille dot encoding, its ASCII shade fallback, and the annulus fill behind Gauge and ProgressCircle.
Package braille is the dot-cell drawing shared by the arc and line charts (widgets/chart) and the progress circle (widgets): the 2x4 braille dot encoding, its ASCII shade fallback, and the annulus fill behind Gauge and ProgressCircle.
cancelreader
Package cancelreader is an io.Reader over an *os.File whose blocked Read can be cancelled from another goroutine.
Package cancelreader is an io.Reader over an *os.File whose blocked Read can be cancelled from another goroutine.
capprobe
Package capprobe holds the terminal capability probe's wire format: the queries a Program writes at start-up and the parser that turns the replies into a Result.
Package capprobe holds the terminal capability probe's wire format: the queries a Program writes at start-up and the parser that turns the replies into a Result.
cellcheck
Package cellcheck runs a model headless under the cell renderer and reports the frames that fell back to the line renderer, for the example corpus tests (spec S11: the cell renderer's fallback rate on the examples is zero).
Package cellcheck runs a model headless under the cell renderer and reports the frames that fell back to the line renderer, for the example corpus tests (spec S11: the cell renderer's fallback rate on the examples is zero).
edit
Package edit is the grapheme- and width-aware text-editing core shared by the input widgets.
Package edit is the grapheme- and width-aware text-editing core shared by the input widgets.
fsutil
Package fsutil is a shared filesystem-listing helper for widgets that browse directories (filepicker, treeview's DirectoryTree builder), so neither duplicates os.ReadDir traversal nor depends on the other.
Package fsutil is a shared filesystem-listing helper for widgets that browse directories (filepicker, treeview's DirectoryTree builder), so neither duplicates os.ReadDir traversal nor depends on the other.
highlight
Package highlight splits source code into classed spans for a set of languages (Go, JSON, shell, YAML, Python, JavaScript, TypeScript, Rust, C, C++, Java, C#, Kotlin, Swift, PHP, Ruby, Lua, HTML, XML, CSS, diff, Markdown, SQL, TOML, INI, Makefile, Dockerfile).
Package highlight splits source code into classed spans for a set of languages (Go, JSON, shell, YAML, Python, JavaScript, TypeScript, Rust, C, C++, Java, C#, Kotlin, Swift, PHP, Ruby, Lua, HTML, XML, CSS, diff, Markdown, SQL, TOML, INI, Makefile, Dockerfile).
isolation
Package isolation holds the module-wide copy-isolation sweep: for every stateful widget Model it snapshots a value copy, drives Update on the original, and asserts the snapshot did not change.
Package isolation holds the module-wide copy-isolation sweep: for every stateful widget Model it snapshots a value copy, drives Update on the original, and asserts the snapshot did not change.
ptytest
Package ptytest opens pseudo-terminals for tests, using /dev/ptmx and the pty ioctls directly (no dependencies).
Package ptytest opens pseudo-terminals for tests, using /dev/ptmx and the pty ioctls directly (no dependencies).
render
Package render is the cell-diffing renderer behind the root package's default renderer.
Package render is the cell-diffing renderer behind the root package's default renderer.
termio
Package termio holds the implementations of tui.Terminal, the runtime's port to the terminal (size query, raw mode, output virtual-terminal processing): OSTerminal for a real terminal and Fake for tests.
Package termio holds the implementations of tui.Terminal, the runtime's port to the terminal (size query, raw mode, output virtual-terminal processing): OSTerminal for a real terminal and Fake for tests.
testutil
Package testutil holds test helpers shared by the module's packages.
Package testutil holds test helpers shared by the module's packages.
tools/apilist command
Command apilist prints the exported API of every public package in the module, one declaration per entry, so a change to the public surface shows up as a diff.
Command apilist prints the exported API of every public package in the module, one declaration per entry, so a change to the public surface shows up as a diff.
tools/benchgate command
Command benchgate compares two `go test -bench -benchmem` outputs and fails when allocations or bytes per operation grew past a threshold.
Command benchgate compares two `go test -bench -benchmem` outputs and fails when allocations or bytes per operation grew past a threshold.
tools/covercheck command
Command covercheck reads a Go cover profile and fails when any library package has less than 90% statement coverage.
Command covercheck reads a Go cover profile and fails when any library package has less than 90% statement coverage.
tools/doccheck command
Command doccheck lists exported declarations that have no doc comment and exits non-zero if there are any.
Command doccheck lists exported declarations that have no doc comment and exits non-zero if there are any.
tools/genbidi command
Command genbidi generates internal/bidi/tables.go, the Unicode Bidi_Class, bracket and mirroring tables used by the UAX #9 reordering, from a pinned version of the Unicode Character Database.
Command genbidi generates internal/bidi/tables.go, the Unicode Bidi_Class, bracket and mirroring tables used by the UAX #9 reordering, from a pinned version of the Unicode Character Database.
tools/gengrapheme command
Command gengrapheme generates ansi/graphemebreak_tables.go, the extended grapheme cluster break data (UAX #29), from a pinned version of the Unicode Character Database.
Command gengrapheme generates ansi/graphemebreak_tables.go, the extended grapheme cluster break data (UAX #29), from a pinned version of the Unicode Character Database.
tools/genwidth command
Command genwidth generates ansi/runewidth_tables.go, the terminal-column width tables, from a pinned version of the Unicode Character Database.
Command genwidth generates ansi/runewidth_tables.go, the terminal-column width tables, from a pinned version of the Unicode Character Database.
vtscreen
Package vtscreen is a small virtual terminal: it applies the escape sequences a Program writes to a grid of cells, so a test can assert on what a user would see.
Package vtscreen is a small virtual terminal: it applies the escape sequences a Program writes to a grid of cells, so a test can assert on what a user would see.
Package keymap is a registry of key bindings that an app fills once and that help widgets (helpscreen, widgets.KeyHints) read, so the help text cannot drift from a second hand-written list.
Package keymap is a registry of key bindings that an app fills once and that help widgets (helpscreen, widgets.KeyHints) read, so the help text cannot drift from a second hand-written list.
Package layout provides simple box-model composition on top of the ansi package: padding, borders, and side-by-side or stacked joins.
Package layout provides simple box-model composition on top of the ansi package: padding, borders, and side-by-side or stacked joins.
Package loadingbar is an indeterminate progress animation — InkUI's "LoadingBar": a segment sliding back and forth across a fixed-width track, distinct from widgets.ProgressBar's determinate fill.
Package loadingbar is an indeterminate progress animation — InkUI's "LoadingBar": a segment sliding back and forth across a fixed-width track, distinct from widgets.ProgressBar's determinate fill.
Package logview is an append-only scrolling log view.
Package logview is an append-only scrolling log view.
Package markdown renders a subset of CommonMark as styled, width-aware terminal text, written from scratch with no dependencies.
Package markdown renders a subset of CommonMark as styled, width-aware terminal text, written from scratch with no dependencies.
Package maskedinput is a thin wrapper over textinput.Model that masks every rendered character of a non-empty value with a configurable rune.
Package maskedinput is a thin wrapper over textinput.Model that masks every rendered character of a non-empty value with a configurable rune.
Package menu is a nested-navigation list widget composed on top of picker.Model.
Package menu is a nested-navigation list widget composed on top of picker.Model.
Package menubar is a horizontal menu bar of titled menus, each opening a dropdown of items below its title.
Package menubar is a horizontal menu bar of titled menus, each opening a dropdown of items below its title.
Package motion provides a small, caller-side reduced-motion preference.
Package motion provides a small, caller-side reduced-motion preference.
Package multiselect is a multi-choice list widget — InkUI's "MultiSelect": a cursor moves between items, Space toggles the item under it, and Enter confirms the whole set of checked items.
Package multiselect is a multi-choice list widget — InkUI's "MultiSelect": a cursor moves between items, Space toggles the item under it, and Enter confirms the whole set of checked items.
Package notificationcenter is a persistent multi-item panel over a bounded notification queue — distinct from toast.Model's transient, one-at-a-time popup, this composites ALL currently-queued notifications simultaneously in a single panel, colored via the same widgets.Variant-to-color convention toast and widgets.Alert already use.
Package notificationcenter is a persistent multi-item panel over a bounded notification queue — distinct from toast.Model's transient, one-at-a-time popup, this composites ALL currently-queued notifications simultaneously in a single panel, colored via the same widgets.Variant-to-color convention toast and widgets.Alert already use.
Package numberinput is a thin wrapper over textinput.Model that restricts typed input to digits and a single leading '-'.
Package numberinput is a thin wrapper over textinput.Model that restricts typed input to digits and a single leading '-'.
Package passwordinput is a thin wrapper over textinput.Model that masks every rendered character of a non-empty value.
Package passwordinput is a thin wrapper over textinput.Model that masks every rendered character of a non-empty value.
Package picker is a single-choice list widget — InkUI's "Select", renamed because 'select' is a Go keyword and can't be a package name.
Package picker is a single-choice list widget — InkUI's "Select", renamed because 'select' is a Go keyword and can't be a package name.
Package popover is a click/key-triggered overlay holding arbitrary (possibly multi-line) content, anchored near a given point rather than centered over the whole base — unlike dialog.Model, which always centers.
Package popover is a click/key-triggered overlay holding arbitrary (possibly multi-line) content, anchored near a given point rather than centered over the whole base — unlike dialog.Model, which always centers.
Package scrollbar is a scrollbar widget: a track with a thumb whose size and position show how much of some content is visible and where.
Package scrollbar is a scrollbar widget: a track with a thumb whose size and position show how much of some content is visible and where.
Package skeleton is a loading placeholder block — InkUI's "Skeleton".
Package skeleton is a loading placeholder block — InkUI's "Skeleton".
Package spinner is an animated loading indicator — InkUI's "Spinner".
Package spinner is an animated loading indicator — InkUI's "Spinner".
Package splitpane is a two-pane split with a draggable divider: two layout.Nodes side by side (Columns) or stacked (Rows), separated by a one-cell divider that the user moves with the keyboard or the mouse.
Package splitpane is a two-pane split with a draggable divider: two layout.Nodes side by side (Columns) or stacked (Rows), separated by a one-cell divider that the user moves with the keyboard or the mouse.
Package streamtext reveals text progressively, a few characters at a time — InkUI's "StreamingText" and "Typewriter", which are one Model with different settings (New for fast streaming, NewTypewriter for pauses and a blinking cursor).
Package streamtext reveals text progressively, a few characters at a time — InkUI's "StreamingText" and "Typewriter", which are one Model with different settings (New for fast streaming, NewTypewriter for pauses and a blinking cursor).
Package tabs is a horizontal tab bar — InkUI's "Tabs".
Package tabs is a horizontal tab bar — InkUI's "Tabs".
Package taginput is a small composite widget for entering a list of short tags: a textinput.Model for typing plus a []string of committed tags, rendered as widgets.Tag chips followed by the live input field.
Package taginput is a small composite widget for entering a list of short tags: a textinput.Model for typing plus a []string of committed tags, rendered as widgets.Tag chips followed by the live input field.
Package term puts the controlling terminal into raw mode and reads its size.
Package term puts the controlling terminal into raw mode and reads its size.
Package textarea is a multi-line text input widget built on top of the root tui package.
Package textarea is a multi-line text input widget built on top of the root tui package.
Package textinput is a single-line text input widget built on top of the root tui package — the first package in this module that depends on tui rather than the other way around, forming a widget layer above the core (term/ansi/input) -> tui layering described in the README.
Package textinput is a single-line text input widget built on top of the root tui package — the first package in this module that depends on tui rather than the other way around, forming a widget layer above the core (term/ansi/input) -> tui layering described in the README.
Package theme is a Go translation of InkUI's theming model (https://inkui-lib.vercel.app/docs/getting-started/theming): a small struct of semantic colors plus a border style, with Dark (the default) and Light built-in values.
Package theme is a Go translation of InkUI's theming model (https://inkui-lib.vercel.app/docs/getting-started/theming): a small struct of semantic colors plus a border style, with Dark (the default) and Light built-in values.
Package toast is a transient auto-dismissing notification — InkUI's "Toast": a small bordered box in a screen corner, composited via layout.Overlay the same way dialog is, that closes itself after Duration via motion.After rather than waiting for a keypress.
Package toast is a transient auto-dismissing notification — InkUI's "Toast": a small bordered box in a screen corner, composited via layout.Overlay the same way dialog is, that closes itself after Duration via motion.After rather than waiting for a keypress.
Package toolapproval is a gate-before-execution prompt for an agent/CLI tool call — InkUI's "ToolApproval".
Package toolapproval is a gate-before-execution prompt for an agent/CLI tool call — InkUI's "ToolApproval".
Package treeview is a hierarchical expandable tree — InkUI's "TreeView" (file-browser-style navigation).
Package treeview is a hierarchical expandable tree — InkUI's "TreeView" (file-browser-style navigation).
Package tuitest is a headless harness for testing tui models.
Package tuitest is a headless harness for testing tui models.
Package viewport is a scrollable window onto content taller than it, built on top of the root tui package the same way textinput is.
Package viewport is a scrollable window onto content taller than it, built on top of the root tui package the same way textinput is.
Package virtuallist renders a scrollable window onto a large, uniform-height list without ever materializing off-screen rows.
Package virtuallist renders a scrollable window onto a large, uniform-height list without ever materializing off-screen rows.
Package widgets holds small, stateless, presentational render helpers — Divider, Badge, StatusIndicator, KeyHint, ProgressBar, CodeBlock, DiffView, TokenCounter — bundled into one package rather than one apiece since each is a handful of lines with no internal state.
Package widgets holds small, stateless, presentational render helpers — Divider, Badge, StatusIndicator, KeyHint, ProgressBar, CodeBlock, DiffView, TokenCounter — bundled into one package rather than one apiece since each is a handful of lines with no internal state.
chart
Package chart renders data as text: Sparkline, BarChart, LineChart, HeatMap and Gauge.
Package chart renders data as text: Sparkline, BarChart, LineChart, HeatMap and Gauge.
Package wizard provides a step-navigation state machine for multi-step flows: Next/Back advance or retreat the current step (clamped at the bounds), and View renders the step indicator via widgets.Stepper.
Package wizard provides a step-navigation state machine for multi-step flows: Next/Back advance or retreat the current step (clamped at the bounds), and View renders the step indicator via widgets.Stepper.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL