Documentation
¶
Overview ¶
Package tuigo is a declarative, component-based terminal UI framework.
Index ¶
- Constants
- Variables
- func Clamp01(t float64) float64
- func EaseInCubic(t float64) float64
- func EaseOutCubic(t float64) float64
- func Global() func(Option) Option
- func Lerp(a, b, t float64) float64
- func Render(component Component) (err error)
- func RenderInline(component Component) (err error)
- func SetTheme(t ThemeColors)
- func UseReducer[S, A any](ctx *Ctx, reducer func(S, A) S, initial S) (S, func(A))
- func UseState[T any](ctx *Ctx, initial T) (T, func(T))
- type BorderStyle
- type Color
- type Component
- type Ctx
- func (c *Ctx) After(d time.Duration, fn func()) func()
- func (c *Ctx) Exit()
- func (c *Ctx) Focus(key string)
- func (c *Ctx) FocusNext()
- func (c *Ctx) FocusPrev()
- func (c *Ctx) IsFocused(key string) bool
- func (c *Ctx) OnCleanup(fn func())
- func (c *Ctx) Overlay(el Element, x, y int)
- func (c *Ctx) Size() (width, height int)
- func (c *Ctx) Wake()
- type Element
- func Badge(text string, bg Color) Element
- func Box(opts ...Option) Element
- func Clock(layout string) Element
- func Dialog(viewportW, viewportH, dialogW, dialogH int, title string, bodyOpts ...Option) Element
- func Divider(width int) Element
- func Fragment(children ...Element) Element
- func Menu(items []MenuItem, selected int, onSelect func(index int)) Element
- func Panel(title string, opts ...Option) Element
- func ProgressBar(fraction float64, width int) Element
- func Spinner(frame int) Element
- func TerminalPane(ctx *Ctx, argv []string, opts ...Option) Element
- func Text(format string, args ...any) Element
- func With(e Element, opts ...Option) Element
- type FlexDirection
- type Key
- type MenuItem
- type MouseButton
- type MouseEvent
- type Option
- func Bold() Option
- func Border(b BorderStyle) Option
- func Children(children ...Element) Option
- func ColorBg(c Color) Option
- func ColorFg(c Color) Option
- func FlexColumn() Option
- func FlexRow() Option
- func Focusable() Option
- func Gap(n int) Option
- func GrabInput() Option
- func Height(n int) Option
- func Italic() Option
- func Margin(n int) Option
- func MarginBottom(n int) Option
- func MarginLeft(n int) Option
- func MarginRight(n int) Option
- func MarginTop(n int) Option
- func MinHeight(n int) Option
- func MinWidth(n int) Option
- func OnAnyKey(handler func(Key)) Option
- func OnClick(handler func(MouseEvent)) Option
- func OnHotkey(k SpecialKey, handler func()) Option
- func OnKey(r rune, handler func()) Option
- func OnMouse(handler func(MouseEvent)) Option
- func OnPaneExit(fn func(error)) Option
- func OnScroll(handler func(delta int)) Option
- func OnSpecialKey(k SpecialKey, handler func()) Option
- func Padding(n int) Option
- func PaddingBottom(n int) Option
- func PaddingLeft(n int) Option
- func PaddingRight(n int) Option
- func PaddingTop(n int) Option
- func Truncate() Option
- func Underline() Option
- func Width(n int) Option
- func WithKey(key string) Option
- type Overlay
- type Screen
- func (s *Screen) CellAt(x, y int) (r rune, fg, bg Color, bold, italic, underline bool)
- func (s *Screen) Cursor() (x, y int, visible bool)
- func (s *Screen) MouseMode() (enabled bool, sgr bool)
- func (s *Screen) PageDepth() int
- func (s *Screen) PopPage(w io.Writer) error
- func (s *Screen) PushPage()
- func (s *Screen) Resize(rows, cols int)
- func (s *Screen) Restore(w io.Writer) error
- func (s *Screen) Size() (rows, cols int)
- func (s *Screen) Snapshot() *ScreenSnapshot
- func (s *Screen) Write(p []byte) (int, error)
- type ScreenSnapshot
- type SpecialKey
- type ThemeColors
Constants ¶
const ( KeyNone = types.KeyNone KeyEnter = types.KeyEnter KeyEsc = types.KeyEsc KeyTab = types.KeyTab KeyBackspace = types.KeyBackspace KeyDelete = types.KeyDelete KeyUp = types.KeyUp KeyDown = types.KeyDown KeyLeft = types.KeyLeft KeyRight = types.KeyRight KeyHome = types.KeyHome KeyEnd = types.KeyEnd KeyCtrlC = types.KeyCtrlC KeyBackTab = types.KeyBackTab KeyCtrlO = types.KeyCtrlO KeyCtrlSpace = types.KeyCtrlSpace KeyCtrlG = types.KeyCtrlG KeyCtrlBackslash = types.KeyCtrlBackslash KeyCtrlRBracket = types.KeyCtrlRBracket KeyCtrlCaret = types.KeyCtrlCaret KeyCtrlUnderscore = types.KeyCtrlUnderscore )
const ( MouseLeft = types.MouseLeft MouseMiddle = types.MouseMiddle MouseRight = types.MouseRight MouseWheelUp = types.MouseWheelUp MouseWheelDown = types.MouseWheelDown )
const ( BorderNone = types.BorderNone BorderSingle = types.BorderSingle BorderDouble = types.BorderDouble BorderRounded = types.BorderRounded )
Variables ¶
var DarkTheme = ThemeColors{ Background: RGB(16, 17, 22), Surface: RGB(28, 29, 36), SurfaceAlt: RGB(42, 44, 54), TextPrimary: ColorBrightWhite, TextMuted: ColorGray, Accent: RGB(0, 180, 216), AccentAlt: RGB(0, 216, 180), Success: ColorGreen, Warning: ColorYellow, Error: ColorRed, }
var FocusCycleKey = types.Key{Special: types.KeyCtrlO}
FocusCycleKey is the global chord that cycles focus between focusable elements, regardless of what is focused (so you can always leave a grab-all TerminalPane). Default: Ctrl-O. Override it before Render, e.g.
tuigo.FocusCycleKey = tuigo.Key{Special: tuigo.KeyBackTab}
Pick something the embedded child doesn't rely on.
var LightTheme = ThemeColors{ Background: RGB(245, 245, 245), Surface: RGB(255, 255, 255), SurfaceAlt: RGB(225, 225, 225), TextPrimary: RGB(20, 20, 20), TextMuted: RGB(90, 90, 90), Accent: RGB(0, 110, 170), AccentAlt: RGB(0, 140, 120), Success: RGB(20, 130, 60), Warning: RGB(160, 110, 0), Error: RGB(180, 30, 30), }
var NamedThemes = map[string]ThemeColors{ "dark": DarkTheme, "light": LightTheme, }
NamedThemes indexes the built-in themes for cycling/lookup (e.g. a "/theme" command).
var RGB = types.RGB
var Theme = DarkTheme
Theme is the active palette. It starts as DarkTheme; call SetTheme to switch it — every subsequent render that reads Theme.* picks up the change immediately, since it's read fresh each render, not cached.
Functions ¶
func Clamp01 ¶ added in v0.1.8
Clamp01 clamps t to [0, 1] — animation progress is almost always a t := elapsed/duration computation that can overshoot past 1 once the animation has finished but before the caller notices and stops ticking.
func EaseInCubic ¶ added in v0.1.8
EaseInCubic is the mirror of EaseOutCubic: slow start, fast finish.
func EaseOutCubic ¶ added in v0.1.8
EaseOutCubic is a common UI motion curve: fast start, slow finish. Pass a Clamp01'd t in [0, 1].
func Lerp ¶ added in v0.1.8
Lerp linearly interpolates between a and b by t. t is not clamped — pass Clamp01(t) yourself if you need that, since some callers deliberately extrapolate (e.g. an overshoot bounce).
func RenderInline ¶ added in v0.1.13
RenderInline is Render on the MAIN screen — it never takes over the alternate screen buffer (see renderer.NewInlineTerminal). Use it when the host already owns an alt screen that must survive the render, e.g. a PTY wrapper showing a modal over a full-screen child: the wrapper switches away from the child's alt buffer, calls RenderInline, then switches back and the terminal restores the child's screen untouched. Same component/event model as Render; still raw mode + mouse + its own stdin for the render's duration.
func UseReducer ¶
Types ¶
type BorderStyle ¶
type BorderStyle = types.BorderStyle
Re-exported so callers only ever import the tuigo package for TUIML.
type Color ¶
Re-exported so callers only ever import the tuigo package for TUIML.
const ( ColorBlack Color = 16 ColorRed Color = 1 ColorGreen Color = 2 ColorYellow Color = 3 ColorBlue Color = 4 ColorMagenta Color = 5 ColorCyan Color = 6 ColorWhite Color = 7 ColorGray Color = 8 ColorBrightRed Color = 9 ColorBrightGreen Color = 10 ColorBrightYellow Color = 11 ColorBrightBlue Color = 12 ColorBrightMagenta Color = 13 ColorBrightCyan Color = 14 ColorBrightWhite Color = 15 )
Named xterm-256 colors. Color's zero value means "unset/inherit" (see types.Color), so plain black lives at the extended-palette index 16 rather than the ambiguous 0.
type Ctx ¶
type Ctx struct {
// contains filtered or unexported fields
}
func (*Ctx) IsFocused ¶ added in v0.1.15
IsFocused reports whether the element identified by key currently holds focus. It matches the app's focus path exactly or by trailing segment, so a pane keyed "term" is focused whether its resolved path is "term" or "panes/term". Used by a Canvas painter to decide whether to draw the child's cursor.
func (*Ctx) OnCleanup ¶ added in v0.1.15
func (c *Ctx) OnCleanup(fn func())
OnCleanup registers fn to run when this component instance is unmounted (removed from the tree on a later render). It piggybacks the same per-instance teardown list Ctx.After cancels use, so a long-lived resource — e.g. a TerminalPane's child process and PTY — is released when the pane leaves the tree. Call it once per mount (guard with UseState), not every render.
func (*Ctx) Overlay ¶
Overlay registers ov to be drawn on top of the main frame for this render. Call it from within Component — like UseState, call it unconditionally on every render you want the overlay visible; omit the call on renders where it should disappear.
func (*Ctx) Wake ¶ added in v0.1.15
func (c *Ctx) Wake()
Wake asks the render loop to run one more frame as soon as it can. It is the redraw trigger a BACKGROUND goroutine uses after mutating state the UI reads (e.g. a TerminalPane's reader goroutine after writing child output into its Screen): the loop selects on the same timers channel Ctx.After feeds, so a wake is just an empty job pushed onto it. Safe to call from any goroutine; non-blocking (a wake is dropped only when one is already queued, which is fine — the queued frame will observe the latest state anyway).
type Element ¶
Element is the tree node produced by Box, Text, and Fragment.
func Clock ¶
Clock renders the current wall-clock time formatted per the time package layout convention (e.g. "15:04:05"). Render's main loop ticks once a second specifically so a Clock element updates on its own without any user input driving a re-render.
func Dialog ¶
Dialog centers a titled Panel of exactly dialogW x dialogH within a viewportW x viewportH screen, using flex spacers on every side. It's a full-viewport takeover, not a true floating overlay — tuigo has no z-order/absolute-positioning compositing yet (see TODO.md), so a Dialog is meant to fully replace the tree it's shown in while open, not draw on top of other content.
func Divider ¶
Divider draws a horizontal rule width cells wide, e.g. to separate sections inside a Panel without a full nested border.
func Menu ¶
Menu renders a selectable list — a dropdown/popup style widget for things like a "/" slash-command palette in a chat input. It occupies its own row(s) in normal flow layout (tuigo has no floating overlays yet), so the idiomatic use is to conditionally insert it as a sibling just above/below the input it's triggered from, pushing other content rather than floating over it. Menu renders items with items[selected] highlighted. onSelect, if non-nil, fires with a row's index when it's clicked — pass nil for a keyboard-only menu.
func Panel ¶
Panel is a titled, bordered container in the spirit of a Turbo Vision / Borland-era window frame: a full-width colored title bar rather than a plain text line, so the title reads as a window's chrome instead of just another content row. The bar uses Theme.Accent — a Box, not a bare Text, since a standalone Text's ColorBg only paints behind its own characters (see renderer/draw.go's drawText), not the full row; a Box's fillBackground paints its entire rect. Pass Children(...) and any other Box options (Width, Height, Padding, ...) as opts; Border defaults to BorderRounded and FlexColumn if not overridden by a later option.
func ProgressBar ¶
ProgressBar renders a fraction in [0,1] as a two-tone bar of the given width plus a percentage label. Zero-width segments are omitted rather than passed as Width(0), since Style's zero value means "unset" (see types.Style) and would otherwise be treated as a flex child instead of literally zero-width.
func Spinner ¶
Spinner returns one frame of a braille spinner glyph. It's pure — the caller drives animation by incrementing frame (e.g. once per received key, or once per tick once TODO.md item 3's timers land) and re-rendering.
func TerminalPane ¶ added in v0.1.15
TerminalPane spawns argv[0] (with argv[1:] as arguments) in a PTY and returns a Canvas element that composites the child's live screen into the tuigo layout. It must be called from inside a component (it uses ctx hooks). Give it a stable WithKey so focus (and thus input routing) targets it; it is Focusable by default. Standard layout options (Width, Height, flex) apply as they do to a Box; OnPaneExit surfaces the child's exit.
tuigo.TerminalPane(ctx, []string{"bash"}, tuigo.WithKey("term"))
type FlexDirection ¶
type FlexDirection = types.FlexDirection
Re-exported so callers only ever import the tuigo package for TUIML.
type MenuItem ¶
type MenuItem struct {
Label string
Hint string // shown dim/right-aligned-ish after Label, e.g. a shortcut or description
}
MenuItem is one row in a Menu.
type MouseButton ¶
type MouseButton = types.MouseButton
MouseButton identifies which button (or wheel direction) a MouseEvent reports.
type MouseEvent ¶
type MouseEvent = types.MouseEvent
MouseEvent is a single mouse report: a button/wheel action at a 0-indexed cell coordinate, delivered by OnClick/OnMouse/OnScroll.
type Option ¶
type Option func(*Element)
Option mutates an Element during construction. Options apply in the order they're passed and never mutate a shared Element.
func FlexRow ¶
func FlexRow() Option
FlexRow lays out children left-to-right. It's the zero-value direction, so this option exists mainly for explicitness.
func GrabInput ¶ added in v0.1.16
func GrabInput() Option
GrabInput marks an element as focusable AND a grab-all key sink: while it holds focus, Tab and Shift-Tab are delivered to it (via its key handlers) instead of cycling focus, so an embedded full-screen child gets those keys. Focus is still switchable with the global focus chord (FocusCycleKey, default Ctrl-O). TerminalPane sets this by default.
func MarginBottom ¶
func MarginLeft ¶
func MarginRight ¶
func OnClick ¶
func OnClick(handler func(MouseEvent)) Option
OnClick calls handler when the left mouse button is pressed anywhere within this Element's rendered rect. The dispatcher hit-tests using the last computed layout, so it works for Box and Text alike.
func OnHotkey ¶ added in v0.1.19
func OnHotkey(k SpecialKey, handler func()) Option
OnHotkey registers a GLOBAL key binding: handler fires when k is pressed anywhere in the app, regardless of which element holds focus (sugar over Global()(OnSpecialKey(...))). Attach it to any element — conventionally the root.
func OnMouse ¶
func OnMouse(handler func(MouseEvent)) Option
OnMouse calls handler for every mouse event that hits this Element's rendered rect, regardless of button.
func OnPaneExit ¶ added in v0.1.15
OnPaneExit registers a callback fired once when a TerminalPane's child process exits (or fails to start). The error is the child's exit error, or nil for a clean exit. A parent typically reacts by exiting the app or replacing the pane.
func OnScroll ¶
OnScroll calls handler with -1 for wheel-up or +1 for wheel-down when the scroll wheel is used over this Element's rendered rect.
func OnSpecialKey ¶
func OnSpecialKey(k SpecialKey, handler func()) Option
func PaddingBottom ¶
func PaddingLeft ¶
func PaddingRight ¶
func PaddingTop ¶
type Overlay ¶
Overlay is one floating panel — a Picture-in-Picture widget, a toast, a popup — composited on top of the main frame at a fixed screen position. Element must carry explicit Width/Height (e.g. via the Width/Height options) since it's laid out and drawn independently of the main tree, with no parent to size it.
tuigo has no z-order/absolute-positioning support in the layout tree itself (see ARCHITECTURE.md) — Overlay is the escape hatch: it renders its own small Element tree into its own Buffer, then stamps that buffer onto the main frame after the main Draw call and before the cell diff, so only the overlay's actually-changed cells get re-flushed like anything else. It does not participate in flow layout, hit-testing, or focus; call OnClick/OnScroll on its own Element if it needs input.
type Screen ¶ added in v0.1.14
type Screen struct {
// contains filtered or unexported fields
}
Screen is a cell-grid terminal emulator (backed by vt10x) plus a page stack. It ingests a terminal output byte stream via Write (io.Writer) and can save the visible screen onto a page stack for later restore. It is not safe for concurrent use; the host must serialise Write against the page/snapshot calls.
func NewScreen ¶ added in v0.1.14
NewScreen creates a rows×cols emulator with a blank screen and an empty page stack.
func (*Screen) CellAt ¶ added in v0.1.15
CellAt returns the painted contents of one cell: its rune, resolved foreground/background as tuigo Colors, and the SGR attributes tuigo can repaint. Palette index 0 and vt10x defaults both map to Color 0 ("unset"), so they cascade to the surrounding style rather than forcing black.
func (*Screen) Cursor ¶ added in v0.1.15
Cursor reports the child cursor's position (col x, row y) and whether it is visible. A TerminalPane draws a block cursor there when the pane is focused.
func (*Screen) MouseMode ¶ added in v0.1.18
MouseMode reports whether the emulated child has enabled ANY mouse reporting mode (X10/button/motion/many) and whether it additionally enabled the SGR (1006) coordinate extension. A TerminalPane consults this before forwarding host mouse events to the child's PTY: if the child never opted in, forwarding is a silent no-op (matches real terminal behavior).
func (*Screen) PageDepth ¶ added in v0.1.14
PageDepth reports how many pages are currently stacked (unclosed PushPage calls). Useful for asserting balanced push/pop.
func (*Screen) PopPage ¶ added in v0.1.14
PopPage repaints the top saved page to w and pops it off the stack. It returns an error if the stack is empty (nothing to restore) or the write fails. The saved page — not the current live screen — is what gets repainted, so a dialog drawn over the child leaves no trace.
func (*Screen) PushPage ¶ added in v0.1.14
func (s *Screen) PushPage()
PushPage saves the current visible screen onto the page stack. Pair it with PopPage to repaint that exact page after a dialog closes; nested dialogs push again and pop in reverse order.
func (*Screen) Restore ¶ added in v0.1.14
Restore is a convenience that snapshots the current screen and repaints it to w.
func (*Screen) Size ¶ added in v0.1.15
Size reports the emulated screen dimensions in rows and cols.
func (*Screen) Snapshot ¶ added in v0.1.14
func (s *Screen) Snapshot() *ScreenSnapshot
Snapshot copies the current visible screen — every cell, the cursor position, and cursor visibility — into a standalone page value that Restore can repaint later.
func (*Screen) Write ¶ added in v0.1.14
Write ingests a terminal output byte stream, updating the emulated screen. A multibyte UTF-8 rune split across two Writes (e.g. at a PTY read-buffer boundary) would otherwise be dropped by vt10x, so an incomplete trailing rune is held back and prepended to the next Write. All input bytes are accepted, so the reported count is always len(p).
type ScreenSnapshot ¶ added in v0.1.14
type ScreenSnapshot struct {
// contains filtered or unexported fields
}
ScreenSnapshot is an immutable copy of a Screen's visible viewport (cell grid + cursor), produced by Snapshot / PushPage and repainted by Restore / PopPage.
func (*ScreenSnapshot) Restore ¶ added in v0.1.14
func (snap *ScreenSnapshot) Restore(w io.Writer) error
Restore repaints the whole page to w: hide cursor, reset SGR, clear screen, then for every cell emit its rune with the right SGR (re-emitting a style only when it changes), and finally leave the cursor exactly where the snapshot had it. Feeding Restore's output into a fresh NewScreen of the same size reproduces an identical grid (round-trip).
type SpecialKey ¶
type SpecialKey = types.SpecialKey
type ThemeColors ¶
type ThemeColors struct {
// Surfaces
Background Color
Surface Color
SurfaceAlt Color
// Text
TextPrimary Color
TextMuted Color
// Accents
Accent Color
AccentAlt Color
// Semantic
Success Color
Warning Color
Error Color
}
ThemeColors is a small, intentional color palette for tuigo applications. Prefer theme colors over raw palette indices; use RGB only for one-off visual effects that don't belong in the theme.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
_examples
|
|
|
chat
command
Command chat is a mock two-sided chat UI demonstrating TUIML's state management (UseState), event handling (OnAnyKey/OnSpecialKey/OnScroll), and widget layer (Panel/Badge/Clock/Spinner/ProgressBar/Menu/Dialog) on top of the style/flex system.
|
Command chat is a mock two-sided chat UI demonstrating TUIML's state management (UseState), event handling (OnAnyKey/OnSpecialKey/OnScroll), and widget layer (Panel/Badge/Clock/Spinner/ProgressBar/Menu/Dialog) on top of the style/flex system. |
|
compositor
command
Command compositor is the proof-of-concept for embedding a DEMANDING full-screen child (think Claude Code) in a tuigo TerminalPane and overlaying a dialog on top of it without corrupting the pane.
|
Command compositor is the proof-of-concept for embedding a DEMANDING full-screen child (think Claude Code) in a tuigo TerminalPane and overlaying a dialog on top of it without corrupting the pane. |
|
static-counter
command
Command static-counter builds a small TUIML element tree and prints its structure.
|
Command static-counter builds a small TUIML element tree and prints its structure. |
|
style-showcase
command
Command style-showcase exercises every style option (colors, text attributes, border, flex row vs column) as a reference for TUIML's style system.
|
Command style-showcase exercises every style option (colors, text attributes, border, flex row vs column) as a reference for TUIML's style system. |
|
termpane
command
Command termpane is a minimal terminal COMPOSITOR built with tuigo: it embeds a live child shell (bash by default, or the program named on the command line) inside a tuigo TerminalPane and lays it out beside a status column — tmux/zellij-as-a-library in ~40 lines.
|
Command termpane is a minimal terminal COMPOSITOR built with tuigo: it embeds a live child shell (bash by default, or the program named on the command line) inside a tuigo TerminalPane and lays it out beside a status column — tmux/zellij-as-a-library in ~40 lines. |
|
Package asciiart decodes images (anything image.Decode supports — png/jpeg/gif via blank imports below) into terminal-friendly grids, either a grayscale character ramp or per-cell averaged RGB for truecolor block rendering.
|
Package asciiart decodes images (anything image.Decode supports — png/jpeg/gif via blank imports below) into terminal-friendly grids, either a grayscale character ramp or per-cell averaged RGB for truecolor block rendering. |
|
Package audio plays and records audio by shelling out to platform-native tools rather than linking a CGo audio backend — no C toolchain, no cross-compile headaches, consistent with tuigo's "boring, testable layers" philosophy (see the root STYLEGUIDE.md).
|
Package audio plays and records audio by shelling out to platform-native tools rather than linking a CGo audio backend — no C toolchain, no cross-compile headaches, consistent with tuigo's "boring, testable layers" philosophy (see the root STYLEGUIDE.md). |
|
Package layout computes pure Go flexbox geometry for an Element tree.
|
Package layout computes pure Go flexbox geometry for an Element tree. |
|
Package renderer turns a laid-out Element tree into a cell buffer, diffs it against the previous frame, and flushes only the changed cells to a terminal.
|
Package renderer turns a laid-out Element tree into a cell buffer, diffs it against the previous frame, and flushes only the changed cells to a terminal. |
|
Package types defines the declarative element tree shared by tuigo's layout, renderer, and reconciler packages.
|
Package types defines the declarative element tree shared by tuigo's layout, renderer, and reconciler packages. |
