tui

package
v0.0.24 Latest Latest
Warning

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

Go to latest
Published: Jan 7, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

README

tui

Declarative terminal UI framework with layout engine, rich components, and event-driven runtime. Build responsive terminal applications using composable views, automatic layout, and single-threaded event handling.

Usage Examples

Basic Application
package main

import (
	"log"

	"github.com/deepnoodle-ai/wonton/tui"
)

type app struct {
	count int
}

func (a *app) View() tui.View {
	return tui.Stack(
		tui.Text("Counter: %d", a.count).Bold(),
		tui.Text("Press + to increment, - to decrement, q to quit").Dim(),
	).Gap(1).Padding(2)
}

func (a *app) HandleEvent(ev tui.Event) []tui.Cmd {
	switch e := ev.(type) {
	case tui.KeyEvent:
		switch e.Rune {
		case '+':
			a.count++
		case '-':
			a.count--
		case 'q':
			return []tui.Cmd{tui.Quit()}
		}
	}
	return nil
}

func main() {
	if err := tui.Run(&app{}); err != nil {
		log.Fatal(err)
	}
}
List Selection
type listApp struct {
	items    []string
	selected int
}

func (a *listApp) View() tui.View {
	return tui.Stack(
		tui.Text("Select an item:").Bold(),
		tui.Table([]tui.TableColumn{{Header: "Items"}}, &a.selected).
			Rows(func() [][]string {
				var rows [][]string
				for _, item := range a.items {
					rows = append(rows, []string{item})
				}
				return rows
			}()).
			Height(10),
		tui.Text("Selected: %s", a.items[a.selected]),
	).Gap(1)
}

func (a *listApp) HandleEvent(ev tui.Event) []tui.Cmd {
	if ev, ok := ev.(tui.KeyEvent); ok && ev.Rune == 'q' {
		return []tui.Cmd{tui.Quit()}
	}
	return nil
}
Text Input Form
type formApp struct {
	name     string
	email    string
	password string
	focused  int // 0=name, 1=email, 2=password
}

func (a *formApp) View() tui.View {
	return tui.Stack(
		tui.Text("Registration Form").Bold(),
		tui.Divider(),

		tui.Text("Name:"),
		tui.InputField(&a.name).
			ID("name").
			OnSubmit(func(s string) { a.focused = 1 }),

		tui.Text("Email:"),
		tui.InputField(&a.email).
			ID("email").
			OnSubmit(func(s string) { a.focused = 2 }),

		tui.Text("Password:"),
		tui.PasswordInput(&a.password).
			ID("password").
			OnSubmit(func(s string) { /* submit form */ }),

		tui.Spacer(),
		tui.Group(
			tui.Button("Submit", func() { /* submit */ }),
			tui.Button("Cancel", func() { /* cancel */ }),
		).Gap(2),
	).Gap(1).Padding(2)
}
Prompt Choice (Claude Code Style)

A selection widget with numbered options where one option can accept inline text input. Similar to confirmation prompts in Claude Code.

type confirmApp struct {
	runner    *tui.InlineApp
	selected  int
	inputText string
	done      bool
}

func (app *confirmApp) LiveView() tui.View {
	if app.done {
		return tui.Text("Done!").Success()
	}

	return tui.Stack(
		tui.Text("Do you want to proceed?").Bold(),
		tui.Text(""),
		tui.PromptChoice(&app.selected, &app.inputText).
			Option("Yes").
			Option("Yes, and remember this choice").
			InputOption("Type custom instructions here...").
			OnSelect(func(idx int, text string) {
				switch idx {
				case 0:
					// "Yes" selected
				case 1:
					// "Yes, and remember" selected
				case 2:
					// Custom input provided in 'text'
					fmt.Println("Custom:", text)
				}
				app.done = true
			}).
			OnCancel(func() {
				app.done = true
			}),
	)
}

Keyboard Controls:

  • Arrow Up/Down: Navigate between options
  • 1-9: Jump directly to numbered option
  • Enter: Confirm selection
  • Escape: Cancel
  • Any character: When on input option, types into the field

Visual Output:

Do you want to proceed?

❯ 1. Yes
  2. Yes, and remember this choice
  3. Type custom instructions here...

Esc to cancel

Customization:

tui.PromptChoice(&selected, &input).
	Option("Option A").
	Option("Option B").
	InputOption("Custom...").
	CursorChar(">").           // Change cursor indicator
	ShowNumbers(false).        // Hide number prefixes
	HintText("Enter to confirm").  // Custom hint text
	Style(tui.NewStyle().WithForeground(tui.ColorCyan))
Table Display
type tableApp struct {
	selected int
}

func (a *tableApp) View() tui.View {
	columns := []tui.TableColumn{
		{Header: "Name", Width: 15},
		{Header: "Age", Width: 5},
		{Header: "City", Width: 20},
	}
	rows := [][]string{
		{"Alice", "28", "New York"},
		{"Bob", "35", "San Francisco"},
		{"Carol", "42", "Boston"},
	}

	return tui.Stack(
		tui.Text("Employee Directory").Bold(),
		tui.Table(columns, &a.selected).
			Rows(rows).
			Bordered().
			Striped(),
	).Gap(1)
}
Markdown Rendering
type docApp struct {
	markdown string
	scrollY  int
}

func (a *docApp) View() tui.View {
	return tui.Padding(2,
		tui.Markdown(a.markdown, &a.scrollY),
	)
}

func main() {
	content := `# Welcome

## Features
- **Bold** and *italic* text
- Code blocks with syntax highlighting
- Lists and tables
- Links and images

## Code Example
\x60\x60\x60go
func main() {
    fmt.Println("Hello, World!")
}
\x60\x60\x60
`

	tui.Run(&docApp{markdown: content})
}
Layout with Stack and Group
func (a *app) View() tui.View {
	return tui.Stack(
		// Header
		tui.Text("Dashboard").Bold().Fg(tui.ColorCyan),
		tui.Divider(),

		// Main content area (horizontal)
		tui.Group(
			// Left sidebar
			tui.Width(20,
				tui.Stack(
					tui.Text("Menu").Bold(),
					tui.Table([]tui.TableColumn{{Header: ""}}, &a.menuIdx).
						Rows([][]string{{"Home"}, {"Profile"}, {"Settings"}}),
				).Bordered(),
			),

			// Main panel
			tui.Stack(
				tui.Text("Content Area"),
				a.renderContent(),
			).Flex(1).Bordered(),

			// Right sidebar
			tui.Width(20,
				tui.Stack(
					tui.Text("Info").Bold(),
					tui.Text("Status: Active").Dim(),
				).Bordered(),
			),
		).Gap(1).Flex(1),

		// Footer
		tui.Divider(),
		tui.Text("Press q to quit").Dim(),
	)
}
Animated Text Effects
func (a *app) View() tui.View {
	return tui.Stack(
		// Rainbow color cycling
		tui.Text("Welcome to the App!").Animate(Rainbow(3)),

		// Pulsing alert
		tui.Text("ALERT").Animate(Pulse(tui.NewRGB(255, 0, 0), 12)),

		// Wave effect
		tui.Text("Status: Connected").Animate(Wave(12,
			tui.NewRGB(50, 150, 255),
			tui.NewRGB(100, 200, 255),
		)),

		// Sliding highlight
		tui.Text("Processing...").Animate(Slide(2,
			tui.NewRGB(100, 100, 100),
			tui.NewRGB(255, 255, 255),
		)),

		// Sparkle effect
		tui.Text("✨ Special ✨").Animate(Sparkle(3,
			tui.NewRGB(180, 180, 220),
			tui.NewRGB(255, 255, 255),
		)),

		// Typewriter effect
		tui.Text("Loading data...").Animate(Typewriter(3,
			tui.NewRGB(0, 255, 100),
			tui.NewRGB(255, 255, 255),
		).WithLoop(true)),

		// Glitch effect
		tui.Text("SIGNAL_LOST").Animate(Glitch(2,
			tui.NewRGB(255, 0, 100),
			tui.NewRGB(0, 255, 255),
		)),

		// Reversed rainbow with custom animation configuration
		tui.Text("Reversed!").Animate(Rainbow(3).Reverse()),
	).Gap(1)
}
Semantic Text Styles
func (a *app) View() tui.View {
	return tui.Stack(
		tui.Text("Operation completed successfully").Success(),
		tui.Text("Failed to connect to server").Error(),
		tui.Text("Disk space is running low").Warning(),
		tui.Text("Indexing 1,234 files...").Info(),
		tui.Text("Optional configuration available").Muted(),
		tui.Text("Press Enter to continue").Hint(),
	).Gap(1)
}
Custom Canvas Drawing
func (a *app) View() tui.View {
	return tui.CanvasContext(func(ctx *tui.RenderContext) {
		w, h := ctx.Size()
		frame := ctx.Frame()

		// Animated moving block
		x := int(frame) % w
		y := h / 2
		ctx.SetCell(x, y, '█', tui.NewStyle().WithForeground(tui.ColorCyan))

		// Draw border
		for i := 0; i < w; i++ {
			ctx.SetCell(i, 0, '─', tui.NewStyle())
			ctx.SetCell(i, h-1, '─', tui.NewStyle())
		}
		for i := 0; i < h; i++ {
			ctx.SetCell(0, i, '│', tui.NewStyle())
			ctx.SetCell(w-1, i, '│', tui.NewStyle())
		}
	})
}
Async Commands
func (a *app) HandleEvent(ev tui.Event) []tui.Cmd {
	switch e := ev.(type) {
	case tui.KeyEvent:
		if e.Rune == 'r' {
			// Trigger async refresh
			return []tui.Cmd{a.fetchData()}
		}
	case dataLoadedEvent:
		// Handle async result
		a.data = e.data
	}
	return nil
}

type dataLoadedEvent struct {
	data []string
}

func (a *app) fetchData() tui.Cmd {
	return func() tui.Event {
		// This runs in a goroutine
		time.Sleep(1 * time.Second)
		data := []string{"Item 1", "Item 2", "Item 3"}
		return dataLoadedEvent{data: data}
	}
}
Periodic Updates
type clockApp struct {
	time string
}

func (a *clockApp) View() tui.View {
	return tui.Text("Current time: %s", a.time).Bold()
}

func (a *clockApp) HandleEvent(ev tui.Event) []tui.Cmd {
	switch ev.(type) {
	case tui.TickEvent:
		a.time = time.Now().Format("15:04:05")
	case tui.KeyEvent:
		return []tui.Cmd{tui.Quit()}
	}
	return nil
}

func main() {
	// Run at 1 FPS for clock updates
	tui.Run(&clockApp{}, tui.WithFPS(1))
}
Progress Indicators
type app struct {
	progress int
	frame    uint64
	status   string
}

func (a *app) HandleEvent(event tui.Event) []tui.Cmd {
	if tick, ok := event.(tui.TickEvent); ok {
		a.frame = tick.Frame
	}
	return nil
}

func (a *app) View() tui.View {
	return tui.Stack(
		tui.Text("Download Progress").Bold(),

		tui.Progress(a.progress, 100).
			Width(40).
			ShowPercent(),

		tui.Loading(a.frame).Label("Loading..."),

		tui.Text("Status: %s", a.status).Dim(),
	).Gap(1)
}

API Reference

Application Types
Type Description
Application Main interface for full-screen UI apps
InlineApplication Interface for inline apps (LiveView method)
EventHandler Optional interface for handling events
Initializable Optional interface for initialization
Destroyable Optional interface for cleanup
View Core interface for UI components
RenderContext Drawing context with animation frame
Runtime Functions
Function Description Inputs Outputs
Run Starts full-screen app app Application, opts ...RuntimeOption error
NewInlineApp Creates inline app runner opts ...InlineOption *InlineApp
RunInline Convenience inline runner app any, opts ...InlineOption error
WithFPS Sets frame rate fps int RuntimeOption
WithMouseTracking Enables mouse support enabled bool RuntimeOption
Quit Returns quit command none Cmd
Layout Views
Function Description Inputs Outputs
Stack Vertical stack layout children ...View *stack
Group Horizontal stack layout children ...View *group
ZStack Layered stack layout children ...View *zStack
Spacer Flexible spacing none *spacerView
Empty Empty view none View

Flex Inheritance: Stack and Group containers automatically inherit flexibility from their children. If a container holds flexible views (like Canvas or Spacer), the container itself becomes flexible without needing an explicit .Flex() call. This enables intuitive nested layouts:

// Canvas expands because Group inherits its flexibility
Stack(
    Text("Header"),
    Group(Canvas()),  // Group auto-inherits flex from Canvas
    Text("Footer"),
)
Text Views
Function Description Inputs Outputs
Text Formatted text format string, args ...interface{} *textView
Markdown Markdown renderer content string, scrollY *int *markdownView
Code Syntax highlight code string, language string *codeView
DiffView Diff display diff *Diff, language string, scrollY *int *diffView
Input Views
Function Description Inputs Outputs
InputField Text input value *string *inputFieldView
PasswordInput Password input value *string *passwordInputView
TextArea Multi-line text input value *string *textAreaView
Interactive Views
Function Description Inputs Outputs
Button Keyboard button label string, onClick func() *buttonView
Clickable Mouse-only clickable label string, onClick func() *clickableView
PromptChoice Selection with inline input selected *int, inputText *string *promptChoiceView
Display Views
Function Description Inputs Outputs
Table Data table columns []TableColumn, selected *int *tableView
Tree Hierarchical tree root *TreeNode *treeView
Progress Progress indicator current, total int *progressView
Loading Loading spinner frame uint64 *loadingView
Divider Horizontal line none *dividerView
Container/Modifier Views
Function Description Inputs Outputs
Bordered Border container inner View *borderedView
Padding Padding container n int, inner View View
PaddingHV H/V padding h, v int, inner View View
Width Fixed width w int, inner View View
Height Fixed height h int, inner View View
MaxWidth Maximum width w int, inner View View
MinWidth Minimum width w int, inner View View
Scroll Scrollable container inner View, scrollY *int *scrollView

borderedView methods: .Title(string), .Border(*BorderStyle), .BorderFg(Color), .FocusBorderFg(Color), .TitleStyle(Style)

Custom Drawing
Function Description Inputs Outputs
Canvas Custom drawing area draw func(frame RenderFrame, bounds Rectangle) *canvasView
CanvasContext Canvas with context draw func(ctx *RenderContext) *canvasView
Collection Views
Function Description Inputs Outputs
ForEach Map items to views items []T, mapper func(T, int) View *forEachView
HForEach Horizontal map items []T, mapper func(T, int) View *hForEachView
Conditional Views
Function Description Inputs Outputs
If Conditional rendering condition bool, view View View
IfElse Conditional with else condition bool, then, else View View
Switch Multi-way conditional value T, cases ...CaseView[T] View
View Modifiers

Views support fluent modifier methods:

Modifier Description Example
.Width(int) Sets fixed width (on TextView) tui.Text("Hello").Width(20)
.Height(int) Sets fixed height tui.Text("Hi").Height(10)
.MaxWidth(int) Sets maximum width tui.Text(long).MaxWidth(80)
.Flex(int) Sets flex factor tui.Stack(...).Flex(1)
.Bordered() Adds border tui.Stack(...).Bordered()
.Padding(int) Adds padding (method on stack) tui.Stack(...).Padding(2)
.Gap(int) Sets spacing (Stack/Group) tui.Stack(...).Gap(1)
.Align(align) Sets alignment (Stack/Group) tui.Stack(...).Align(tui.AlignCenter)
.ID(string) Sets focus ID (inputs) tui.InputField(&s).ID("name")
Text Style Modifiers
Modifier Description
.Bold() Bold text
.Dim() Dimmed text
.Italic() Italic text
.Underline() Underlined text
.Strikethrough() Strikethrough text
.Blink() Blinking text
.Reverse() Reverse video (swap fg/bg)
.Fg(Color) Sets foreground color
.Bg(Color) Sets background color
.FgRGB(r, g, b) Sets foreground RGB color
.BgRGB(r, g, b) Sets background RGB color
.Wrap() Enable text wrapping
.Center() Center align text
.Right() Right align text
.FillBg() Fill background with color
Semantic Style Modifiers
Modifier Description
.Success() Green bold text
.Error() Red bold text
.Warning() Yellow bold text
.Info() Cyan text
.Muted() Dim gray text
.Hint() Dim italic text
Animation Modifiers

Apply animations using .Animate(animation) with animation constructors:

Animation Constructor Description Parameters Chainable Methods
Rainbow(speed) Rainbow color cycle speed int .Reverse(), .WithLength(int)
Pulse(color, speed) Pulsing brightness color RGB, speed int .Brightness(min, max float64)
Wave(speed, colors...) Wave color effect speed int, colors ...RGB .WithAmplitude(float64)
Slide(speed, base, highlight) Sliding highlight speed int, base, highlight RGB .Reversed(), .WithWidth(int)
Sparkle(speed, base, spark) Sparkle effect speed int, base, spark RGB .WithDensity(int)
Typewriter(speed, text, cursor) Typewriter reveal speed int, text, cursor RGB .WithLoop(bool), .WithHoldFrames(int)
Glitch(speed, base, glitch) Glitch effect speed int, base, glitch RGB .WithIntensity(int)

Example:

tui.Text("Hello").Animate(Rainbow(3))
tui.Text("Hello").Animate(Rainbow(3).Reverse())
tui.Text("Alert").Animate(Pulse(tui.NewRGB(255, 0, 0), 10).Brightness(0.3, 1.0))
Event Types
Event Description Fields
KeyEvent Keyboard input Rune rune, Key Key, Modifiers KeyModifier
MouseEvent Mouse input X, Y int, Button MouseButton, Action MouseAction
TickEvent Frame tick Frame uint64
ResizeEvent Terminal resize Width, Height int
ErrorEvent Error occurred Err error
QuitEvent Quit requested none
Command Types
Type Description
Cmd Function returning Event (runs async)
Quit() Exits application
Tick(dur) Creates a timer command
After(dur) Executes function after duration
Batch(...) Executes multiple commands
Sequence() Executes commands sequentially

Architecture

The TUI framework uses a declarative, single-threaded event model:

  1. View Tree: Application.View() returns an immutable view tree describing the UI
  2. Event Loop: Events are processed sequentially in a single goroutine
  3. No Locks: Application code never needs synchronization
  4. Commands: Async operations run in background goroutines and send results as events
  5. Render: After each event, View() is called and the tree is rendered

Runtime Options

tui.Run(app,
	tui.WithFPS(60),                    // 60 frames per second
	tui.WithMouseTracking(true),        // Enable mouse support
	tui.WithAlternateScreen(true),      // Use alternate screen buffer (default)
	tui.WithHideCursor(true),           // Hide cursor during rendering (default)
	tui.WithBracketedPaste(true),       // Enable bracketed paste mode
	tui.WithPasteTabWidth(4),           // Convert tabs to spaces in paste
)

Snapshot Testing

The tui package includes a comprehensive snapshot (golden) testing system for verifying rendered output. This approach captures the exact visual output of views and compares against saved snapshots.

Writing Snapshot Tests
func TestGolden_MyComponent(t *testing.T) {
    // Build your view
    view := Stack(
        Text("Header").Bold(),
        Divider(),
        Text("Content"),
    )

    // Render to a virtual screen with specific dimensions
    screen := SprintScreen(view, WithWidth(30), WithHeight(10))

    // Assert against saved snapshot
    termtest.AssertScreen(t, screen)
}
Test Organization

Tests are organized by feature in golden_test.go with clear section headers:

// =============================================================================
// MY COMPONENT TESTS - Description of what's being tested
// =============================================================================

func TestGolden_MyComponent_BasicUsage(t *testing.T) { ... }
func TestGolden_MyComponent_EdgeCase(t *testing.T) { ... }
Running Tests
# Run all golden tests
go test ./tui -run "TestGolden"

# Run specific test category
go test ./tui -run "TestGolden_UI"

# Run with verbose output
go test ./tui -run "TestGolden" -v
Creating and Updating Snapshots

When you add new tests or intentionally change rendering behavior:

# Create/update snapshots (flag must come AFTER package)
go test ./tui -run "TestGolden_MyComponent" -update

# Update all snapshots
go test ./tui -run "TestGolden" -update

Snapshots are stored in testdata/snapshots/ as .snap files.

Reviewing Tests

Use the reviewtests tool to review test code alongside snapshots:

# Review all tests containing "Flex"
go run ./tui/cmd/reviewtests Flex

# Review specific test
go run ./tui/cmd/reviewtests MyComponent

# Review all UI tests
go run ./tui/cmd/reviewtests UI

The tool displays each test's code and its expected snapshot output for easy verification.

Best Practices
  1. Descriptive Names: Use TestGolden_Category_Scenario naming convention
  2. Explicit Dimensions: Always specify WithWidth() and WithHeight() for consistent snapshots
  3. Comments: Add comments explaining what the test verifies
  4. Edge Cases: Test narrow widths, empty containers, and boundary conditions
  5. Real-World Patterns: Create tests inspired by actual UI patterns from examples/
Example: Complex UI Test
func TestGolden_UI_Dashboard(t *testing.T) {
    // Dashboard with header, panels, and footer
    view := Stack(
        HeaderBar("Dashboard"),
        Group(
            Bordered(Text("Panel A")).Border(&RoundedBorder).Title("Left"),
            Bordered(Text("Panel B")).Border(&RoundedBorder).Title("Right"),
        ).Gap(1),
        StatusBar("Ready"),
    )
    screen := SprintScreen(view, WithWidth(50), WithHeight(10))
    termtest.AssertScreen(t, screen)
}

Non-Interactive Printing

For CLI tools that want to display styled output without taking over the screen, use Print():

view := tui.Stack(
	tui.Text("Success!").Success(),
	tui.Text("Operation completed").Dim(),
).Gap(1)

// Print to stdout inline (no alternate screen, no event loop)
tui.Print(view)

// Or render to a string
output := tui.Sprint(view, tui.WithWidth(80))
fmt.Println(output)

The Print family of functions renders views without:

  • Enabling alternate screen mode
  • Starting an event loop
  • Handling keyboard input
  • Clearing the screen

This is perfect for command-line tools that want rich formatting without a full TUI.

Inline Applications

For applications that need both scrollback output and live updating regions, use InlineApp. This is ideal for chat interfaces, build tools with logs, REPLs, and similar applications.

Basic InlineApp
type CounterApp struct {
    runner *tui.InlineApp
    count  int
}

func (app *CounterApp) LiveView() tui.View {
    return tui.Stack(
        tui.Divider(),
        tui.Text(" Count: %d", app.count).Bold(),
        tui.Text(" Press +/- to change, q to quit").Dim(),
        tui.Divider(),
    )
}

func (app *CounterApp) HandleEvent(event tui.Event) []tui.Cmd {
    if key, ok := event.(tui.KeyEvent); ok {
        switch key.Rune {
        case '+':
            app.count++
            app.runner.Printf("Incremented to %d", app.count)
        case '-':
            app.count--
            app.runner.Printf("Decremented to %d", app.count)
        case 'q':
            return []tui.Cmd{tui.Quit()}
        }
    }
    return nil
}

func main() {
    app := &CounterApp{}
    app.runner = tui.NewInlineApp()
    if err := app.runner.Run(app); err != nil {
        log.Fatal(err)
    }
}
InlineApp vs Run
Feature tui.Run() tui.InlineApp
Screen mode Alternate (full screen) Inline (coexists with scrollback)
Interface View() LiveView()
Output View-only Print() to scrollback + live region
Use case Full TUI applications Chat, logs, REPLs
Terminal history Cleared on exit Preserved
InlineApp Options
runner := tui.NewInlineApp(
    tui.WithInlineWidth(80),              // Set rendering width
    tui.WithInlineFPS(30),                // Enable tick events for animations
    tui.WithInlineMouseTracking(true),    // Enable mouse events
    tui.WithInlineBracketedPaste(true),   // Enable bracketed paste mode
    tui.WithInlineKittyKeyboard(true),    // Enable Kitty keyboard protocol
    tui.WithInlinePasteTabWidth(4),       // Convert tabs in pasted text
)
InlineApp Architecture

InlineApp uses the same three-goroutine architecture as Run():

  1. Event loop: Processes events sequentially, calls HandleEvent and LiveView
  2. Input reader: Reads from stdin, sends key/mouse events
  3. Command executor: Runs async Cmd functions

This design ensures:

  • No race conditions in application code
  • HandleEvent and LiveView are never called concurrently
  • No locks needed in your application
Rendering Optimization

InlineApp minimizes flicker using:

  • Line-level diffing: Only changed lines are redrawn
  • Synchronized output mode: Terminal buffers changes and renders atomically (DEC 2026)
  • Atomic Print: Scrollback output and live region re-render happen as one operation
  • terminal - Low-level terminal control and ANSI sequences
  • termsession - Terminal session recording and playback
  • termtest - Terminal output testing with screen simulation

Documentation

Overview

Package tui provides a declarative terminal user interface framework for building interactive command-line applications in Go.

Philosophy

Wonton TUI is designed around declarative UI principles similar to SwiftUI and React. Applications describe what the UI should look like in terms of the current state, and the framework handles rendering and updates efficiently. This eliminates manual terminal manipulation and focus on application logic.

Quick Start

A minimal TUI application implements the Application interface with a View() method that returns the UI tree:

type App struct {
    count int
}

func (a *App) View() tui.View {
    return tui.Stack(
        tui.Text("Count: %d", a.count),
        tui.Button("Increment", func() { a.count++ }),
    )
}

func (a *App) HandleEvent(event tui.Event) []tui.Cmd {
    if key, ok := event.(tui.KeyEvent); ok && key.Rune == 'q' {
        return []tui.Cmd{tui.Quit()}
    }
    return nil
}

func main() {
    tui.Run(&App{})
}

Core Concepts

View Tree: The UI is built as a tree of View components. Containers like Stack, Group, and ZStack arrange children, while leaf views like Text, Button, and Input display content.

Event Loop: The Runtime manages a single-threaded event loop that processes events sequentially. This eliminates race conditions in application code - no locks needed for state management.

Commands: Async operations are handled through the Cmd system. Commands execute in separate goroutines and send their results back as events.

Layout System

Views are arranged in two phases:

  1. Measurement: Each view calculates its preferred size given constraints
  2. Rendering: Each view draws itself within its allocated space

Layout containers (Stack, Group, etc.) coordinate child sizing:

Stack(              // Vertical stack
    Text("Header"),
    Spacer(),       // Expands to fill space
    Text("Footer"),
).Gap(1)            // Space between children

Flex Inheritance: Containers automatically inherit flexibility from their children. If a Stack or Group contains flexible views (like Canvas or Spacer), the container itself becomes flexible. This allows nested layouts to work intuitively without explicitly setting .Flex() at every level:

Stack(
    Text("Header"),
    Group(Canvas()),  // Group inherits flex from Canvas
    Text("Footer"),
)

The Canvas will expand to fill available space because Group automatically becomes flexible when it contains flexible children.

View Components

Text and Styling:

Text("Hello").Fg(ColorGreen).Bold()
Text("Error: %s", err).Error()  // Semantic styling

Layout Containers:

Stack(children...)   // Vertical layout
Group(children...)   // Horizontal layout
ZStack(children...)  // Layered (z-axis) layout

Interactive Elements:

Button("Submit", func() { ... })      // Keyboard + mouse
Clickable("Link", func() { ... })     // Mouse-only
InputField(&app.name).Placeholder("Enter name")

Modifiers:

Padding(2, content)              // Add space around view
Bordered(content).Title("Box")   // Draw border with optional title
Width(40, content)               // Fixed width
MaxWidth(80, content)            // Maximum width constraint

Event Handling

Applications can optionally implement EventHandler to process events:

func (a *App) HandleEvent(event tui.Event) []tui.Cmd {
    switch e := event.(type) {
    case tui.KeyEvent:
        if e.Rune == 'q' {
            return []tui.Cmd{tui.Quit()}
        }
    case tui.ResizeEvent:
        // Handle terminal resize
    }
    return nil
}

Event Types:

  • KeyEvent: Keyboard input with modifiers
  • MouseEvent: Mouse clicks, movement, scrolling
  • TickEvent: Periodic timer for animations (based on FPS)
  • ResizeEvent: Terminal size changed
  • QuitEvent: Application should exit

Async Operations

Long-running operations use the Cmd system to avoid blocking the UI:

func fetchData() tui.Cmd {
    return func() tui.Event {
        data, err := http.Get("...")
        return DataEvent{data, err}
    }
}

func (a *App) HandleEvent(event tui.Event) []tui.Cmd {
    if _, ok := event.(tui.KeyEvent); ok {
        return []tui.Cmd{fetchData()}
    }
    if data, ok := event.(DataEvent); ok {
        a.data = data
    }
    return nil
}

Focus Management

Interactive elements like Button and InputField are automatically focusable. Users navigate with Tab/Shift+Tab, activate with Enter/Space:

Stack(
    InputField(&app.name).ID("name"),
    InputField(&app.email).ID("email"),
    Button("Submit", func() { app.submit() }),
)

Views can respond to focus state:

Bordered(content).
    FocusID("name").
    FocusBorderFg(ColorCyan)

Mouse Support

Mouse tracking is opt-in via WithMouseTracking:

tui.Run(&App{}, tui.WithMouseTracking(true))

Applications receive MouseEvent events for clicks, movement, and scrolling. Interactive elements (Button, Clickable) automatically handle mouse clicks.

Animations

Animations are driven by TickEvent, which fires at the configured FPS:

type App struct {
    frame uint64
}

func (a *App) HandleEvent(event tui.Event) []tui.Cmd {
    if tick, ok := event.(tui.TickEvent); ok {
        a.frame = tick.Frame
    }
    return nil
}

func (a *App) View() tui.View {
    return Text("Frame %d", a.frame)
}

Text views support built-in animations:

Text("Rainbow").Rainbow(5)
Text("Pulse").Pulse(tui.NewRGB(255, 0, 0), 10)
Text("Type").Typewriter(3, textColor, cursorColor)

Advanced Features

Collections: ForEach renders slices of data:

ForEach(app.items, func(item Item) tui.View {
    return Text(item.Name)
})

Conditional Rendering: If/IfElse for dynamic UI:

If(app.showHelp, func() tui.View {
    return Text("Help text...")
})

Scrolling: ScrollView for content larger than viewport:

Scroll(content, &app.scrollY).Height(20)

Tables: Table for tabular data:

Table(headers, &app.selected).Rows(rows)

Markdown: Markdown for rich formatted text:

Markdown(mdContent).Width(80)

Code Highlighting: CodeView with syntax highlighting:

CodeView(code, "go").Width(80)

Testing

The terminal package provides TestTerminal for unit testing TUI applications:

func TestApp(t *testing.T) {
    term := tui.NewTestTerminal(80, 24)
    runtime := tui.NewRuntime(term, &App{}, 30)
    // Send events and verify output
}

Thread Safety

The Runtime guarantees that View() and HandleEvent() are NEVER called concurrently. All state mutations happen in the single-threaded event loop, eliminating the need for locks in application code.

Commands execute in separate goroutines but communicate back via events, maintaining the single-threaded guarantee for application logic.

Performance

The framework uses double-buffering and dirty region tracking to minimize terminal I/O. Only changed cells are sent to the terminal, enabling smooth 60 FPS animations even in large UIs.

Package Organization

This package re-exports types from the terminal package (Style, Color, etc.) for convenience. Applications typically only need to import "tui".

Related packages:

  • terminal: Low-level terminal control and rendering
  • termtest: Testing utilities for TUI applications

For detailed examples, see the examples directory in the repository.

Index

Examples

Constants

View Source
const (
	DiffLineContext    = unidiff.LineContext
	DiffLineAdded      = unidiff.LineAdded
	DiffLineRemoved    = unidiff.LineRemoved
	DiffLineHeader     = unidiff.LineHeader
	DiffLineHunkHeader = unidiff.LineHunkHeader
)

Re-export DiffLineType constants with tui naming convention

View Source
const (
	KeyUnknown    = terminal.KeyUnknown
	KeyEnter      = terminal.KeyEnter
	KeyTab        = terminal.KeyTab
	KeyBackspace  = terminal.KeyBackspace
	KeyEscape     = terminal.KeyEscape
	KeyArrowUp    = terminal.KeyArrowUp
	KeyArrowDown  = terminal.KeyArrowDown
	KeyArrowLeft  = terminal.KeyArrowLeft
	KeyArrowRight = terminal.KeyArrowRight
	KeyHome       = terminal.KeyHome
	KeyEnd        = terminal.KeyEnd
	KeyPageUp     = terminal.KeyPageUp
	KeyPageDown   = terminal.KeyPageDown
	KeyDelete     = terminal.KeyDelete
	KeyInsert     = terminal.KeyInsert
	KeyF1         = terminal.KeyF1
	KeyF2         = terminal.KeyF2
	KeyF3         = terminal.KeyF3
	KeyF4         = terminal.KeyF4
	KeyF5         = terminal.KeyF5
	KeyF6         = terminal.KeyF6
	KeyF7         = terminal.KeyF7
	KeyF8         = terminal.KeyF8
	KeyF9         = terminal.KeyF9
	KeyF10        = terminal.KeyF10
	KeyF11        = terminal.KeyF11
	KeyF12        = terminal.KeyF12
	KeyCtrlA      = terminal.KeyCtrlA
	KeyCtrlB      = terminal.KeyCtrlB
	KeyCtrlC      = terminal.KeyCtrlC
	KeyCtrlD      = terminal.KeyCtrlD
	KeyCtrlE      = terminal.KeyCtrlE
	KeyCtrlF      = terminal.KeyCtrlF
	KeyCtrlG      = terminal.KeyCtrlG
	KeyCtrlH      = terminal.KeyCtrlH
	KeyCtrlI      = terminal.KeyCtrlI
	KeyCtrlJ      = terminal.KeyCtrlJ
	KeyCtrlK      = terminal.KeyCtrlK
	KeyCtrlL      = terminal.KeyCtrlL
	KeyCtrlM      = terminal.KeyCtrlM
	KeyCtrlN      = terminal.KeyCtrlN
	KeyCtrlO      = terminal.KeyCtrlO
	KeyCtrlP      = terminal.KeyCtrlP
	KeyCtrlQ      = terminal.KeyCtrlQ
	KeyCtrlR      = terminal.KeyCtrlR
	KeyCtrlS      = terminal.KeyCtrlS
	KeyCtrlT      = terminal.KeyCtrlT
	KeyCtrlU      = terminal.KeyCtrlU
	KeyCtrlV      = terminal.KeyCtrlV
	KeyCtrlW      = terminal.KeyCtrlW
	KeyCtrlX      = terminal.KeyCtrlX
	KeyCtrlY      = terminal.KeyCtrlY
	KeyCtrlZ      = terminal.KeyCtrlZ
)

Key constants

View Source
const (
	PasteAccept   = terminal.PasteAccept
	PasteReject   = terminal.PasteReject
	PasteModified = terminal.PasteModified
)

Paste constants

View Source
const (
	PasteDisplayNormal      = terminal.PasteDisplayNormal
	PasteDisplayPlaceholder = terminal.PasteDisplayPlaceholder
	PasteDisplayHidden      = terminal.PasteDisplayHidden
)
View Source
const (
	MouseButtonLeft       = terminal.MouseButtonLeft
	MouseButtonMiddle     = terminal.MouseButtonMiddle
	MouseButtonRight      = terminal.MouseButtonRight
	MouseButtonNone       = terminal.MouseButtonNone
	MouseButtonWheelUp    = terminal.MouseButtonWheelUp
	MouseButtonWheelDown  = terminal.MouseButtonWheelDown
	MouseButtonWheelLeft  = terminal.MouseButtonWheelLeft
	MouseButtonWheelRight = terminal.MouseButtonWheelRight
)

MouseButton constants

View Source
const (
	MousePress       = terminal.MousePress
	MouseRelease     = terminal.MouseRelease
	MouseClick       = terminal.MouseClick
	MouseDoubleClick = terminal.MouseDoubleClick
	MouseTripleClick = terminal.MouseTripleClick
	MouseDrag        = terminal.MouseDrag
	MouseDragStart   = terminal.MouseDragStart
	MouseDragEnd     = terminal.MouseDragEnd
	MouseDragCancel  = terminal.MouseDragCancel
	MouseMove        = terminal.MouseMove
	MouseEnter       = terminal.MouseEnter
	MouseLeave       = terminal.MouseLeave
	MouseScroll      = terminal.MouseScroll
)

MouseEventType constants

View Source
const (
	ModShift = terminal.ModShift
	ModAlt   = terminal.ModAlt
	ModCtrl  = terminal.ModCtrl
	ModMeta  = terminal.ModMeta
)

MouseModifiers constants

View Source
const (
	CursorDefault    = terminal.CursorDefault
	CursorPointer    = terminal.CursorPointer
	CursorText       = terminal.CursorText
	CursorResizeEW   = terminal.CursorResizeEW
	CursorResizeNS   = terminal.CursorResizeNS
	CursorResizeNESW = terminal.CursorResizeNESW
	CursorResizeNWSE = terminal.CursorResizeNWSE
	CursorMove       = terminal.CursorMove
	CursorNotAllowed = terminal.CursorNotAllowed
)

CursorStyle constants

View Source
const (
	BorderNone borderStyleType = iota
	BorderSingle
	BorderDouble
	BorderRounded
	BorderHeavy
)
View Source
const (
	ColorDefault       = terminal.ColorDefault
	ColorBlack         = terminal.ColorBlack
	ColorRed           = terminal.ColorRed
	ColorGreen         = terminal.ColorGreen
	ColorYellow        = terminal.ColorYellow
	ColorBlue          = terminal.ColorBlue
	ColorMagenta       = terminal.ColorMagenta
	ColorCyan          = terminal.ColorCyan
	ColorWhite         = terminal.ColorWhite
	ColorBrightBlack   = terminal.ColorBrightBlack
	ColorBrightRed     = terminal.ColorBrightRed
	ColorBrightGreen   = terminal.ColorBrightGreen
	ColorBrightYellow  = terminal.ColorBrightYellow
	ColorBrightBlue    = terminal.ColorBrightBlue
	ColorBrightMagenta = terminal.ColorBrightMagenta
	ColorBrightCyan    = terminal.ColorBrightCyan
	ColorBrightWhite   = terminal.ColorBrightWhite
)

Re-export color constants from terminal

View Source
const (
	AlignLeft   = terminal.AlignLeft
	AlignCenter = terminal.AlignCenter
	AlignRight  = terminal.AlignRight
)

Re-export alignment constants from terminal

Variables

View Source
var (
	// ErrInterrupted is returned when the user presses Ctrl+C.
	ErrInterrupted = errors.New("interrupted")

	// ErrEOF is returned when the user presses Ctrl+D on empty input.
	ErrEOF = errors.New("end of input")
)

Common errors returned by Prompt

View Source
var (
	SpinnerDots = SpinnerStyle{
		Frames:   []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
		Interval: 80 * time.Millisecond,
	}

	SpinnerLine = SpinnerStyle{
		Frames:   []string{"-", "\\", "|", "/"},
		Interval: 100 * time.Millisecond,
	}

	SpinnerArrows = SpinnerStyle{
		Frames:   []string{"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
		Interval: 100 * time.Millisecond,
	}

	SpinnerCircle = SpinnerStyle{
		Frames:   []string{"◐", "◓", "◑", "◒"},
		Interval: 120 * time.Millisecond,
	}

	SpinnerSquare = SpinnerStyle{
		Frames:   []string{"◰", "◳", "◲", "◱"},
		Interval: 100 * time.Millisecond,
	}

	SpinnerBounce = SpinnerStyle{
		Frames:   []string{"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
		Interval: 80 * time.Millisecond,
	}

	SpinnerBar = SpinnerStyle{
		Frames: []string{
			"[    ]",
			"[=   ]",
			"[==  ]",
			"[=== ]",
			"[====]",
			"[ ===]",
			"[  ==]",
			"[   =]",
		},
		Interval: 80 * time.Millisecond,
	}

	SpinnerStars = SpinnerStyle{
		Frames: []string{
			"✶",
			"✸",
			"✹",
			"✺",
			"✹",
			"✷",
		},
		Interval: 120 * time.Millisecond,
	}

	SpinnerStarField = SpinnerStyle{
		Frames: []string{
			"·   ",
			"*·  ",
			"✦*· ",
			"✧✦*·",
			"·✧✦*",
			" ·✧✦",
			"  ·✧",
			"   ·",
		},
		Interval: 100 * time.Millisecond,
	}

	SpinnerAsterisk = SpinnerStyle{
		Frames: []string{
			"*",
			"⁎",
			"✱",
			"✲",
			"✳",
			"✴",
			"✵",
			"✶",
			"✷",
			"✸",
			"✹",
			"✺",
		},
		Interval: 100 * time.Millisecond,
	}

	SpinnerSparkle = SpinnerStyle{
		Frames: []string{
			"✨",
			"💫",
			"⭐",
			"🌟",
			"✨",
			"💫",
		},
		Interval: 150 * time.Millisecond,
	}
)

Predefined spinner styles

View Source
var (
	NewRGB          = terminal.NewRGB
	Gradient        = terminal.Gradient
	RainbowGradient = terminal.RainbowGradient
	SmoothRainbow   = terminal.SmoothRainbow
	MultiGradient   = terminal.MultiGradient
)

Re-export color functions from terminal

View Source
var (
	SingleBorder  = terminal.SingleBorder
	DoubleBorder  = terminal.DoubleBorder
	RoundedBorder = terminal.RoundedBorder
	ThickBorder   = terminal.ThickBorder
	ASCIIBorder   = terminal.ASCIIBorder
)

Re-export border styles from terminal

View Source
var (
	NewTerminal      = terminal.NewTerminal
	NewTestTerminal  = terminal.NewTestTerminal
	NewRenderMetrics = terminal.NewRenderMetrics
	NewHyperlink     = terminal.NewHyperlink
	OSC8Start        = terminal.OSC8Start
	OSC8End          = terminal.OSC8End
	NewFrame         = terminal.NewFrame
)

Re-export terminal functions

View Source
var (
	ErrOutOfBounds   = terminal.ErrOutOfBounds
	ErrNotInRawMode  = terminal.ErrNotInRawMode
	ErrClosed        = terminal.ErrClosed
	ErrInvalidFrame  = terminal.ErrInvalidFrame
	ErrAlreadyActive = terminal.ErrAlreadyActive
)

Re-export error types from terminal

View Source
var AnimationPresets = struct {
	FadeIn    func(uint64) *Animation
	FadeOut   func(uint64) *Animation
	Pulse     func(uint64) *Animation
	Bounce    func(uint64) *Animation
	Elastic   func(uint64) *Animation
	SlideIn   func(uint64) *Animation
	Attention func(uint64) *Animation
}{
	FadeIn:    PresetFadeIn,
	FadeOut:   PresetFadeOut,
	Pulse:     PresetPulse,
	Bounce:    PresetBounce,
	Elastic:   PresetElastic,
	SlideIn:   PresetSlideIn,
	Attention: PresetAttention,
}

AnimationPresets provides common animation presets.

View Source
var BorderAnimationPresets = struct {
	Pulsing func(RGB, int) BorderAnimation
	Rainbow func(int, bool) BorderAnimation
	Marquee func(RGB, RGB, int, int) BorderAnimation
	Fire    func(int) BorderAnimation
}{
	Pulsing: PresetPulsingBorder,
	Rainbow: PresetRainbowBorder,
	Marquee: PresetMarqueeBorder,
	Fire:    PresetFireBorder,
}

BorderAnimationPresets provides common border animation presets.

View Source
var GlobalAnimationCoordinator = NewAnimationCoordinator()

GlobalAnimationCoordinator manages cross-view animations and global effects. It allows views to opt into synchronized animations and global effects.

View Source
var NewKeyDecoder = terminal.NewKeyDecoder

NewKeyDecoder creates a new key decoder

View Source
var NewStyle = terminal.NewStyle

Re-export style constructor

View Source
var ParseMouseEvent = terminal.ParseMouseEvent

ParseMouseEvent parses a mouse event from terminal input

Functions

func AlignText

func AlignText(text string, width int, align Alignment) string

AlignText aligns the given text within the specified width. It processes each line of the text individually.

func AnimatedBordered added in v0.0.2

func AnimatedBordered(inner View, animation BorderAnimation) *animatedBorderedView

AnimatedBordered wraps a view with an animated border.

func ApplyConstraints

func ApplyConstraints(size image.Point, constraints SizeConstraints) image.Point

ApplyConstraints applies size constraints to a size, returning the constrained size

func AvailableLanguages

func AvailableLanguages() []string

AvailableLanguages returns a list of available language lexers.

func AvailableThemes

func AvailableThemes() []string

AvailableThemes returns a list of available syntax highlighting themes.

func Bordered

func Bordered(inner View) *borderedView

Bordered wraps a view with a border and optional title. The border consumes 2 cells of width and height (1 on each side).

Use the builder pattern to customize the border:

Bordered(content).
    Border(&RoundedBorder).
    Title("Box Title").
    BorderFg(ColorCyan)

Focus-aware borders change color when a watched element is focused:

Bordered(InputField(&app.input)).
    FocusID("my-input").
    FocusBorderFg(ColorGreen)
Example

ExampleBordered demonstrates bordered views.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	content := tui.Text("Inside box")

	// Simple border
	_ = tui.Bordered(content)

	// Border with title and styling
	_ = tui.Bordered(content).
		Border(&tui.RoundedBorder).
		Title("Box Title").
		BorderFg(tui.ColorCyan)

	// Focus-aware border
	_ = tui.Bordered(content).
		FocusID("my-element").
		FocusBorderFg(tui.ColorGreen)
}

func Button added in v0.0.2

func Button(label string, callback func()) *buttonView

Button creates a focusable button element. The callback is invoked when the button is clicked or activated with Enter/Space.

Example:

Button("Submit", func() { app.submit() })
Example

ExampleButton demonstrates interactive buttons.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	type App struct {
		count int
	}
	app := &App{}

	// Button with callback
	button := tui.Button("Click Me", func() {
		app.count++
	})

	// Styled button
	_ = tui.Button("Submit", func() {}).
		Fg(tui.ColorGreen).
		Bold()

	_ = button
}

func Canvas

func Canvas(draw func(frame RenderFrame, bounds image.Rectangle)) *canvasView

Canvas creates a view that calls a custom draw function. This is the escape hatch for imperative drawing within declarative UI.

Example:

Canvas(func(frame RenderFrame, bounds image.Rectangle) {
    // Custom imperative drawing
    frame.PrintStyled(0, 0, "Custom content", style)
})

func CanvasContext added in v0.0.2

func CanvasContext(draw func(ctx *RenderContext)) *canvasView

CanvasContext creates a view with access to the full RenderContext. Use this when you need access to context information like the animation frame counter (ctx.Frame()) for custom animations.

Example:

CanvasContext(func(ctx *RenderContext) {
    w, h := ctx.Size()
    frame := ctx.Frame()  // Animation frame counter
    // Custom drawing using frame for animation timing
    x := int(frame) % w
    ctx.SetCell(x, 0, '█', style)
})

func CellGrid

func CellGrid(cols, rows int) *gridView

CellGrid creates a grid of clickable cells.

Example:

CellGrid(5, 5).CellSize(6, 3).Gap(1).OnClick(func(c, r int) { ... })

func CharGrid

func CharGrid(data [][]rune) *charGridView

CharGrid creates a grid displaying characters.

Example:

CharGrid([][]rune{
	{'X', 'O', 'X'},
	{'O', 'X', 'O'},
	{'X', 'O', 'X'},
})

func CheckboxList

func CheckboxList(items []ListItem, checked []bool, cursor *int) *checkboxListView

CheckboxList creates a list with checkable items. checked should be a slice tracking which items are checked. cursor should be a pointer to the current cursor position.

The component handles keyboard navigation (arrow keys) and toggling (space) automatically when focused. Use Tab to focus the list.

For focus management to work, you must set an ID using the ID() method. Without an ID, the list will not be focusable via Tab key navigation.

Example:

CheckboxList(items, app.checked, &app.cursor).
    ID("my-checkbox-list").
    OnToggle(func(i int, c bool) { ... })

func CheckboxListStrings

func CheckboxListStrings(labels []string, checked []bool, cursor *int) *checkboxListView

CheckboxListStrings creates a checkbox list from string labels.

func ClearLine added in v0.0.18

func ClearLine(cfg ...CursorConfig)

ClearLine clears the entire current line.

func ClearLines added in v0.0.18

func ClearLines(n int, cfg ...CursorConfig)

ClearLines clears n lines starting from the current cursor position, moving upward. Useful for clearing multi-line input areas. After clearing, the cursor is positioned at the start of the top-most cleared line.

func ClearLinesDown added in v0.0.18

func ClearLinesDown(n int, cfg ...CursorConfig)

ClearLinesDown clears n lines starting from current position, moving downward. After clearing, cursor is at the start of the last cleared line.

func ClearScreen added in v0.0.18

func ClearScreen(cfg ...CursorConfig)

ClearScreen clears the entire screen and moves cursor to top-left.

func ClearToEndOfLine added in v0.0.18

func ClearToEndOfLine(cfg ...CursorConfig)

ClearToEndOfLine clears from cursor to end of current line.

func ClearToEndOfScreen added in v0.0.18

func ClearToEndOfScreen(cfg ...CursorConfig)

ClearToEndOfScreen clears from cursor to end of screen.

func ClearToStartOfLine added in v0.0.18

func ClearToStartOfLine(cfg ...CursorConfig)

ClearToStartOfLine clears from cursor to start of current line.

func ClearToStartOfScreen added in v0.0.18

func ClearToStartOfScreen(cfg ...CursorConfig)

ClearToStartOfScreen clears from cursor to start of screen.

func Clickable

func Clickable(label string, callback func()) *clickableView

Clickable creates a mouse-only clickable element (not keyboard focusable). For keyboard-accessible buttons, use Button() instead.

Example:

Clickable("Link", func() { app.openLink() })

func Code

func Code(code string, language string) *codeView

Code creates a code view with syntax highlighting.

Example:

Code(`func main() {
    fmt.Println("Hello")
}`, "go")

func ColorGrid

func ColorGrid(cols, rows int, state [][]int, colors []Color) *colorGridView

ColorGrid creates a grid where clicking cycles through colors. state should be a 2D slice tracking the color index for each cell.

Example:

ColorGrid(5, 5, app.gridState, []Color{ColorBlack, ColorRed, ColorGreen, ColorBlue})

func CoordinatedView added in v0.0.2

func CoordinatedView(inner View, effectName string, transformFunc func(View, float64) View) *coordinatedView

CoordinatedView wraps a view to participate in a global animation effect. transformFunc is called with the current effect value to transform the view.

func Debug

func Debug(info *DebugInfo) *debugView

Debug creates a debug overlay view that displays runtime statistics.

Example:

type MyApp struct {
    debug *tui.DebugInfo
}

func (app *MyApp) View() tui.View {
    return tui.ZStack(
        app.mainContent(),
        tui.Debug(&app.debug).Position(tui.DebugTopRight),
    )
}

func (app *MyApp) HandleEvent(event tui.Event) []tui.Cmd {
    app.debug.Update(event) // Call this to track events
    // ...
}

func DiffView

func DiffView(diff *Diff, language string, scrollY *int) *diffView

DiffView creates a diff view from a parsed Diff. scrollY should be a pointer to the scroll position (optional, can be nil).

Example:

diff, _ := tui.ParseUnifiedDiff(diffText)
DiffView(diff, "go", &app.scrollY)

func DiffViewFromText

func DiffViewFromText(diffText, language string, scrollY *int) (*diffView, error)

DiffViewFromText creates a diff view from diff text. Returns an error if the diff cannot be parsed.

func Divider

func Divider() *dividerView

Divider creates a horizontal line separator that fills available width.

Example:

Divider()
Divider().Char('═')
Divider().Title("Section")

func EaseInBack added in v0.0.2

func EaseInBack(t float64) float64

EaseInBack overshoots then returns.

func EaseInBounce added in v0.0.2

func EaseInBounce(t float64) float64

EaseInBounce creates a bouncing effect at the start.

func EaseInCirc added in v0.0.2

func EaseInCirc(t float64) float64

EaseInCirc accelerates using circular function.

func EaseInCubic added in v0.0.2

func EaseInCubic(t float64) float64

EaseInCubic accelerates from zero velocity (cubic).

func EaseInElastic added in v0.0.2

func EaseInElastic(t float64) float64

EaseInElastic creates an elastic/spring effect at the start.

func EaseInExpo added in v0.0.2

func EaseInExpo(t float64) float64

EaseInExpo accelerates exponentially.

func EaseInOutBack added in v0.0.2

func EaseInOutBack(t float64) float64

EaseInOutBack overshoots in both directions.

func EaseInOutBounce added in v0.0.2

func EaseInOutBounce(t float64) float64

EaseInOutBounce creates a bouncing effect in both directions.

func EaseInOutCirc added in v0.0.2

func EaseInOutCirc(t float64) float64

EaseInOutCirc accelerates and decelerates using circular function.

func EaseInOutCubic added in v0.0.2

func EaseInOutCubic(t float64) float64

EaseInOutCubic accelerates until halfway, then decelerates (cubic).

func EaseInOutElastic added in v0.0.2

func EaseInOutElastic(t float64) float64

EaseInOutElastic creates an elastic effect in both directions.

func EaseInOutExpo added in v0.0.2

func EaseInOutExpo(t float64) float64

EaseInOutExpo accelerates and decelerates exponentially.

func EaseInOutQuad added in v0.0.2

func EaseInOutQuad(t float64) float64

EaseInOutQuad accelerates until halfway, then decelerates (quadratic).

func EaseInOutQuart added in v0.0.2

func EaseInOutQuart(t float64) float64

EaseInOutQuart accelerates until halfway, then decelerates (quartic).

func EaseInOutSine added in v0.0.2

func EaseInOutSine(t float64) float64

EaseInOutSine accelerates and decelerates using sine wave.

func EaseInQuad added in v0.0.2

func EaseInQuad(t float64) float64

EaseInQuad accelerates from zero velocity (quadratic).

func EaseInQuart added in v0.0.2

func EaseInQuart(t float64) float64

EaseInQuart accelerates from zero velocity (quartic).

func EaseInSine added in v0.0.2

func EaseInSine(t float64) float64

EaseInSine accelerates using sine wave.

func EaseLinear added in v0.0.2

func EaseLinear(t float64) float64

EaseLinear provides constant speed (no easing).

func EaseOutBack added in v0.0.2

func EaseOutBack(t float64) float64

EaseOutBack overshoots then settles.

func EaseOutBounce added in v0.0.2

func EaseOutBounce(t float64) float64

EaseOutBounce creates a bouncing effect at the end.

func EaseOutCirc added in v0.0.2

func EaseOutCirc(t float64) float64

EaseOutCirc decelerates using circular function.

func EaseOutCubic added in v0.0.2

func EaseOutCubic(t float64) float64

EaseOutCubic decelerates to zero velocity (cubic).

func EaseOutElastic added in v0.0.2

func EaseOutElastic(t float64) float64

EaseOutElastic creates an elastic/spring effect at the end.

func EaseOutExpo added in v0.0.2

func EaseOutExpo(t float64) float64

EaseOutExpo decelerates exponentially.

func EaseOutQuad added in v0.0.2

func EaseOutQuad(t float64) float64

EaseOutQuad decelerates to zero velocity (quadratic).

func EaseOutQuart added in v0.0.2

func EaseOutQuart(t float64) float64

EaseOutQuart decelerates to zero velocity (quartic).

func EaseOutSine added in v0.0.2

func EaseOutSine(t float64) float64

EaseOutSine decelerates using sine wave.

func FilePicker

func FilePicker(items []ListItem, filter *string, selected *int) *filePickerView

FilePicker creates a file picker view with filter and list. items should contain the files/directories to display. filter should be a pointer to the filter text. selected should be a pointer to the selected index.

The filter input and list are keyboard navigable. Use Tab to switch between them, type to filter, and arrow keys to navigate the list.

Example:

FilePicker(app.files, &app.filter, &app.selected).
    CurrentPath(app.currentDir).
    OnSelect(func(item ListItem, index int) { app.handleSelect(item, index) })

func Fill

func Fill(char rune) *fillView

Fill creates a view that fills its available space with the given character.

func FilterableList added in v0.0.2

func FilterableList(items []ListItem, selected *int) *listView

FilterableList creates a new filterable list view with the given items. selected is a pointer to the currently selected index (cursor position) in the filtered list.

The component handles keyboard navigation (arrow keys), filtering (typing), and selection (Enter) automatically when focused. Use Tab to focus the list.

For focus management to work, you must set an ID using the ID() method. Without an ID, the list will not be focusable via Tab key navigation.

To track chosen items (items confirmed with Enter), use Chosen() to bind an external slice. Use MultiSelect(true) to allow multiple items to be chosen, or leave as single-select mode (default) where choosing a new item clears the previous choice. Use Markers() to display chosen/unchosen indicators on the right side of each item.

Example:

FilterableList(items, &app.selected).
    ID("my-list").
    Filter(&app.filterText).
    Height(10).
    MultiSelect(true).
    Chosen(&app.chosen).
    Markers("[ ]", "[x]").
    OnSelect(func(item tui.ListItem, idx int) { ... })

func FilterableListStrings added in v0.0.2

func FilterableListStrings(labels []string, selected *int) *listView

FilterableListStrings creates a filterable list from string labels.

func FocusText added in v0.0.2

func FocusText(content string, focusID string) *focusTextView

FocusText creates a text view that changes style based on a watched focus ID. This is useful for labels that should highlight when their associated input is focused.

Example:

FocusText("Name: ", "name-input").
    Style(dimStyle).
    FocusStyle(brightStyle)

func ForEach

func ForEach[T any](items []T, mapper func(item T, index int) View) *forEachView[T]

ForEach maps a slice of items to views using a mapper function. The resulting views are arranged in a Stack by default.

Example:

ForEach(app.items, func(item Item, i int) View {
    return Text("%d. %s", i+1, item.Name)
})

func Fprint added in v0.0.2

func Fprint(w io.Writer, view View, cfgs ...PrintConfig) error

Fprint renders a view to the specified writer.

func FuzzyMatch

func FuzzyMatch(pattern, text string) bool

FuzzyMatch checks if pattern matches text (subsequence match)

func FuzzySearch

func FuzzySearch(pattern string, candidates []string) []string

FuzzySearch returns the top matches for pattern in candidates

func Group added in v0.0.2

func Group(children ...View) *group

Group creates a group that arranges children left-to-right (horizontal layout).

Children are laid out horizontally with optional spacing and alignment. Flexible children (like Spacer) will expand to fill available space.

Example:

Group(
    Text("Left"),
    Spacer(),
    Text("Right"),
).Gap(2).Align(AlignCenter)

func HForEach

func HForEach[T any](items []T, mapper func(item T, index int) View) *hForEachView[T]

HForEach is like ForEach but arranges items horizontally in a Group.

Example:

HForEach(app.tabs, func(tab Tab, i int) View {
    return Text(tab.Title).Padding(1)
})

func HeaderBar

func HeaderBar(text string) *headerBarView

HeaderBar creates a full-width header bar with centered text.

Example:

HeaderBar("My App").Bg(ColorBlue).Fg(ColorWhite)

func HideCursor added in v0.0.18

func HideCursor(cfg ...CursorConfig)

HideCursor hides the cursor.

func InlineLinks(separator string, links ...Hyperlink) *inlineLinkView

InlineLinks creates a horizontal row of hyperlinks with separators.

Example:

InlineLinks(" | ",
	NewHyperlink("https://go.dev", "Go"),
	NewHyperlink("https://github.com", "GitHub"),
)

func Input

func Input(binding *string) *inputView

Input creates a text input view bound to a string pointer. Changes to the input will update the bound string.

Example:

Input(&app.name).Placeholder("Enter name...")

func InputField added in v0.0.2

func InputField(binding *string) *inputFieldView

InputField creates a high-level input component with optional label and border. Focus styling is handled automatically - the label and border change appearance when the input is focused.

Example:

InputField(&app.name).
    Label("Name:").
    Placeholder("Enter your name").
    Bordered().
    Width(40)
Example

ExampleInputField demonstrates text input fields.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	type App struct {
		name  string
		email string
	}
	app := &App{}

	// Basic input field
	input := tui.InputField(&app.name).
		Label("Name:").
		Placeholder("Enter your name")

	// Input with border and custom styling
	_ = tui.InputField(&app.email).
		Label("Email").
		Placeholder("[email protected]").
		Bordered().
		FocusBorderFg(tui.ColorCyan).
		Width(40)

	_ = input
}

func KeyValue

func KeyValue(label, value string) *keyValueView

KeyValue creates a key-value pair display.

Example:

KeyValue("Name", "John Doe")
KeyValue("Status", "Active").LabelFg(ColorYellow).ValueFg(ColorGreen)
func Link(url, text string) *hyperlinkView

Link creates a declarative hyperlink view. If text is empty, the URL will be used as the display text.

Example:

Link("https://github.com", "GitHub")
Link("https://example.com", "").Fg(ColorCyan)  // uses URL as text
func LinkList(links ...Hyperlink) *linkListView

LinkList creates a vertical list of hyperlinks.

Example:

LinkList(
	NewHyperlink("https://go.dev", "Go"),
	NewHyperlink("https://github.com", "GitHub"),
)

func LinkRow

func LinkRow(label, url, linkText string) *linkRowView

LinkRow creates a label + hyperlink pair, useful for tables of links.

Example:

LinkRow("Documentation", "https://docs.example.com", "docs.example.com")

func Live added in v0.0.2

func Live(fn func(update func(View)), cfgs ...PrintConfig) error

Live is a convenience function for simple live updates with a callback. It creates a LivePrinter, calls the provided function with an update callback, and automatically stops when done.

Example:

tui.Live(func(update func(tui.View)) {
    for i := 0; i <= 100; i++ {
        update(tui.Text("Loading... %d%%", i))
        time.Sleep(50 * time.Millisecond)
    }
}, tui.PrintConfig{Width: 40})

func Loading

func Loading(frame uint64) *loadingView

Loading creates an animated loading spinner view. The frame parameter should come from TickEvent.Frame for animation.

Example:

Loading(app.frame).Label("Loading...")
Loading(app.frame).CharSet(SpinnerDots.Frames)

func Markdown

func Markdown(content string, scrollY *int) *markdownView

Markdown creates a markdown view with the given content. scrollY should be a pointer to the scroll position (optional, can be nil).

Example:

Markdown(content, &app.scrollY).Theme(tui.DefaultMarkdownTheme())

func MeasureText

func MeasureText(text string) (width, height int)

MeasureText returns the width and height (number of lines) of the text.

func MeasureWidget

func MeasureWidget(w ComposableWidget, c SizeConstraints) image.Point

MeasureWidget is a helper to measure a widget whether it implements Measurable or not. If the widget is Measurable, it calls Measure. Otherwise, it calls GetPreferredSize and applies the constraints.

func Meter

func Meter(label string, value, max int) *meterView

Meter creates a labeled meter/gauge view.

Example:

Meter("CPU", 75, 100).Width(20)

func MoveCursorDown added in v0.0.18

func MoveCursorDown(n int, cfg ...CursorConfig)

MoveCursorDown moves the cursor down n lines.

func MoveCursorLeft added in v0.0.18

func MoveCursorLeft(n int, cfg ...CursorConfig)

MoveCursorLeft moves the cursor left n columns.

func MoveCursorRight added in v0.0.18

func MoveCursorRight(n int, cfg ...CursorConfig)

MoveCursorRight moves the cursor right n columns.

func MoveCursorUp added in v0.0.18

func MoveCursorUp(n int, cfg ...CursorConfig)

MoveCursorUp moves the cursor up n lines.

func MoveToColumn added in v0.0.18

func MoveToColumn(col int, cfg ...CursorConfig)

MoveToColumn moves the cursor to the specified column (1-based).

func MoveToLineStart added in v0.0.18

func MoveToLineStart(cfg ...CursorConfig)

MoveToLineStart moves the cursor to the beginning of the current line.

func Newline added in v0.0.18

func Newline(cfg ...CursorConfig)

Newline prints a newline character.

func Panel

func Panel(content View) *panelView

Panel creates a filled box/panel view with optional content.

Example:

Panel(nil).Width(20).Height(5).Bg(ColorBlue)
Panel(Text("Hello")).Border(BorderSingle)

func Print added in v0.0.2

func Print(view View, cfgs ...PrintConfig) error

Print renders a view to the terminal without taking over the screen. This outputs the view inline, preserving scroll history and existing content.

Unlike Run(), Print does not:

  • Enable alternate screen mode
  • Enable raw mode or handle keyboard input
  • Clear the screen
  • Start an event loop

This is useful for CLI tools that want to display styled output once and then exit, without the full TUI experience.

Example:

view := tui.Stack(
    tui.Text("Hello").Bold(),
    tui.Text("World").Foreground(tui.ColorRed),
)
tui.Print(view)

With configuration:

tui.Print(view, tui.PrintConfig{Width: 60})

func Progress

func Progress(current, total int) *progressView

Progress creates a declarative progress bar view. current is the current progress value, total is the maximum value.

Example:

Progress(50, 100).Width(30).ShowPercent()

func Prompt added in v0.0.19

func Prompt(prompt string, opts ...PromptOption) (string, error)

Prompt reads a line of input from the user with rich editing support. It temporarily enables raw mode, handles input, and restores terminal state. Returns the entered text (trimmed) or an error.

Errors:

  • ErrInterrupted: User pressed Ctrl+C
  • ErrEOF: User pressed Ctrl+D on empty input
  • Other errors from terminal operations

Example:

input, err := tui.Prompt("> ",
    tui.WithHistory(&history),
    tui.WithAutocomplete(completeFn),
    tui.WithMultiLine(true),
)

func PromptChoice added in v0.0.22

func PromptChoice(selected *int, inputText *string) *promptChoiceView

PromptChoice creates a prompt choice view with selectable options.

This component creates a Claude Code-style selection interface with numbered options and optional inline text input. It's ideal for confirmation dialogs, action menus, and prompts that need both preset choices and custom input.

Parameters

  • selected: Pointer to track the currently selected index (0-based). This value updates as the user navigates.
  • inputText: Pointer to store custom input text. Can be nil if no input option is needed. When an InputOption is added, typed text is stored here.

Basic Example

var selected int
var customText string

view := PromptChoice(&selected, &customText).
    Option("Yes").
    Option("No").
    OnSelect(func(idx int, text string) {
        if idx == 0 {
            // User selected "Yes"
        }
    })

With Input Option

view := PromptChoice(&selected, &customText).
    Option("Approve").
    Option("Reject").
    InputOption("Provide feedback...").
    OnSelect(func(idx int, text string) {
        if idx == 2 {
            // User typed custom feedback in 'text'
            fmt.Println("Feedback:", text)
        }
    }).
    OnCancel(func() {
        fmt.Println("Cancelled")
    })

Customization Example

view := PromptChoice(&selected, nil).
    Option("Option A").
    Option("Option B").
    CursorChar(">").
    ShowNumbers(false).
    HintText("Press Enter to confirm").
    Style(tui.NewStyle().WithForeground(tui.ColorCyan))

InlineApp Integration

In an InlineApp, include PromptChoice in your LiveView:

func (app *App) LiveView() tui.View {
    return tui.Stack(
        tui.Text("Do you want to proceed?").Bold(),
        tui.Text(""),
        tui.PromptChoice(&app.selected, &app.input).
            Option("Yes").
            Option("No").
            InputOption("Other...").
            OnSelect(app.handleSelection).
            OnCancel(app.handleCancel),
    )
}

func RadioList

func RadioList(items []ListItem, selected *int) *radioListView

RadioList creates a list with radio button items (single selection). selected should be a pointer to the currently selected index.

The component handles keyboard navigation (arrow keys) and selection (Enter/Space) automatically when focused. Use Tab to focus the list.

For focus management to work, you must set an ID using the ID() method. Without an ID, the list will not be focusable via Tab key navigation.

Example:

RadioList(items, &app.selected).
    ID("my-radio-list").
    OnSelect(func(i int) { ... })

func RadioListStrings

func RadioListStrings(labels []string, selected *int) *radioListView

RadioListStrings creates a radio list from string labels.

func ReadPassword

func ReadPassword(prompt string) (string, error)

ReadPassword reads a password with no echo to the terminal.

func ReadSimple

func ReadSimple(prompt string) (string, error)

ReadSimple reads a single line of input from stdin.

func RestoreCursor added in v0.0.18

func RestoreCursor(cfg ...CursorConfig)

RestoreCursor restores the cursor to the last saved position.

func Run

func Run(app any, opts ...RunOption) error

Run is the simplest way to start a Wonton application. It creates a terminal, configures it, runs the application, and cleans up.

The app parameter must implement Application.

Example:

type MyApp struct {
    count int
}

func (app *MyApp) View() tui.View {
    return tui.Stack(
        tui.Text("Count: %d", app.count),
        tui.Clickable("[+]", func() { app.count++ }),
    )
}

func (app *MyApp) HandleEvent(event tui.Event) []tui.Cmd {
    if key, ok := event.(tui.KeyEvent); ok && key.Rune == 'q' {
        return []tui.Cmd{tui.Quit()}
    }
    return nil
}

func main() {
    if err := tui.Run(&MyApp{}); err != nil {
        log.Fatal(err)
    }
}

Options can be passed to customize behavior:

tui.Run(&MyApp{},
    tui.WithFPS(60),
    tui.WithMouseTracking(true),
)
Example

ExampleRun demonstrates running a TUI application with options.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	type App struct{}

	app := &App{}

	// Run with default options
	// tui.Run(app)

	// Run with custom options
	_ = tui.Run(app,
		tui.WithFPS(60),               // 60 FPS for smooth animations
		tui.WithMouseTracking(true),   // Enable mouse events
		tui.WithAlternateScreen(true), // Use alternate screen buffer
		tui.WithBracketedPaste(true),  // Handle pasted text properly
	)

}

func RunInline added in v0.0.19

func RunInline(app any, cfg func(*InlineApp)) error

RunInline is a convenience function that creates and runs an InlineApp. You can configure it by passing a configuration function that receives the app.

Example:

err := tui.RunInline(&MyApp{}, func(app *tui.InlineApp) {
    app.Width(80).BracketedPaste(true)
})

func SaveCursor added in v0.0.18

func SaveCursor(cfg ...CursorConfig)

SaveCursor saves the current cursor position. Use RestoreCursor to return to this position later.

Example:

tui.SaveCursor()
fmt.Println("This text appears below")
tui.RestoreCursor()
fmt.Print("Cursor is back!")

func Scroll

func Scroll(inner View, scrollY *int) *scrollView

Scroll creates a scrollable viewport around the inner view. If scrollY is provided, it controls the scroll offset externally.

Example:

Scroll(content, &app.scrollY).Anchor(ScrollAnchorBottom)
Scroll(content, nil).Bottom() // auto-scroll to bottom

func SelectList

func SelectList(items []ListItem, selected *int) *selectListView

SelectList creates a selectable list view using the existing ListItem type. selected should be a pointer to the currently selected index.

The component handles keyboard navigation (arrow keys) and selection (Enter) automatically when focused. Use Tab to focus the list.

Example:

SelectList(items, &app.selectedIndex).OnSelect(func(item, idx) { app.handleSelect(item, idx) })

func SelectListStrings

func SelectListStrings(labels []string, selected *int) *selectListView

SelectListStrings creates a list from string labels.

func ShowCursor added in v0.0.18

func ShowCursor(cfg ...CursorConfig)

ShowCursor shows the cursor.

func Sine

func Sine(x float64) float64

Sine helper for pulse calculations

func Spacer

func Spacer() *spacerView

Spacer returns a flexible view that expands to fill available space. In a Stack, it fills vertical space. In a Group, it fills horizontal space.

Spacers are useful for pushing content to edges or distributing space:

Stack(
    Text("Top"),
    Spacer(),        // Pushes footer to bottom
    Text("Bottom"),
)

Group(
    Text("Left"),
    Spacer(),        // Pushes right content to edge
    Text("Right"),
)
Example

ExampleSpacer demonstrates flexible space distribution.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	// Push content to opposite edges
	view := tui.Stack(
		tui.Text("Top"),
		tui.Spacer(), // Fills all available space
		tui.Text("Bottom"),
	)

	_ = view
}

func Sprint added in v0.0.2

func Sprint(view View, cfgs ...PrintConfig) string

Sprint renders a view to a string with ANSI escape codes.

func SprintScreen added in v0.0.2

func SprintScreen(view View, cfgs ...PrintConfig) *termtest.Screen

SprintScreen renders a view and returns a termtest.Screen for assertions. This is a convenience function for testing that combines Sprint with termtest.Screen parsing, making it easy to write precise visual tests.

Example:

func TestButton(t *testing.T) {
    btn := Button("Submit", func() {})
    screen := SprintScreen(btn, PrintConfig{Width: 20})
    termtest.AssertRowContains(t, screen, 0, "Submit")
}

func Stack added in v0.0.2

func Stack(children ...View) *stack

Stack creates a vertical stack that arranges children top-to-bottom. This is one of the primary layout containers in TUI applications.

Children are laid out vertically with optional spacing and alignment. Flexible children (like Spacer) will expand to fill available space.

Example:

Stack(
    Text("Header").Bold(),
    Spacer(),
    Text("Footer"),
).Gap(1).Align(AlignCenter)
Example

ExampleStack demonstrates vertical layout of views.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	// Stack arranges children vertically
	view := tui.Stack(
		tui.Text("Header").Bold(),
		tui.Text("Body content"),
		tui.Spacer(),
		tui.Text("Footer").Dim(),
	).Gap(1) // 1 row between children

	_ = view
}

func StatusBar

func StatusBar(text string) *headerBarView

StatusBar creates a full-width status bar (same as HeaderBar but defaults to bottom style).

func StyledButton

func StyledButton(label string, callback func()) *styledButtonView

StyledButton creates a styled button with dimensions. Unlike Clickable, this draws a filled box with centered text.

Example:

StyledButton("Submit", func() { app.submit() }).Width(20).Height(3).Bg(ColorBlue)

func Table

func Table(columns []TableColumn, selected *int) *tableView

Table creates a new table view with the given columns. selected should be a pointer to the currently selected row index.

The component handles keyboard navigation (arrow keys) and selection (Enter) automatically when focused. Use Tab to focus the table.

Example:

Table([]TableColumn{{Title: "Name"}, {Title: "Age"}}, &app.selected).
    Rows([][]string{{"Alice", "30"}, {"Bob", "25"}})

func Text

func Text(format string, args ...any) *textView

Text creates a text view with optional Printf-style formatting. This is the fundamental component for displaying text in TUI applications.

The text view supports:

  • Styling methods (Fg, Bg, Bold, Italic, etc.)
  • Semantic styling (Success, Error, Warning, Info, Muted, Hint)
  • Animation effects (Rainbow, Pulse, Typewriter, Glitch, etc.)

Example:

Text("Hello, %s!", userName).Fg(ColorGreen).Bold()
Text("Error: %s", err).Error()
Text("Loading...").Pulse(tui.NewRGB(0, 255, 0), 10)
Example

ExampleText demonstrates text rendering with styling.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	// Basic text
	_ = tui.Text("Hello, World!")

	// Text with formatting
	name := "Alice"
	_ = tui.Text("Hello, %s!", name)

	// Text with styling
	_ = tui.Text("Success").Fg(tui.ColorGreen).Bold()

	// Semantic styling
	_ = tui.Text("Error occurred").Error()
	_ = tui.Text("Warning message").Warning()
	_ = tui.Text("Helpful hint").Hint()

}

func TextArea added in v0.0.2

func TextArea(binding *string) *textAreaView

TextArea creates a scrollable text display component. It's focusable, supports keyboard scrolling, and has focus-aware border styling.

Example:

TextArea(&app.output).
    ID("output-view").
    Title("Output").
    Bordered().
    Size(60, 10)

func Toggle

func Toggle(value *bool) *toggleView

Toggle creates an on/off toggle switch. value should be a pointer to a bool controlling the toggle state.

The component handles keyboard input (Space/Enter to toggle) automatically when focused. Use Tab to focus the toggle.

Example:

Toggle(&app.darkMode).OnChange(func(v bool) { app.updateTheme() })

func Tree

func Tree(root *TreeNode) *treeView

Tree creates a tree view with the given root node.

The component handles keyboard navigation (arrow keys) and expand/collapse (Enter/Space) automatically when focused. Use Tab to focus the tree.

Example:

root := tui.NewTreeNode("Root").SetExpanded(true).AddChildren(
    tui.NewTreeNode("Child 1"),
    tui.NewTreeNode("Child 2").AddChildren(
        tui.NewTreeNode("Grandchild"),
    ),
)
Tree(root).OnSelect(func(node *tui.TreeNode) {
    fmt.Println("Selected:", node.Label)
})

func WrapText

func WrapText(text string, width int) string

WrapText wraps the given text to fit within the specified width. It respects existing newlines and wraps on word boundaries where possible.

func ZStack

func ZStack(children ...View) *zStack

ZStack creates a stack that layers children on top of each other (z-axis). The first child is at the bottom layer, the last child is on top.

This is useful for overlays, backgrounds, and layered effects. The stack sizes to the largest child.

Example:

ZStack(
    // Background layer
    Bordered(Empty()).Border(&SingleBorder),
    // Foreground content
    Padding(2, Text("Overlay")),
).Align(AlignCenter)

Types

type Alignment

type Alignment = terminal.Alignment

Alignment represents text alignment

type AnimatedElement

type AnimatedElement interface {
	Update(frame uint64)
	Draw(frame RenderFrame)
	Position() (x, y int)
	Dimensions() (width, height int)
}

AnimatedElement represents any element that can be animated

type AnimatedMultiLine

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

AnimatedMultiLine represents multiple lines of animated content

func NewAnimatedMultiLine

func NewAnimatedMultiLine(x, y, width int) *AnimatedMultiLine

NewAnimatedMultiLine creates a new multi-line animated element

func (*AnimatedMultiLine) AddLine

func (aml *AnimatedMultiLine) AddLine(text string, animation TextAnimation)

AddLine adds a new line with animation

func (*AnimatedMultiLine) ClearLines

func (aml *AnimatedMultiLine) ClearLines()

ClearLines removes all lines

func (*AnimatedMultiLine) Dimensions

func (aml *AnimatedMultiLine) Dimensions() (width, height int)

Dimensions returns the element's dimensions

func (*AnimatedMultiLine) Draw

func (aml *AnimatedMultiLine) Draw(frame RenderFrame)

Draw renders all animated lines

func (*AnimatedMultiLine) Position

func (aml *AnimatedMultiLine) Position() (x, y int)

Position returns the element's position

func (*AnimatedMultiLine) SetLine

func (aml *AnimatedMultiLine) SetLine(index int, text string, animation TextAnimation)

SetLine sets the content and animation for a specific line

func (*AnimatedMultiLine) SetPosition

func (aml *AnimatedMultiLine) SetPosition(x, y int)

SetPosition updates the element's position

func (*AnimatedMultiLine) SetWidth

func (aml *AnimatedMultiLine) SetWidth(width int)

SetWidth updates the element's width

func (*AnimatedMultiLine) Update

func (aml *AnimatedMultiLine) Update(frame uint64)

Update updates the animation frame

type AnimatedStatusBar

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

AnimatedStatusBar represents an animated status bar

func NewAnimatedStatusBar

func NewAnimatedStatusBar(x, y, width int) *AnimatedStatusBar

NewAnimatedStatusBar creates a new animated status bar

func (*AnimatedStatusBar) AddItem

func (asb *AnimatedStatusBar) AddItem(key, value, icon string, animation TextAnimation, style Style)

AddItem adds a status item with optional animation

func (*AnimatedStatusBar) Dimensions

func (asb *AnimatedStatusBar) Dimensions() (width, height int)

Dimensions returns the status bar dimensions

func (*AnimatedStatusBar) Draw

func (asb *AnimatedStatusBar) Draw(frame RenderFrame)

Draw renders the animated status bar

func (*AnimatedStatusBar) Position

func (asb *AnimatedStatusBar) Position() (x, y int)

Position returns the status bar position

func (*AnimatedStatusBar) SetPosition

func (asb *AnimatedStatusBar) SetPosition(x, y int)

SetPosition updates the status bar position

func (*AnimatedStatusBar) Update

func (asb *AnimatedStatusBar) Update(frame uint64)

Update updates the animation frame

func (*AnimatedStatusBar) UpdateItem

func (asb *AnimatedStatusBar) UpdateItem(index int, key, value string)

UpdateItem updates an existing status item

type AnimatedStatusItem

type AnimatedStatusItem struct {
	Key       string
	Value     string
	Icon      string
	Animation TextAnimation
	Style     Style
}

AnimatedStatusItem represents a single status item with animation

type AnimatedText

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

AnimatedText represents text with color animation

func NewAnimatedText

func NewAnimatedText(x, y int, text string, animation TextAnimation) *AnimatedText

NewAnimatedText creates a new animated text element

func (*AnimatedText) Dimensions

func (at *AnimatedText) Dimensions() (width, height int)

Dimensions returns the element's dimensions

func (*AnimatedText) Draw

func (at *AnimatedText) Draw(frame RenderFrame)

Draw renders the animated text

func (*AnimatedText) Position

func (at *AnimatedText) Position() (x, y int)

Position returns the element's position

func (*AnimatedText) SetPosition

func (at *AnimatedText) SetPosition(x, y int)

SetPosition updates the position

func (*AnimatedText) SetText

func (at *AnimatedText) SetText(text string)

SetText updates the text content

func (*AnimatedText) Update

func (at *AnimatedText) Update(frame uint64)

Update updates the animation frame

type Animation added in v0.0.2

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

Animation represents a time-based animation with duration, easing, and looping.

func NewAnimation added in v0.0.2

func NewAnimation(duration uint64) *Animation

NewAnimation creates a new animation with the given duration in frames.

func PresetAttention added in v0.0.2

func PresetAttention(duration uint64) *Animation

PresetAttention creates an attention-grabbing animation.

func PresetBounce added in v0.0.2

func PresetBounce(duration uint64) *Animation

PresetBounce creates a bouncing animation.

func PresetElastic added in v0.0.2

func PresetElastic(duration uint64) *Animation

PresetElastic creates an elastic animation.

func PresetFadeIn added in v0.0.2

func PresetFadeIn(duration uint64) *Animation

PresetFadeIn creates a fade-in animation.

func PresetFadeOut added in v0.0.2

func PresetFadeOut(duration uint64) *Animation

PresetFadeOut creates a fade-out animation.

func PresetPulse added in v0.0.2

func PresetPulse(duration uint64) *Animation

PresetPulse creates a pulsing animation.

func PresetSlideIn added in v0.0.2

func PresetSlideIn(duration uint64) *Animation

PresetSlideIn creates a slide-in animation.

func (*Animation) IsRunning added in v0.0.2

func (a *Animation) IsRunning() bool

IsRunning returns whether the animation is currently running.

func (*Animation) Lerp added in v0.0.2

func (a *Animation) Lerp(from, to float64) float64

Lerp performs linear interpolation between two values using the animation value.

func (*Animation) LerpRGB added in v0.0.2

func (a *Animation) LerpRGB(from, to RGB) RGB

LerpRGB performs linear interpolation between two RGB colors.

func (*Animation) Start added in v0.0.2

func (a *Animation) Start(frame uint64)

Start starts or restarts the animation.

func (*Animation) Stop added in v0.0.2

func (a *Animation) Stop()

Stop stops the animation.

func (*Animation) Update added in v0.0.2

func (a *Animation) Update(frame uint64)

Update updates the animation value based on the current frame.

func (*Animation) Value added in v0.0.2

func (a *Animation) Value() float64

Value returns the current animation value [0,1].

func (*Animation) WithEasing added in v0.0.2

func (a *Animation) WithEasing(easing Easing) *Animation

WithEasing sets the easing function for the animation.

func (*Animation) WithLoop added in v0.0.2

func (a *Animation) WithLoop(loop bool) *Animation

WithLoop enables looping for the animation.

func (*Animation) WithPingPong added in v0.0.2

func (a *Animation) WithPingPong(pingPong bool) *Animation

WithPingPong enables ping-pong looping (reverses direction on loop).

type AnimationBuilder added in v0.0.2

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

AnimationBuilder provides a fluent API for composing complex animations.

func NewAnimationBuilder added in v0.0.2

func NewAnimationBuilder() *AnimationBuilder

NewAnimationBuilder creates a new animation builder.

func (*AnimationBuilder) Add added in v0.0.2

func (ab *AnimationBuilder) Add(duration uint64, easing Easing) *AnimationBuilder

Add adds an animation to the builder.

func (*AnimationBuilder) AddAnimation added in v0.0.2

func (ab *AnimationBuilder) AddAnimation(anim *Animation) *AnimationBuilder

AddAnimation adds a pre-configured animation to the builder.

func (*AnimationBuilder) Build added in v0.0.2

func (ab *AnimationBuilder) Build() interface{}

Build constructs the final animation based on the mode.

func (*AnimationBuilder) Parallel added in v0.0.2

func (ab *AnimationBuilder) Parallel() *AnimationBuilder

Parallel sets the mode to parallel (animations play simultaneously).

func (*AnimationBuilder) Sequence added in v0.0.2

func (ab *AnimationBuilder) Sequence() *AnimationBuilder

Sequence sets the mode to sequence (animations play one after another).

func (*AnimationBuilder) Stagger added in v0.0.2

func (ab *AnimationBuilder) Stagger() *AnimationBuilder

Stagger sets the mode to staggered (each animation starts with a delay).

type AnimationController added in v0.0.2

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

AnimationController coordinates multiple animations and provides timing control. It can be used to create synchronized animations across different views.

func NewAnimationController added in v0.0.2

func NewAnimationController() *AnimationController

NewAnimationController creates a new animation controller.

func (*AnimationController) Get added in v0.0.2

func (ac *AnimationController) Get(name string) *Animation

Get retrieves a registered animation by name.

func (*AnimationController) GlobalTime added in v0.0.2

func (ac *AnimationController) GlobalTime() uint64

GlobalTime returns the current global frame counter.

func (*AnimationController) Register added in v0.0.2

func (ac *AnimationController) Register(name string, anim *Animation)

Register adds an animation to the controller with a unique name.

func (*AnimationController) Update added in v0.0.2

func (ac *AnimationController) Update(frame uint64)

Update updates all managed animations.

type AnimationCoordinator added in v0.0.2

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

AnimationCoordinator coordinates animations across multiple views.

func NewAnimationCoordinator added in v0.0.2

func NewAnimationCoordinator() *AnimationCoordinator

NewAnimationCoordinator creates a new animation coordinator.

func (*AnimationCoordinator) CurrentFrame added in v0.0.2

func (ac *AnimationCoordinator) CurrentFrame() uint64

CurrentFrame returns the current frame counter.

func (*AnimationCoordinator) GetEffect added in v0.0.2

func (ac *AnimationCoordinator) GetEffect(name string) *GlobalEffect

GetEffect retrieves a registered global effect.

func (*AnimationCoordinator) GetSubscribers added in v0.0.2

func (ac *AnimationCoordinator) GetSubscribers(effectName string) []AnimationSubscriber

GetSubscribers returns all subscribers for a given effect.

func (*AnimationCoordinator) RegisterEffect added in v0.0.2

func (ac *AnimationCoordinator) RegisterEffect(name string, effect *GlobalEffect)

RegisterEffect registers a global effect with a unique name.

func (*AnimationCoordinator) Subscribe added in v0.0.2

func (ac *AnimationCoordinator) Subscribe(effectName string, subscriber AnimationSubscriber)

Subscribe allows a view to subscribe to a global effect.

func (*AnimationCoordinator) Unsubscribe added in v0.0.2

func (ac *AnimationCoordinator) Unsubscribe(effectName string, subscriber AnimationSubscriber)

Unsubscribe removes a subscriber from a global effect.

func (*AnimationCoordinator) Update added in v0.0.2

func (ac *AnimationCoordinator) Update(frame uint64)

Update updates all global effects and notifies subscribers.

type AnimationGroup added in v0.0.2

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

AnimationGroup represents multiple animations that play simultaneously.

func NewAnimationGroup added in v0.0.2

func NewAnimationGroup(animations ...*Animation) *AnimationGroup

NewAnimationGroup creates a new animation group.

func (*AnimationGroup) CombineAverage added in v0.0.2

func (ag *AnimationGroup) CombineAverage() *AnimationGroup

CombineAverage averages all animation values.

func (*AnimationGroup) CombineMax added in v0.0.2

func (ag *AnimationGroup) CombineMax() *AnimationGroup

CombineMax takes the maximum of all animation values.

func (*AnimationGroup) CombineMin added in v0.0.2

func (ag *AnimationGroup) CombineMin() *AnimationGroup

CombineMin takes the minimum of all animation values.

func (*AnimationGroup) Start added in v0.0.2

func (ag *AnimationGroup) Start(frame uint64)

Start starts all animations in the group.

func (*AnimationGroup) Update added in v0.0.2

func (ag *AnimationGroup) Update(frame uint64)

Update updates all animations in the group.

func (*AnimationGroup) Value added in v0.0.2

func (ag *AnimationGroup) Value() float64

Value returns the combined value from all animations.

func (*AnimationGroup) WithCombiner added in v0.0.2

func (ag *AnimationGroup) WithCombiner(combiner func(values []float64) float64) *AnimationGroup

WithCombiner sets how to combine values from multiple animations.

type AnimationMode added in v0.0.2

type AnimationMode int

AnimationMode determines how multiple animations are combined.

const (
	AnimationModeSequence AnimationMode = iota // Play one after another
	AnimationModeParallel                      // Play simultaneously
	AnimationModeStagger                       // Start each with a delay
)

type AnimationSequence added in v0.0.2

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

AnimationSequence represents a sequence of animations that play one after another.

func NewAnimationSequence added in v0.0.2

func NewAnimationSequence(animations ...*Animation) *AnimationSequence

NewAnimationSequence creates a new animation sequence.

func (*AnimationSequence) IsComplete added in v0.0.2

func (as *AnimationSequence) IsComplete() bool

IsComplete returns whether all animations in the sequence have finished.

func (*AnimationSequence) Reset added in v0.0.2

func (as *AnimationSequence) Reset()

Reset resets the sequence to the beginning.

func (*AnimationSequence) Update added in v0.0.2

func (as *AnimationSequence) Update(frame uint64)

Update updates the current animation in the sequence.

func (*AnimationSequence) Value added in v0.0.2

func (as *AnimationSequence) Value() float64

Value returns the current animation value.

type AnimationStep added in v0.0.2

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

AnimationStep represents one step in a chained animation.

type AnimationSubscriber added in v0.0.2

type AnimationSubscriber interface {
	OnEffectUpdate(effectName string, value float64)
}

AnimationSubscriber represents a view that can subscribe to global effects.

type Application

type Application interface {
	// View returns the declarative view tree representing the current UI.
	// This is called automatically after each event is processed.
	View() View
}

Application is the main interface for declarative UI applications. Implementations provide a View that describes the current UI state.

The View method is called after every event to render the UI. The framework efficiently diffs the view tree and only updates changed terminal regions.

Thread Safety: View and HandleEvent (if implemented) are NEVER called concurrently. The Runtime ensures these methods run sequentially in a single goroutine, eliminating the need for locks in application code.

Example:

type CounterApp struct {
    count int
}

func (a *CounterApp) View() tui.View {
    return tui.Stack(
        tui.Text("Count: %d", a.count),
        tui.Button("Increment", func() { a.count++ }),
    )
}
Example

ExampleApplication demonstrates the basic Application interface for building declarative TUI applications.

package main

import ()

func main() {
	type CounterApp struct{}

	// View returns the UI tree based on current state
	// func (app *CounterApp) View() tui.View {
	// 	return tui.Stack(
	// 		tui.Text("Count: %d", app.count),
	// 		tui.Button("Increment", func() { app.count++ }),
	// 	)
	// }

	app := &CounterApp{}
	_ = app

	// Run the application
	// tui.Run(app)
}

type AutocompleteFunc added in v0.0.19

type AutocompleteFunc func(input string, cursorPos int) (completions []string, replaceFrom int)

AutocompleteFunc is called when the user presses Tab to get completion suggestions. It receives the current input and cursor position, and returns a list of completions and the index in the input string where replacement should start from.

Example for file completion:

func(input string, cursorPos int) ([]string, int) {
    // Find @ before cursor
    atIdx := strings.LastIndex(input[:cursorPos], "@")
    if atIdx == -1 {
        return nil, 0
    }
    prefix := input[atIdx+1 : cursorPos]
    matches := findFiles(prefix)
    return matches, atIdx
}

type BaseWidget

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

BaseWidget provides a default implementation of ComposableWidget that other widgets can embed to get composition support. This handles the boilerplate of bounds management, parent tracking, and visibility.

func NewBaseWidget

func NewBaseWidget() BaseWidget

NewBaseWidget creates a new BaseWidget with sensible defaults

func (*BaseWidget) ClearDirty

func (bw *BaseWidget) ClearDirty()

ClearDirty clears the dirty flag

func (*BaseWidget) Destroy

func (bw *BaseWidget) Destroy()

Destroy cleans up the widget (default: no-op)

func (*BaseWidget) GetBounds

func (bw *BaseWidget) GetBounds() image.Rectangle

GetBounds returns the widget's current bounds

func (*BaseWidget) GetLayoutParams

func (bw *BaseWidget) GetLayoutParams() LayoutParams

GetLayoutParams returns the layout parameters

func (*BaseWidget) GetMinSize

func (bw *BaseWidget) GetMinSize() image.Point

GetMinSize returns the minimum size (can be overridden)

func (*BaseWidget) GetParent

func (bw *BaseWidget) GetParent() ComposableWidget

GetParent returns the parent widget

func (*BaseWidget) GetPreferredSize

func (bw *BaseWidget) GetPreferredSize() image.Point

GetPreferredSize returns the preferred size (can be overridden)

func (*BaseWidget) Init

func (bw *BaseWidget) Init()

Init initializes the widget (default: no-op)

func (*BaseWidget) IsVisible

func (bw *BaseWidget) IsVisible() bool

IsVisible returns whether the widget is visible

func (*BaseWidget) MarkDirty

func (bw *BaseWidget) MarkDirty()

MarkDirty marks the widget as needing redraw

func (*BaseWidget) NeedsRedraw

func (bw *BaseWidget) NeedsRedraw() bool

NeedsRedraw returns whether the widget needs to be redrawn

func (*BaseWidget) SetBounds

func (bw *BaseWidget) SetBounds(bounds image.Rectangle)

SetBounds sets the widget's position and size

func (*BaseWidget) SetLayoutParams

func (bw *BaseWidget) SetLayoutParams(params LayoutParams)

SetLayoutParams sets the layout parameters

func (*BaseWidget) SetMinSize

func (bw *BaseWidget) SetMinSize(size image.Point)

SetMinSize sets the minimum size

func (*BaseWidget) SetParent

func (bw *BaseWidget) SetParent(parent ComposableWidget)

SetParent sets the parent widget

func (*BaseWidget) SetPreferredSize

func (bw *BaseWidget) SetPreferredSize(size image.Point)

SetPreferredSize sets the preferred size

func (*BaseWidget) SetVisible

func (bw *BaseWidget) SetVisible(visible bool)

SetVisible sets the widget's visibility

type BatchEvent

type BatchEvent struct {
	Time   time.Time
	Events []Event
}

BatchEvent contains multiple events that should be processed together. The runtime will unpack this and process each event individually in order.

This is typically returned by Sequence() to group related command results.

func (BatchEvent) Timestamp

func (e BatchEvent) Timestamp() time.Time

type BorderAnimation added in v0.0.2

type BorderAnimation interface {
	GetBorderStyle(frame uint64, borderPart BorderPart, position int, length int) Style
}

BorderAnimation defines how a border should be animated.

func PresetFireBorder added in v0.0.2

func PresetFireBorder(speed int) BorderAnimation

PresetFireBorder creates a fire border animation preset.

func PresetMarqueeBorder added in v0.0.2

func PresetMarqueeBorder(onColor, offColor RGB, speed, segmentLength int) BorderAnimation

PresetMarqueeBorder creates a marquee border animation preset.

func PresetPulsingBorder added in v0.0.2

func PresetPulsingBorder(color RGB, speed int) BorderAnimation

PresetPulsingBorder creates a pulsing border animation preset.

func PresetRainbowBorder added in v0.0.2

func PresetRainbowBorder(speed int, reversed bool) BorderAnimation

PresetRainbowBorder creates a rainbow border animation preset.

type BorderPart added in v0.0.2

type BorderPart int

BorderPart identifies which part of the border is being rendered.

const (
	BorderPartTop BorderPart = iota
	BorderPartRight
	BorderPartBottom
	BorderPartLeft
	BorderPartTopLeft
	BorderPartTopRight
	BorderPartBottomLeft
	BorderPartBottomRight
)

type BorderStyle

type BorderStyle = terminal.BorderStyle

BorderStyle defines the characters used for drawing borders

type Box

type Box = terminal.Box

Box is a simple box primitive

type CaseView

type CaseView[T comparable] struct {
	// contains filtered or unexported fields
}

CaseView represents a case in a Switch statement.

func Case

func Case[T comparable](value T, view View) CaseView[T]

Case creates a case for use with Switch.

func Default

func Default[T comparable](view View) CaseView[T]

Default creates a default case for use with Switch.

type Cell

type Cell = terminal.Cell

Cell represents a single character cell on the terminal

type ChainedAnimation added in v0.0.2

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

ChainedAnimation allows chaining different animation types together.

func NewChainedAnimation added in v0.0.2

func NewChainedAnimation() *ChainedAnimation

NewChainedAnimation creates a new chained animation.

func (*ChainedAnimation) AddBorderAnimation added in v0.0.2

func (ca *ChainedAnimation) AddBorderAnimation(anim BorderAnimation, duration uint64) *ChainedAnimation

AddBorderAnimation adds a border animation step.

func (*ChainedAnimation) AddTextAnimation added in v0.0.2

func (ca *ChainedAnimation) AddTextAnimation(anim TextAnimation, duration uint64) *ChainedAnimation

AddTextAnimation adds a text animation step.

func (*ChainedAnimation) AddTransform added in v0.0.2

func (ca *ChainedAnimation) AddTransform(transform func(View) View, duration uint64) *ChainedAnimation

AddTransform adds a view transformation step.

func (*ChainedAnimation) GetCurrentStep added in v0.0.2

func (ca *ChainedAnimation) GetCurrentStep(frame uint64) *AnimationStep

GetCurrentStep returns the current animation step based on frame.

type Cmd

type Cmd func() Event

Cmd is a function that performs async work and returns an event. Commands are executed in separate goroutines by the Runtime, and their results are sent back to the event loop as events.

This pattern enables non-blocking async operations (HTTP requests, timers, file I/O) while maintaining the single-threaded event loop guarantee for application state.

Example:

func fetchData() Cmd {
    return func() Event {
        data, err := http.Get("https://api.example.com/data")
        if err != nil {
            return ErrorEvent{Err: err, Cause: "fetch"}
        }
        return DataReceivedEvent{Data: data}
    }
}

func (a *App) HandleEvent(event Event) []Cmd {
    if _, ok := event.(KeyEvent); ok {
        return []Cmd{fetchData()}
    }
    return nil
}
Example

ExampleCmd demonstrates async command execution.

package main

import ()

func main() {
	// type DataEvent struct {
	// 	Time time.Time
	// 	Data string
	// }

	// Custom event type needs Timestamp method
	// func (e DataEvent) Timestamp() time.Time { return e.Time }

	// Command that performs async work
	// fetchData := func() tui.Cmd {
	// 	return func() tui.Event {
	// 		// Simulate async operation
	// 		data := "fetched data"
	// 		return DataEvent{Time: time.Now(), Data: data}
	// 	}
	// }

}

func After

func After(duration time.Duration, fn func()) Cmd

After returns a command that waits for the specified duration, executes the provided function, and returns a TickEvent. If fn is nil, it simply waits and returns a TickEvent.

func Batch

func Batch(cmds ...Cmd) []Cmd

Batch combines multiple commands into a single list. This is a convenience function for returning multiple commands from HandleEvent.

Example:

return Batch(
    fetchUserData(),
    fetchSettings(),
    startTimer(),
)
Example

ExampleBatch demonstrates running multiple commands.

package main

import (
	"fmt"

	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	cmd1 := func() tui.Cmd {
		return func() tui.Event {
			return tui.TickEvent{}
		}
	}

	cmd2 := func() tui.Cmd {
		return func() tui.Event {
			return tui.TickEvent{}
		}
	}

	// Run multiple commands in parallel
	cmds := tui.Batch(
		cmd1(),
		cmd2(),
	)

	fmt.Printf("Queued %d commands", len(cmds))
}
Output:

Queued 2 commands

func Focus

func Focus(id string) Cmd

Focus returns a command that sets focus to the specified element ID. The command produces a FocusSetEvent that the Runtime or InlineApp processes to update the focus state.

Use this in HandleEvent to programmatically focus an element:

func (app *App) HandleEvent(e Event) []Cmd {
    if key, ok := e.(KeyEvent); ok && key.Rune == 'n' {
        return []Cmd{Focus("name-input")}
    }
    return nil
}

The element ID must match the ID used when creating the focusable component (e.g., Input("id"), Button("id", ...), etc.).

func FocusNext

func FocusNext() Cmd

FocusNext returns a command that moves focus to the next element in Tab order. The command produces a FocusNextEvent that the Runtime or InlineApp processes. This is equivalent to pressing Tab.

func FocusPrev added in v0.0.2

func FocusPrev() Cmd

FocusPrev returns a command that moves focus to the previous element in Tab order. The command produces a FocusPrevEvent that the Runtime or InlineApp processes. This is equivalent to pressing Shift+Tab.

func None

func None() []Cmd

None returns an empty command list. This is a convenience function for when HandleEvent needs to return an empty slice explicitly (useful for readability).

func Quit

func Quit() Cmd

Quit returns a command that triggers application shutdown. The application will clean up and exit gracefully.

Example:

func (a *App) HandleEvent(event Event) []Cmd {
    if key, ok := event.(KeyEvent); ok && key.Rune == 'q' {
        return []Cmd{Quit()}
    }
    return nil
}

func Sequence

func Sequence(cmds ...Cmd) Cmd

Sequence returns a command that executes multiple commands in order, collecting their results into a BatchEvent.

Unlike Batch (which runs commands in parallel), Sequence runs them sequentially in a single goroutine. Use this when commands depend on each other or when parallel execution would cause issues.

Example:

return []Cmd{Sequence(
    authenticate(),
    fetchData(),
    processResults(),
)}

func Tick

func Tick(duration time.Duration) Cmd

Tick returns a command that waits for the specified duration and then returns a TickEvent.

type Color

type Color = terminal.Color

Color represents a terminal color

type ComposableWidget

type ComposableWidget interface {
	Widget // Embed basic Widget interface (Draw, HandleKey)

	// Lifecycle methods
	Init()    // Called when widget is first created or added to parent
	Destroy() // Called when widget is removed or screen is closing

	// Bounds management - enables relative positioning within containers
	SetBounds(bounds image.Rectangle) // Parent/container sets the widget's position and size
	GetBounds() image.Rectangle       // Returns current bounds
	GetMinSize() image.Point          // Returns minimum size needed to render properly
	GetPreferredSize() image.Point    // Returns preferred size (may be larger than minimum)

	// Parent-child tree structure for event propagation
	GetParent() ComposableWidget // Returns parent widget (nil for root)
	SetParent(ComposableWidget)  // Sets parent widget

	// Visibility and layout participation
	IsVisible() bool   // Whether widget should be rendered and included in layout
	SetVisible(bool)   // Show/hide the widget
	NeedsRedraw() bool // Whether widget needs to be redrawn
	MarkDirty()        // Mark widget as needing redraw
	ClearDirty()       // Clear dirty flag after redraw
}

ComposableWidget extends the basic Widget interface with composition capabilities. This interface enables bounds-based positioning, parent-child relationships, and lifecycle management necessary for complex, nested layouts.

Components implementing this interface can be nested inside containers and will have their bounds managed by layout managers instead of using absolute positioning.

type ConstraintLayoutManager

type ConstraintLayoutManager interface {
	LayoutManager // Embed legacy interface for compatibility

	// Measure calculates the size of the container given the constraints.
	// It should also measure children recursively.
	Measure(children []ComposableWidget, constraints SizeConstraints) image.Point

	// LayoutWithConstraints positions children.
	// It is similar to Layout but provides the constraints used during measurement.
	LayoutWithConstraints(children []ComposableWidget, constraints SizeConstraints, containerBounds image.Rectangle)
}

ConstraintLayoutManager is an enhanced LayoutManager that supports constraint-based layout.

type CornerHighlightAnimation added in v0.0.2

type CornerHighlightAnimation struct {
	Speed          int
	BaseColor      RGB
	HighlightColor RGB
	Duration       int // How long each corner stays highlighted (in frames)
}

CornerHighlightAnimation highlights corners in sequence.

func (*CornerHighlightAnimation) GetBorderStyle added in v0.0.2

func (c *CornerHighlightAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type CursorConfig added in v0.0.22

type CursorConfig struct {
	Output io.Writer // nil = os.Stdout. Specify where to write cursor commands.
}

CursorConfig configures cursor control functions. The zero value is acceptable and will use sensible defaults (os.Stdout for output).

type CursorStyle

type CursorStyle = terminal.CursorStyle

Mouse types from terminal

type DebugInfo

type DebugInfo struct {
	// FPS is the current frames per second.
	FPS float64

	// FrameTime is the time taken for the last frame.
	FrameTime time.Duration

	// FrameCount is the total number of frames rendered.
	FrameCount uint64

	// TerminalSize is the current terminal dimensions.
	TerminalSize image.Point

	// LastEvent is a description of the most recent event.
	LastEvent string

	// LastEventTime is when the last event occurred.
	LastEventTime time.Time

	// EventCount is the total number of events processed.
	EventCount uint64

	// Custom allows applications to add custom debug values.
	Custom map[string]string
}

DebugInfo holds runtime debug information.

func NewDebugInfo

func NewDebugInfo() *DebugInfo

NewDebugInfo creates a new DebugInfo instance.

func (*DebugInfo) Clear

func (d *DebugInfo) Clear(key string)

Clear removes a custom debug value.

func (*DebugInfo) Set

func (d *DebugInfo) Set(key, value string)

Set sets a custom debug value.

func (*DebugInfo) SetFPS

func (d *DebugInfo) SetFPS(fps float64)

SetFPS sets the FPS value.

func (*DebugInfo) SetFrameTime

func (d *DebugInfo) SetFrameTime(t time.Duration)

SetFrameTime sets the frame time.

func (*DebugInfo) Update

func (d *DebugInfo) Update(event Event)

Update updates the debug info based on an event. Call this from your HandleEvent to track events.

type DebugPosition

type DebugPosition int

DebugPosition specifies where the debug overlay appears.

const (
	DebugTopLeft DebugPosition = iota
	DebugTopRight
	DebugBottomLeft
	DebugBottomRight
)

type DebugWrapper

type DebugWrapper struct {
	Info *DebugInfo
	// contains filtered or unexported fields
}

DebugWrapper wraps an Application to automatically track debug info.

func WrapWithDebug

func WrapWithDebug(app Application) *DebugWrapper

WrapWithDebug wraps an application to automatically collect debug info.

Example:

wrapper := tui.WrapWithDebug(&MyApp{})
// Use wrapper.Info in your View to display debug overlay
tui.Run(wrapper)

func (*DebugWrapper) Enable

func (w *DebugWrapper) Enable(enabled bool)

Enable enables or disables debug tracking.

func (*DebugWrapper) HandleEvent

func (w *DebugWrapper) HandleEvent(event Event) []Cmd

HandleEvent implements EventHandler.

func (*DebugWrapper) View

func (w *DebugWrapper) View() View

View implements Application.

type Destroyable

type Destroyable interface {
	Destroy()
}

Destroyable is an optional interface that applications can implement to perform cleanup when the runtime stops.

type Diff

type Diff struct {
	Files []DiffFile
}

Diff represents a complete diff (may contain multiple files)

func ParseUnifiedDiff

func ParseUnifiedDiff(diffText string) (*Diff, error)

ParseUnifiedDiff parses a unified diff format string into a Diff structure

type DiffFile

type DiffFile struct {
	OldPath string // Path to old file (before change)
	NewPath string // Path to new file (after change)
	Hunks   []DiffHunk
}

DiffFile represents changes to a single file

type DiffHunk

type DiffHunk struct {
	OldStart int    // Starting line in old file
	OldCount int    // Number of lines in old file
	NewStart int    // Starting line in new file
	NewCount int    // Number of lines in new file
	Header   string // The @@ line content
	Lines    []DiffLine
}

DiffHunk represents a contiguous block of changes

type DiffLine

type DiffLine struct {
	Type       DiffLineType
	OldLineNum int    // Line number in old file (0 if added)
	NewLineNum int    // Line number in new file (0 if removed)
	Content    string // Line content without the leading +/- marker
	RawLine    string // Original line including marker
}

DiffLine represents a single line in a diff

type DiffLineType

type DiffLineType = unidiff.LineType

DiffLineType represents the type of a diff line

type DiffRenderer

type DiffRenderer struct {
	Theme           DiffTheme
	ShowLineNums    bool
	SyntaxHighlight bool
	LineNumWidth    int // Width reserved for line numbers
	TabWidth        int // Number of spaces to expand tabs to (default 4)
}

DiffRenderer renders diff content with syntax highlighting

func NewDiffRenderer

func NewDiffRenderer() *DiffRenderer

NewDiffRenderer creates a new diff renderer with default settings

func (*DiffRenderer) RenderDiff

func (dr *DiffRenderer) RenderDiff(diff *Diff, language string) []RenderedDiffLine

RenderDiff renders a diff to styled output

func (*DiffRenderer) WithTheme

func (dr *DiffRenderer) WithTheme(theme DiffTheme) *DiffRenderer

WithTheme sets a custom theme

type DiffTheme

type DiffTheme struct {
	AddedBg      RGB    // Background for added lines
	AddedFg      RGB    // Foreground for added lines
	RemovedBg    RGB    // Background for removed lines
	RemovedFg    RGB    // Foreground for removed lines
	ContextStyle Style  // Style for context lines
	HeaderStyle  Style  // Style for file headers
	HunkStyle    Style  // Style for hunk headers
	LineNumStyle Style  // Style for line numbers
	SyntaxTheme  string // Chroma theme for syntax highlighting
}

DiffTheme defines colors and styles for diff rendering

func DefaultDiffTheme

func DefaultDiffTheme() DiffTheme

DefaultDiffTheme returns a default diff theme

type DirtyRegion

type DirtyRegion = terminal.DirtyRegion

DirtyRegion tracks the rectangular area that has been modified

type Easing added in v0.0.2

type Easing func(t float64) float64

Easing represents an easing function that maps input [0,1] to output [0,1]. Easing functions control the rate of change over time for animations.

func EaseChain added in v0.0.2

func EaseChain(easings ...Easing) Easing

EaseChain chains multiple easing functions sequentially. Each function is applied to an equal portion of the total time.

func EaseClamp added in v0.0.2

func EaseClamp(easing Easing) Easing

EaseClamp ensures the output is clamped between 0 and 1.

func EaseMirror added in v0.0.2

func EaseMirror(easing Easing) Easing

EaseMirror creates a mirrored easing (goes up then down).

func EaseReverse added in v0.0.2

func EaseReverse(easing Easing) Easing

EaseReverse reverses an easing function (mirrors it).

func EaseScale added in v0.0.2

func EaseScale(easing Easing, scale float64) Easing

EaseScale scales the output of an easing function.

type ErrorEvent

type ErrorEvent struct {
	Time  time.Time
	Err   error
	Cause string // Optional description of what caused the error
}

ErrorEvent represents an error that occurred during async command execution. Applications should handle these events to display errors to users.

Example:

func (a *App) HandleEvent(event Event) []Cmd {
    if err, ok := event.(ErrorEvent); ok {
        a.errorMsg = err.Error()
    }
    return nil
}

func (ErrorEvent) Error

func (e ErrorEvent) Error() string

func (ErrorEvent) Timestamp

func (e ErrorEvent) Timestamp() time.Time

type Event

type Event interface {
	// Timestamp returns when the event occurred
	Timestamp() time.Time
}

Event represents any event that can occur in the application. All events must provide a timestamp indicating when they occurred.

The Runtime processes events sequentially in a single-threaded loop, calling HandleEvent (if implemented) and then View() to render updates.

Built-in event types include:

  • KeyEvent: Keyboard input
  • MouseEvent: Mouse clicks, movement, scrolling
  • TickEvent: Periodic timer for animations
  • ResizeEvent: Terminal size changed
  • QuitEvent: Application shutdown request
  • ErrorEvent: Error from async command
  • BatchEvent: Multiple events to process together

Applications can define custom event types for command results:

type DataReceivedEvent struct {
    Time time.Time
    Data []byte
}

func (e DataReceivedEvent) Timestamp() time.Time { return e.Time }

type EventHandler

type EventHandler interface {
	// HandleEvent processes an event and optionally returns commands for async execution.
	// This is called in a single-threaded event loop, so state can be mutated freely
	// without locks. Return commands for async operations (HTTP requests, timers, etc.).
	HandleEvent(event Event) []Cmd
}

EventHandler is an optional interface that applications can implement to handle events and trigger async operations.

Applications implementing both Application and EventHandler get full control: HandleEvent processes the event (potentially mutating state and returning commands), then View is called to render the updated UI.

Example:

func (a *App) HandleEvent(event Event) []Cmd {
    switch e := event.(type) {
    case KeyEvent:
        if e.Rune == 'q' {
            return []Cmd{Quit()}
        }
        if e.Rune == 'r' {
            a.loading = true
            return []Cmd{a.fetchData()}
        }
    case DataEvent:
        a.data = e.Data
        a.loading = false
    }
    return nil
}

type FireBorderAnimation added in v0.0.2

type FireBorderAnimation struct {
	Speed      int
	CoolColors []RGB // Cooler fire colors (base)
	HotColors  []RGB // Hotter fire colors (peaks)
}

FireBorderAnimation creates a flickering fire effect around the border.

func (*FireBorderAnimation) GetBorderStyle added in v0.0.2

func (f *FireBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type FlexAlignItems

type FlexAlignItems int

FlexAlignItems specifies how items are aligned along cross axis

const (
	FlexAlignItemsStart   FlexAlignItems = iota // Items aligned to start of cross axis
	FlexAlignItemsEnd                           // Items aligned to end of cross axis
	FlexAlignItemsCenter                        // Items centered on cross axis
	FlexAlignItemsStretch                       // Items stretched to fill cross axis
)

type FlexDirection

type FlexDirection int

FlexDirection specifies the direction in which flex items are placed

const (
	FlexRow    FlexDirection = iota // Horizontal, left to right
	FlexColumn                      // Vertical, top to bottom
)

type FlexJustify

type FlexJustify int

FlexJustify specifies how items are distributed along main axis

const (
	FlexJustifyStart        FlexJustify = iota // Items packed to start
	FlexJustifyEnd                             // Items packed to end
	FlexJustifyCenter                          // Items centered
	FlexJustifySpaceBetween                    // Even spacing between items
	FlexJustifySpaceAround                     // Even spacing around items
	FlexJustifySpaceEvenly                     // Equal spacing between and around items
)

type FlexLayout

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

FlexLayout implements a flexbox-style layout manager with support for flexible sizing, wrapping, and sophisticated alignment options.

This provides capabilities similar to CSS Flexbox: - Flexible item sizing with grow/shrink factors - Multiple justification options (start, end, center, space-between, etc.) - Cross-axis alignment - Support for row and column directions - Optional wrapping

func NewFlexLayout

func NewFlexLayout() *FlexLayout

NewFlexLayout creates a new flex layout with default settings

func (*FlexLayout) CalculateMinSize

func (fl *FlexLayout) CalculateMinSize(children []ComposableWidget) image.Point

CalculateMinSize returns minimum size needed for flex layout

func (*FlexLayout) CalculatePreferredSize

func (fl *FlexLayout) CalculatePreferredSize(children []ComposableWidget) image.Point

CalculatePreferredSize returns preferred size for flex layout

func (*FlexLayout) Layout

func (fl *FlexLayout) Layout(containerBounds image.Rectangle, children []ComposableWidget)

Layout positions children using flexbox algorithm

func (*FlexLayout) WithAlignItems

func (fl *FlexLayout) WithAlignItems(alignItems FlexAlignItems) *FlexLayout

WithAlignItems sets the cross axis alignment

func (*FlexLayout) WithDirection

func (fl *FlexLayout) WithDirection(direction FlexDirection) *FlexLayout

WithDirection sets the flex direction

func (*FlexLayout) WithJustify

func (fl *FlexLayout) WithJustify(justify FlexJustify) *FlexLayout

WithJustify sets the main axis justification

func (*FlexLayout) WithLineSpacing

func (fl *FlexLayout) WithLineSpacing(lineSpacing int) *FlexLayout

WithLineSpacing sets the spacing between wrapped lines

func (*FlexLayout) WithSpacing

func (fl *FlexLayout) WithSpacing(spacing int) *FlexLayout

WithSpacing sets the spacing between items

func (*FlexLayout) WithWrap

func (fl *FlexLayout) WithWrap(wrap FlexWrap) *FlexLayout

WithWrap sets the flex wrap behavior

type FlexWrap

type FlexWrap int

FlexWrap specifies whether flex items wrap to new lines

const (
	FlexNoWrap FlexWrap = iota // All items on one line
	FlexWrapOn                 // Items wrap to new lines as needed
)

type Flexible

type Flexible interface {
	// contains filtered or unexported methods
}

Flexible is implemented by views that can expand to fill available space. Views implementing Flexible will share remaining space proportionally based on their flex factor.

type FocusHandler added in v0.0.2

type FocusHandler struct {
	// ID is the unique identifier for this focusable element.
	ID string
	// OnKey is called when a key event occurs while this element is focused.
	// Return true to consume the event.
	OnKey func(event KeyEvent) bool
}

FocusHandler defines callbacks for custom focusable behavior.

type FocusManager added in v0.0.2

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

FocusManager manages focus state for all focusable elements. It handles Tab/Shift+Tab navigation and routes keyboard events.

Each Runtime or InlineApp instance has its own FocusManager, avoiding global state and allowing multiple independent TUI applications.

func NewFocusManager added in v0.0.22

func NewFocusManager() *FocusManager

NewFocusManager creates a new focus manager instance.

func (*FocusManager) Clear added in v0.0.2

func (fm *FocusManager) Clear()

Clear clears all registered focusables (called before each render).

func (*FocusManager) FocusNext added in v0.0.2

func (fm *FocusManager) FocusNext()

FocusNext moves focus to the next element in registration order.

func (*FocusManager) FocusPrev added in v0.0.2

func (fm *FocusManager) FocusPrev()

FocusPrev moves focus to the previous element in registration order.

func (*FocusManager) GetFocused added in v0.0.2

func (fm *FocusManager) GetFocused() Focusable

GetFocused returns the currently focused element, or nil if none.

func (*FocusManager) GetFocusedID added in v0.0.2

func (fm *FocusManager) GetFocusedID() string

GetFocusedID returns the ID of the currently focused element.

func (*FocusManager) HandleClick added in v0.0.2

func (fm *FocusManager) HandleClick(x, y int) bool

HandleClick checks if a click hit any focusable element and focuses it. Returns true if a focusable was clicked.

func (*FocusManager) HandleKey added in v0.0.2

func (fm *FocusManager) HandleKey(event KeyEvent) bool

HandleKey routes a key event to the focused element. Returns true if the event was handled.

func (*FocusManager) Register added in v0.0.2

func (fm *FocusManager) Register(f Focusable)

Register adds a focusable element to the manager. Elements are registered in render order, which determines Tab navigation order.

func (*FocusManager) SetFocus added in v0.0.2

func (fm *FocusManager) SetFocus(id string)

SetFocus sets focus to a specific element by ID.

type FocusNextEvent added in v0.0.22

type FocusNextEvent struct {
	Time time.Time // When the event was created
}

FocusNextEvent is produced by the FocusNext command and processed by the Runtime or InlineApp to move focus to the next element in Tab order.

func (FocusNextEvent) Timestamp added in v0.0.22

func (e FocusNextEvent) Timestamp() time.Time

Timestamp implements Event.

type FocusPrevEvent added in v0.0.22

type FocusPrevEvent struct {
	Time time.Time // When the event was created
}

FocusPrevEvent is produced by the FocusPrev command and processed by the Runtime or InlineApp to move focus to the previous element in Tab order.

func (FocusPrevEvent) Timestamp added in v0.0.22

func (e FocusPrevEvent) Timestamp() time.Time

Timestamp implements Event.

type FocusSetEvent added in v0.0.22

type FocusSetEvent struct {
	ID   string    // The ID of the element to focus
	Time time.Time // When the event was created
}

FocusSetEvent is produced by the Focus command and processed by the Runtime or InlineApp to set focus to a specific element. This event flows through the command/event system rather than directly manipulating focus state, allowing proper integration with the event loop.

func (FocusSetEvent) Timestamp added in v0.0.22

func (e FocusSetEvent) Timestamp() time.Time

Timestamp implements Event.

type Focusable

type Focusable interface {
	// FocusID returns a unique identifier for this focusable element.
	// This ID is used for programmatic focus control via Focus(id).
	FocusID() string

	// IsFocused returns whether this element currently has focus.
	IsFocused() bool

	// SetFocused is called when focus changes to or from this element.
	SetFocused(focused bool)

	// HandleKeyEvent processes a key event while this element has focus.
	// Returns true if the event was consumed and should not propagate.
	HandleKeyEvent(event KeyEvent) bool

	// FocusBounds returns the screen bounds of this focusable element.
	// Used for mouse click focus detection.
	FocusBounds() image.Rectangle
}

Focusable is implemented by views that can receive keyboard focus. Views implementing this interface participate in Tab navigation and can handle keyboard events when focused.

type Footer struct {
	Left        string
	Center      string
	Right       string
	Style       Style
	Background  Style
	Border      bool
	BorderStyle BorderStyle
	Height      int
	StatusBar   bool
	StatusItems []StatusItem
}

Footer represents the bottom section of the terminal

func SimpleFooter

func SimpleFooter(left, center, right string, style Style) *Footer

SimpleFooter creates a simple footer

func StatusBarFooter

func StatusBarFooter(items []StatusItem) *Footer

StatusBarFooter creates a status bar footer

type Frame

type Frame = terminal.Frame

Frame is a bordered frame/box

type GlitchAnimation added in v0.0.2

type GlitchAnimation struct {
	Speed       int // Base speed for glitch timing
	BaseColor   RGB
	GlitchColor RGB // Color during glitch
	Intensity   int // How often glitches occur (1-10, higher = more glitches)
}

GlitchAnimation creates a cyberpunk-style digital glitch effect

func Glitch added in v0.0.7

func Glitch(speed int, baseColor, glitchColor RGB) *GlitchAnimation

Glitch creates a new glitch animation with a cyberpunk-style digital effect. Speed controls glitch timing (lower = faster).

Example:

Text("SYSTEM ERROR").Animate(Glitch(2, gray, red))
Text("SYSTEM ERROR").Animate(Glitch(2, gray, red).WithIntensity(8))

func (*GlitchAnimation) GetStyle added in v0.0.2

func (g *GlitchAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*GlitchAnimation) WithIntensity added in v0.0.7

func (g *GlitchAnimation) WithIntensity(intensity int) *GlitchAnimation

WithIntensity sets how often glitches occur (1-10, higher = more glitches).

type GlobalEffect added in v0.0.2

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

GlobalEffect represents a global animation effect that views can subscribe to.

func NewGlobalEffect added in v0.0.2

func NewGlobalEffect(effectType GlobalEffectType, duration uint64) *GlobalEffect

NewGlobalEffect creates a new global effect.

func (*GlobalEffect) ColorValue added in v0.0.2

func (ge *GlobalEffect) ColorValue() RGB

ColorValue returns the current color value (for color effects).

func (*GlobalEffect) Start added in v0.0.2

func (ge *GlobalEffect) Start(frame uint64)

Start starts the global effect.

func (*GlobalEffect) Type added in v0.0.2

func (ge *GlobalEffect) Type() GlobalEffectType

Type returns the effect type.

func (*GlobalEffect) Update added in v0.0.2

func (ge *GlobalEffect) Update(frame uint64)

Update updates the global effect.

func (*GlobalEffect) Value added in v0.0.2

func (ge *GlobalEffect) Value() float64

Value returns the current effect value.

func (*GlobalEffect) WithColorValue added in v0.0.2

func (ge *GlobalEffect) WithColorValue(color RGB) *GlobalEffect

WithColorValue sets a color value for color-based effects.

func (*GlobalEffect) WithEasing added in v0.0.2

func (ge *GlobalEffect) WithEasing(easing Easing) *GlobalEffect

WithEasing sets the easing function for the effect.

func (*GlobalEffect) WithWaveParams added in v0.0.2

func (ge *GlobalEffect) WithWaveParams(amplitude, frequency float64) *GlobalEffect

WithWaveParams sets wave parameters for wave-based effects.

type GlobalEffectType added in v0.0.2

type GlobalEffectType int

GlobalEffectType defines the type of global effect.

const (
	GlobalEffectBrightness GlobalEffectType = iota
	GlobalEffectColorShift
	GlobalEffectWave
	GlobalEffectPulse
	GlobalEffectShake
)

type GradientBorderAnimation added in v0.0.2

type GradientBorderAnimation struct {
	Speed    int
	Colors   []RGB
	Reversed bool
}

GradientBorderAnimation creates a gradient that rotates around the border.

func (*GradientBorderAnimation) GetBorderStyle added in v0.0.2

func (g *GradientBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type HBoxLayout

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

HBoxLayout arranges children horizontally in a row, left to right. Each child gets its preferred/minimum width, and height fills the container.

func NewHBoxLayout

func NewHBoxLayout(spacing int) *HBoxLayout

NewHBoxLayout creates a new horizontal box layout with the specified spacing

func (*HBoxLayout) CalculateMinSize

func (hb *HBoxLayout) CalculateMinSize(children []ComposableWidget) image.Point

CalculateMinSize returns the minimum size needed for all children

func (*HBoxLayout) CalculatePreferredSize

func (hb *HBoxLayout) CalculatePreferredSize(children []ComposableWidget) image.Point

CalculatePreferredSize returns the preferred size for all children

func (*HBoxLayout) Layout

func (hb *HBoxLayout) Layout(containerBounds image.Rectangle, children []ComposableWidget)

Layout positions children horizontally within the container bounds

func (*HBoxLayout) LayoutWithConstraints

func (hb *HBoxLayout) LayoutWithConstraints(children []ComposableWidget, constraints SizeConstraints, containerBounds image.Rectangle)

LayoutWithConstraints implements ConstraintLayoutManager.LayoutWithConstraints

func (*HBoxLayout) Measure

func (hb *HBoxLayout) Measure(children []ComposableWidget, constraints SizeConstraints) image.Point

Measure implements ConstraintLayoutManager.Measure

func (*HBoxLayout) WithAlignment

func (hb *HBoxLayout) WithAlignment(align LayoutAlignment) *HBoxLayout

WithAlignment sets the vertical alignment for children

func (*HBoxLayout) WithDistribute

func (hb *HBoxLayout) WithDistribute(distribute bool) *HBoxLayout

WithDistribute enables distributing extra horizontal space among children

type Header struct {
	Left        string
	Center      string
	Right       string
	Style       Style
	Background  Style
	Border      bool
	BorderStyle BorderStyle
	Height      int
}

Header represents the top section of the terminal

func BorderedHeader

func BorderedHeader(title string, style Style) *Header

BorderedHeader creates a bordered header

func SimpleHeader

func SimpleHeader(title string, style Style) *Header

SimpleHeader creates a simple header with title

type Hyperlink = terminal.Hyperlink

Hyperlink represents a clickable hyperlink in the terminal

type Initializable

type Initializable interface {
	Init() error
}

Initializable is an optional interface that applications can implement to perform initialization before the event loop starts.

type InlineApp added in v0.0.19

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

InlineApp manages the runtime for inline terminal applications. It provides the same event-driven architecture as tui.Run() but renders inline rather than taking over the screen.

Architecture:

  • Goroutine 1: Main event loop (processes events sequentially, calls HandleEvent/LiveView)
  • Goroutine 2: Input reader (blocks on stdin, sends KeyEvents)
  • Goroutine 3: Command executor (runs async commands, sends results as events)

This design eliminates race conditions in application code while maintaining responsive UI through non-blocking async operations.

func NewInlineApp added in v0.0.19

func NewInlineApp(cfgs ...InlineAppConfig) *InlineApp

NewInlineApp creates a new inline application runner.

Example:

runner := tui.NewInlineApp(tui.InlineAppConfig{
    Width:          80,
    BracketedPaste: true,
})
if err := runner.Run(&MyApp{}); err != nil {
    log.Fatal(err)
}

func (*InlineApp) ClearScrollback added in v0.0.19

func (r *InlineApp) ClearScrollback()

ClearScrollback clears the terminal scrollback history. This sends the ANSI escape sequence to clear the scrollback buffer. Safe to call from HandleEvent.

func (*InlineApp) Print added in v0.0.19

func (r *InlineApp) Print(view View)

Print renders a View to the scrollback history. The view can be any tui.View: Text, Stack, Group, Bordered, etc.

This method temporarily clears the live region, prints the view to scrollback, and restores the live region below.

Thread-safe: Can be called from HandleEvent (recommended) or from a Cmd goroutine via the app reference.

func (*InlineApp) Printf added in v0.0.19

func (r *InlineApp) Printf(format string, args ...any)

Printf is a convenience method that prints formatted text to scrollback. Equivalent to: r.Print(tui.Text(format, args...))

func (*InlineApp) Run added in v0.0.19

func (r *InlineApp) Run(app any) error

Run starts the inline application and blocks until it exits. This is the main entry point, similar to tui.Run().

The app parameter must implement InlineApplication (LiveView method). Optionally implement EventHandler, Initializable, Destroyable.

Lifecycle:

  1. Call Init() if implemented
  2. Enter raw mode, enable configured terminal features
  3. Start event loop, input reader, and command executor goroutines
  4. Render initial LiveView()
  5. Process events until Quit command
  6. Restore terminal state
  7. Call Destroy() if implemented

func (*InlineApp) SendEvent added in v0.0.19

func (r *InlineApp) SendEvent(event Event)

SendEvent sends an event to the application's event loop. This is useful for custom event sources or testing. It's safe to call from any goroutine.

func (*InlineApp) Stop added in v0.0.19

func (r *InlineApp) Stop()

Stop gracefully stops the inline application by sending a QuitEvent. This can be called from any goroutine.

type InlineAppConfig added in v0.0.22

type InlineAppConfig struct {
	Width          int       // 0 = auto (terminal width or 80). Rendering width.
	Output         io.Writer // nil = os.Stdout. Where to write output.
	Input          io.Reader // nil = os.Stdin. Where to read input.
	FPS            int       // 0 = no ticks. Frames per second for TickEvents.
	MouseTracking  bool      // Enable mouse event tracking.
	BracketedPaste bool      // Enable bracketed paste mode.
	PasteTabWidth  int       // 0 = preserve tabs. Convert tabs to N spaces in pastes.
	KittyKeyboard  bool      // Enable Kitty keyboard protocol.
}

InlineAppConfig configures an InlineApp. All fields are optional with sensible zero-value defaults.

type InlineApplication added in v0.0.19

type InlineApplication interface {
	// LiveView returns the view for the live region.
	// Called after each event is processed to re-render the live region.
	// The view should represent the current state of the interactive area.
	LiveView() View
}

InlineApplication is the interface for inline applications. Applications implementing this interface render a live region that updates in place, while supporting scrollback output via the runner's Print() method.

Thread Safety: LiveView and HandleEvent (if implemented) are NEVER called concurrently. The InlineApp ensures these methods run sequentially in a single goroutine, eliminating the need for locks in application code.

Optional Interfaces:

  • EventHandler: Implement HandleEvent(Event) []Cmd for event handling
  • Initializable: Implement Init() error for setup before the event loop
  • Destroyable: Implement Destroy() for cleanup after the event loop

type InputCursorStyle added in v0.0.2

type InputCursorStyle int

InputCursorStyle represents different cursor rendering styles for text input

const (
	// InputCursorBlock renders a block cursor (default)
	InputCursorBlock InputCursorStyle = iota
	// InputCursorUnderline renders an underline cursor
	InputCursorUnderline
	// InputCursorBar renders a vertical bar/beam cursor
	InputCursorBar
)

type InputSource added in v0.0.2

type InputSource interface {
	ReadEvent() (Event, error)
	SetPasteTabWidth(int)
}

InputSource abstracts the source of input events.

type Key

type Key = terminal.Key

Key represents special keyboard keys

type KeyDecoder

type KeyDecoder = terminal.KeyDecoder

KeyDecoder handles key event decoding from a byte stream

type KeyEvent

type KeyEvent = terminal.KeyEvent

KeyEvent is a keyboard event from the terminal package

type Layout

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

Layout manages the overall terminal layout with header, footer, and content area

func NewLayout

func NewLayout(terminal *Terminal) *Layout

NewLayout creates a new layout manager

func (*Layout) Close

func (l *Layout) Close() error

Close stops any background refresh goroutines and releases resources

func (*Layout) ContentArea

func (l *Layout) ContentArea() (y, height int)

ContentArea returns the Y position and height of the content area

func (*Layout) DisableAutoRefresh

func (l *Layout) DisableAutoRefresh()

DisableAutoRefresh stops automatic refresh

func (*Layout) Draw

func (l *Layout) Draw()

Draw renders the entire layout

func (*Layout) DrawTo

func (l *Layout) DrawTo(frame RenderFrame)

DrawTo renders the layout to the provided frame

func (*Layout) EnableAutoRefresh

func (l *Layout) EnableAutoRefresh(interval time.Duration) *Layout

EnableAutoRefresh enables automatic refresh of header/footer

func (*Layout) PrintInContent

func (l *Layout) PrintInContent(text string)

PrintInContent prints text in the content area

func (*Layout) Refresh

func (l *Layout) Refresh()

Refresh updates the header and footer

func (*Layout) SetFooter

func (l *Layout) SetFooter(footer *Footer) *Layout

SetFooter configures the footer

func (*Layout) SetHeader

func (l *Layout) SetHeader(header *Header) *Layout

SetHeader configures the header

type LayoutAlignment

type LayoutAlignment int

LayoutAlignment specifies how content should be aligned within available space

const (
	LayoutAlignStart   LayoutAlignment = iota // Align to top (vertical) or left (horizontal)
	LayoutAlignCenter                         // Center content
	LayoutAlignEnd                            // Align to bottom (vertical) or right (horizontal)
	LayoutAlignStretch                        // Stretch to fill available space
)

type LayoutManager

type LayoutManager interface {
	// Layout positions and sizes all children within the container's bounds.
	// The container bounds represent the available space for children.
	// This method should call SetBounds() on each child widget.
	Layout(containerBounds image.Rectangle, children []ComposableWidget)

	// CalculateMinSize determines the minimum size needed to layout all children.
	// This enables containers to know their own minimum size based on their contents.
	CalculateMinSize(children []ComposableWidget) image.Point

	// CalculatePreferredSize determines the preferred size for the container.
	// This may be larger than minimum if children want more space.
	CalculatePreferredSize(children []ComposableWidget) image.Point
}

LayoutManager defines strategies for positioning and sizing child widgets within a container. Different implementations provide different layout behaviors (vertical stack, horizontal stack, grid, flexbox-style, etc.).

type LayoutParams

type LayoutParams struct {
	// Flex/grow factor - how much extra space this widget should take relative to siblings
	// 0 means don't grow, 1 means grow proportionally with others, 2 means grow twice as much, etc.
	Grow int

	// Shrink factor - how much this widget should shrink when space is constrained
	// 0 means don't shrink, 1 means shrink proportionally
	Shrink int

	// Alignment within the container
	Align LayoutAlignment

	// Margin around the widget (space outside the widget)
	MarginTop    int
	MarginRight  int
	MarginBottom int
	MarginLeft   int

	// Padding inside the widget (space inside borders, outside content)
	PaddingTop    int
	PaddingRight  int
	PaddingBottom int
	PaddingLeft   int

	// Size constraints
	Constraints SizeConstraints
}

LayoutParams holds layout-specific parameters that widgets can use to influence how their parent container positions them.

func DefaultLayoutParams

func DefaultLayoutParams() LayoutParams

DefaultLayoutParams returns sensible defaults for layout parameters

type List

type List struct {
	BaseWidget
	Items         []ListItem
	SelectedIndex int
	ScrollOffset  int

	// Styles
	NormalStyle   Style
	SelectedStyle Style

	// Callbacks
	OnSelect func(item ListItem)
}

List is a scrollable list widget

func NewList

func NewList(items []ListItem) *List

NewList creates a new list widget

func (*List) Draw

func (l *List) Draw(frame RenderFrame)

Draw renders the list

func (*List) GetSelected

func (l *List) GetSelected() *ListItem

GetSelected returns the currently selected item

func (*List) HandleKey

func (l *List) HandleKey(event KeyEvent) bool

HandleKey handles key events

func (*List) HandleMouse

func (l *List) HandleMouse(event MouseEvent) bool

HandleMouse handles mouse events

func (*List) SelectNext

func (l *List) SelectNext()

SelectNext selects the next item

func (*List) SelectPrev

func (l *List) SelectPrev()

SelectPrev selects the previous item

func (*List) SetItems

func (l *List) SetItems(items []ListItem)

SetItems updates the list items

func (*List) SetStyles

func (l *List) SetStyles(normal, selected Style)

SetStyles sets the styles for the list

type ListItem

type ListItem struct {
	Label string
	Value interface{}
	Icon  string // Optional icon/prefix
}

ListItem represents an item in a list

type ListItemRenderer added in v0.0.2

type ListItemRenderer func(item ListItem, selected bool) View

ListItemRenderer is a function that renders a list item. It receives the item, whether it's selected, and returns a View.

type LivePrinter added in v0.0.2

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

LivePrinter renders views to a fixed region of the terminal that updates in place. This is useful for progress bars, status displays, and loading indicators that need to update without scrolling the terminal.

Rendering Optimization

LivePrinter uses several techniques to minimize flicker and improve performance:

  • Line-level diffing: Each frame is compared line-by-line against the previous frame. Only lines that actually changed are redrawn. This is similar to how the Terminal package uses cell-level diffing, but operates at a coarser granularity for simpler inline updates.

  • Synchronized output mode: Updates are wrapped in DEC private mode 2026 escape sequences (\033[?2026h ... \033[?2026l). This tells the terminal to buffer all changes and render them atomically, eliminating partial-frame flicker. Supported by most modern terminals (iTerm2, kitty, alacritty, Windows Terminal, WezTerm, foot). Terminals that don't support it simply ignore the escape sequences.

  • Cursor hiding: The cursor is hidden during updates and restored on Stop().

Thread Safety

LivePrinter is NOT thread-safe. All calls to Update, Clear, and Stop should be made from the same goroutine, or protected by external synchronization. For thread-safe live updates, use InlineApp which handles synchronization.

Example

live := tui.NewLivePrinter(tui.PrintConfig{Width: 60})
defer live.Stop()

for i := 0; i <= 100; i++ {
    live.Update(tui.Text("Progress: %d%%", i))
    time.Sleep(50 * time.Millisecond)
}

func NewLivePrinter added in v0.0.2

func NewLivePrinter(cfgs ...PrintConfig) *LivePrinter

NewLivePrinter creates a new LivePrinter for updating a region in place.

func (*LivePrinter) Clear added in v0.0.2

func (lp *LivePrinter) Clear()

Clear removes the live region content and resets state.

func (*LivePrinter) SetWidth added in v0.0.24

func (lp *LivePrinter) SetWidth(w int)

SetWidth updates the width of the live printer. This is useful when the terminal is resized. The next Update() will use the new width.

func (*LivePrinter) Stop added in v0.0.2

func (lp *LivePrinter) Stop()

Stop finalizes the live region, moving the cursor below the content and restoring cursor visibility.

func (*LivePrinter) Update added in v0.0.2

func (lp *LivePrinter) Update(view View) error

Update renders a new view, replacing the previous content in place. The cursor moves back to overwrite the previous output.

Optimization: Uses line-level diffing to only update lines that changed, similar to how the Terminal package uses cell-level diffing. This reduces the amount of data written and minimizes flicker.

func (*LivePrinter) UpdateNoSync added in v0.0.19

func (lp *LivePrinter) UpdateNoSync(view View) error

UpdateNoSync is like Update but without synchronized output mode wrapping. This is useful when the caller is already managing sync mode to avoid nested escape sequences.

Use cases:

  • InlineApp.Print() uses this to atomically clear, print, and re-render
  • Batch operations that need to wrap multiple updates in a single sync block

Most users should use Update() instead, which handles sync mode automatically.

func (*LivePrinter) UpdateWithFocus added in v0.0.22

func (lp *LivePrinter) UpdateWithFocus(view View, fm *FocusManager) error

UpdateWithFocus renders a view with focus management support. The focus manager is passed through the render context to focusable components.

type MarkdownRenderer

type MarkdownRenderer struct {
	Theme    MarkdownTheme
	MaxWidth int // Maximum width for text wrapping (0 = no limit)
	TabWidth int // Width of tab character in spaces
	// contains filtered or unexported fields
}

MarkdownRenderer renders markdown content to styled terminal output

func NewMarkdownRenderer

func NewMarkdownRenderer() *MarkdownRenderer

NewMarkdownRenderer creates a new markdown renderer with the default theme

func (*MarkdownRenderer) Render

func (mr *MarkdownRenderer) Render(markdown string) (*RenderedMarkdown, error)

Render parses and renders markdown content

func (*MarkdownRenderer) WithMaxWidth

func (mr *MarkdownRenderer) WithMaxWidth(width int) *MarkdownRenderer

WithMaxWidth sets the maximum width for text wrapping

func (*MarkdownRenderer) WithTheme

func (mr *MarkdownRenderer) WithTheme(theme MarkdownTheme) *MarkdownRenderer

WithTheme sets a custom theme for the renderer

type MarkdownTheme

type MarkdownTheme struct {
	// Heading styles by level (1-6)
	H1Style Style
	H2Style Style
	H3Style Style
	H4Style Style
	H5Style Style
	H6Style Style

	// Text styles
	BoldStyle          Style
	ItalicStyle        Style
	CodeStyle          Style
	LinkStyle          Style
	StrikethroughStyle Style

	// Block styles
	BlockQuoteStyle Style
	CodeBlockStyle  Style

	// List styles
	BulletChar string
	NumberFmt  string // Format string for numbered lists, e.g., "%d. "

	// Other
	HorizontalRuleChar  string
	HorizontalRuleStyle Style

	// Syntax highlighting theme name for code blocks
	SyntaxTheme string

	// Table styles
	TableBorderStyle Style
	TableHeaderStyle Style
	TableCellStyle   Style
}

MarkdownTheme defines the colors and styles for markdown rendering

func DefaultMarkdownTheme

func DefaultMarkdownTheme() MarkdownTheme

DefaultMarkdownTheme returns a default markdown theme with good contrast

type MarqueeBorderAnimation added in v0.0.2

type MarqueeBorderAnimation struct {
	Speed         int
	OnColor       RGB
	OffColor      RGB
	SegmentLength int  // Length of each on/off segment
	Reversed      bool // Direction of movement
}

MarqueeBorderAnimation creates a moving pattern around the border.

func (*MarqueeBorderAnimation) GetBorderStyle added in v0.0.2

func (m *MarqueeBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type Measurable

type Measurable interface {
	ComposableWidget
	// Measure calculates the desired size of the widget given the constraints.
	// The returned size should respect the constraints (be within min/max).
	Measure(constraints SizeConstraints) image.Point
}

Measurable interface allows widgets to participate in a constraint-based layout pass. This is the "first pass" of a Flutter-style layout system, where parents pass constraints down to children, and children return their desired size.

type MetricsSnapshot

type MetricsSnapshot = terminal.MetricsSnapshot

MetricsSnapshot is a point-in-time snapshot of rendering metrics

type MouseAware

type MouseAware interface {
	HandleMouse(event MouseEvent) bool
}

MouseAware is an optional interface for widgets that handle mouse events

type MouseButton

type MouseButton = terminal.MouseButton

Mouse types from terminal

type MouseEvent

type MouseEvent = terminal.MouseEvent

MouseEvent is a mouse event from the terminal package

type MouseEventType

type MouseEventType = terminal.MouseEventType

Mouse types from terminal

type MouseHandler

type MouseHandler struct {

	// Configuration
	DoubleClickThreshold time.Duration
	TripleClickThreshold time.Duration
	ClickMoveThreshold   int
	DragStartThreshold   int
	// contains filtered or unexported fields
}

MouseHandler manages mouse regions and events with advanced features. This is a higher-level UI component that tracks regions, handles click detection, drag state, and dispatches events to handlers.

func NewMouseHandler

func NewMouseHandler() *MouseHandler

NewMouseHandler creates a new mouse handler

func (*MouseHandler) AddRegion

func (h *MouseHandler) AddRegion(region *MouseRegion)

AddRegion adds a clickable region

func (*MouseHandler) CancelDrag

func (h *MouseHandler) CancelDrag()

CancelDrag cancels an active drag (e.g., on Escape key)

func (*MouseHandler) ClearRegions

func (h *MouseHandler) ClearRegions()

ClearRegions removes all regions

func (*MouseHandler) DisableDebug

func (h *MouseHandler) DisableDebug()

DisableDebug disables debug logging

func (*MouseHandler) EnableDebug

func (h *MouseHandler) EnableDebug()

EnableDebug enables debug logging for mouse events

func (*MouseHandler) HandleEvent

func (h *MouseHandler) HandleEvent(event *MouseEvent)

HandleEvent processes a mouse event with full state machine

func (*MouseHandler) RemoveRegion

func (h *MouseHandler) RemoveRegion(region *MouseRegion)

RemoveRegion removes a specific region

type MouseModifiers

type MouseModifiers = terminal.MouseModifiers

Mouse types from terminal

type MouseRegion

type MouseRegion = terminal.MouseRegion

MouseRegion is a clickable region from the terminal package

type PasswordInput

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

PasswordInput provides secure password input with advanced features. It supports: - iTerm2 secure input mode (prevents keylogging) - VS Code terminal integration - Clipboard disable during input - Memory zeroing on completion - Show/hide toggle - Paste confirmation

func NewPasswordInput

func NewPasswordInput(terminal *Terminal) *PasswordInput

NewPasswordInput creates a new secure password input handler.

func (*PasswordInput) ConfirmPaste

func (p *PasswordInput) ConfirmPaste(confirm bool) *PasswordInput

ConfirmPaste controls whether to require confirmation before accepting pasted content.

func (*PasswordInput) DisableClipboard

func (p *PasswordInput) DisableClipboard(disable bool) *PasswordInput

DisableClipboard controls whether to disable clipboard access during password input.

func (*PasswordInput) EnableSecureMode

func (p *PasswordInput) EnableSecureMode(enable bool) *PasswordInput

EnableSecureMode controls whether to enable terminal-specific secure input modes. When enabled (default), sends escape codes to iTerm2/VS Code to prevent keylogging.

func (*PasswordInput) Read

func (p *PasswordInput) Read() (*SecureString, error)

Read reads a password with enhanced security features. The password is stored in a byte slice that is zeroed after use.

Example:

pwdInput := tui.NewPasswordInput(terminal)
pwdInput.WithPrompt("Enter password: ", tui.NewStyle())
password, err := pwdInput.Read()
if err != nil {
    return err
}
defer password.Clear() // Zero memory when done
// Use password...

func (*PasswordInput) ShowCharacters

func (p *PasswordInput) ShowCharacters(show bool) *PasswordInput

ShowCharacters enables showing masked characters (*) instead of no echo. This provides visual feedback while maintaining security.

func (*PasswordInput) WithMaskChar

func (p *PasswordInput) WithMaskChar(char rune) *PasswordInput

WithMaskChar sets the character used to mask password input. Only used when ShowCharacters is enabled.

func (*PasswordInput) WithMaxLength

func (p *PasswordInput) WithMaxLength(length int) *PasswordInput

WithMaxLength sets the maximum password length.

func (*PasswordInput) WithPlaceholder

func (p *PasswordInput) WithPlaceholder(placeholder string) *PasswordInput

WithPlaceholder sets placeholder text.

func (*PasswordInput) WithPrompt

func (p *PasswordInput) WithPrompt(prompt string, style Style) *PasswordInput

WithPrompt sets the password prompt.

type PasteDisplayMode

type PasteDisplayMode = terminal.PasteDisplayMode

Paste types

type PasteHandler

type PasteHandler = terminal.PasteHandler

Paste types

type PasteHandlerDecision

type PasteHandlerDecision = terminal.PasteHandlerDecision

Paste types

type PasteInfo

type PasteInfo = terminal.PasteInfo

Paste types

type PlaybackController

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

PlaybackController manages playback of recorded sessions

func LoadRecording

func LoadRecording(filename string) (*PlaybackController, error)

LoadRecording loads a .cast file and returns a playback controller

func (*PlaybackController) GetDuration

func (p *PlaybackController) GetDuration() float64

GetDuration returns the total duration of the recording in seconds

func (*PlaybackController) GetHeader

func (p *PlaybackController) GetHeader() RecordingHeader

GetHeader returns the recording metadata

func (*PlaybackController) GetPosition

func (p *PlaybackController) GetPosition() float64

GetPosition returns the current playback position in seconds

func (*PlaybackController) GetProgress

func (p *PlaybackController) GetProgress() float64

GetProgress returns playback progress as a value between 0.0 and 1.0

func (*PlaybackController) IsPaused

func (p *PlaybackController) IsPaused() bool

IsPaused returns true if playback is paused

func (*PlaybackController) Pause

func (p *PlaybackController) Pause()

Pause pauses playback

func (*PlaybackController) Play

func (p *PlaybackController) Play(terminal *Terminal) error

Play starts playback of the recording

func (*PlaybackController) Resume

func (p *PlaybackController) Resume()

Resume resumes playback

func (*PlaybackController) Seek

func (p *PlaybackController) Seek(seconds float64)

Seek jumps to a specific time offset in the recording

func (*PlaybackController) SetLoop

func (p *PlaybackController) SetLoop(loop bool)

SetLoop enables or disables looping

func (*PlaybackController) SetSpeed

func (p *PlaybackController) SetSpeed(speed float64)

SetSpeed sets the playback speed multiplier (1.0 = normal, 2.0 = 2x, 0.5 = half speed)

func (*PlaybackController) Speed

func (p *PlaybackController) Speed() float64

Speed returns the current playback speed

func (*PlaybackController) Stop

func (p *PlaybackController) Stop()

Stop stops playback completely

func (*PlaybackController) TogglePause

func (p *PlaybackController) TogglePause()

TogglePause toggles pause/resume

type PrintConfig added in v0.0.22

type PrintConfig struct {
	Width   int       // 0 = auto (terminal width or 80). Positive values set the width.
	Height  int       // 0 = auto (based on view size). Positive values set a fixed height.
	Output  io.Writer // nil = os.Stdout. Specify where to write output.
	RawMode bool      // false = use \n line endings. true = use \r\n for raw terminal mode.
}

PrintConfig configures printing functions like Print, Fprint, and Sprint. All fields are optional with sensible zero-value defaults.

type ProgressBar deprecated

type ProgressBar struct {
	Total       int
	Current     int
	Width       int
	Style       Style
	FillChar    string
	EmptyChar   string
	ShowPercent bool
	ShowNumbers bool
	Message     string
}

ProgressBar represents a progress bar widget

Deprecated: Use Progress() for declarative progress bar.

func NewProgressBar deprecated

func NewProgressBar(total int) *ProgressBar

NewProgressBar creates a new progress bar widget

Deprecated: Use Progress() for declarative progress bar.

func (*ProgressBar) Draw

func (p *ProgressBar) Draw(frame RenderFrame, x, y int)

Draw renders the progress bar at the specified position

func (*ProgressBar) SetProgress

func (p *ProgressBar) SetProgress(current int)

SetProgress sets the current progress

func (*ProgressBar) WithChars

func (p *ProgressBar) WithChars(fill, empty string) *ProgressBar

WithChars sets the fill and empty characters

func (*ProgressBar) WithStyle

func (p *ProgressBar) WithStyle(style Style) *ProgressBar

WithStyle sets the progress bar style

func (*ProgressBar) WithWidth

func (p *ProgressBar) WithWidth(width int) *ProgressBar

WithWidth sets the progress bar width

type PromptOption added in v0.0.19

type PromptOption func(*promptConfig)

PromptOption configures the Prompt function.

func WithAutocomplete added in v0.0.19

func WithAutocomplete(fn AutocompleteFunc) PromptOption

WithAutocomplete enables tab completion. The function receives the current input and cursor position, returns a list of completions and the start index to replace from.

func WithHistory added in v0.0.19

func WithHistory(history *[]string) PromptOption

WithHistory enables command history navigation with up/down arrows. The provided slice is updated with new entries.

func WithInputStyle added in v0.0.19

func WithInputStyle(style Style) PromptOption

WithInputStyle styles the user's input text.

func WithMask added in v0.0.19

func WithMask(char string) PromptOption

WithMask masks input (for passwords). Empty string = show nothing.

func WithMaxLength added in v0.0.19

func WithMaxLength(n int) PromptOption

WithMaxLength limits input length.

func WithMultiLine added in v0.0.19

func WithMultiLine(enabled bool) PromptOption

WithMultiLine allows multi-line input via Shift+Enter or Ctrl+J. Enter submits; Shift+Enter/Ctrl+J inserts newline.

func WithPlaceholder added in v0.0.19

func WithPlaceholder(text string) PromptOption

WithPlaceholder shows hint text when input is empty.

func WithPromptOutput added in v0.0.19

func WithPromptOutput(w io.Writer) PromptOption

WithPromptOutput sets the output writer for the prompt. Default is os.Stdout.

func WithPromptStyle added in v0.0.19

func WithPromptStyle(style Style) PromptOption

WithPromptStyle styles the prompt text.

func WithValidator added in v0.0.19

func WithValidator(fn func(string) error) PromptOption

WithValidator validates input before submission. Return non-nil error to prevent submission and show error message.

type PulseAnimation

type PulseAnimation struct {
	Speed         int
	Color         RGB
	MinBrightness float64
	MaxBrightness float64
}

PulseAnimation creates a pulsing brightness effect

func Pulse added in v0.0.7

func Pulse(color RGB, speed int) *PulseAnimation

Pulse creates a new pulse animation with a pulsing brightness effect. Color is the base color, speed controls the pulse rate (lower = faster).

Example:

Text("Active").Animate(Pulse(green, 10))
Text("Active").Animate(Pulse(green, 10).Brightness(0.3, 1.0))

func (*PulseAnimation) Brightness added in v0.0.7

func (p *PulseAnimation) Brightness(minBrightness, maxBrightness float64) *PulseAnimation

Brightness sets the minimum and maximum brightness levels (0.0 to 1.0).

func (*PulseAnimation) GetStyle

func (p *PulseAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

type PulseBorderAnimation added in v0.0.2

type PulseBorderAnimation struct {
	Speed         int
	Color         RGB
	MinBrightness float64
	MaxBrightness float64
	Easing        Easing // Optional easing function for the pulse
}

PulseBorderAnimation creates a pulsing brightness effect on the border.

func (*PulseBorderAnimation) GetBorderStyle added in v0.0.2

func (p *PulseBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type QuitEvent

type QuitEvent struct {
	Time time.Time
}

QuitEvent signals that the application should shut down. The Runtime will exit the event loop, call Destroy() if implemented, and return from Run().

func (QuitEvent) Timestamp

func (e QuitEvent) Timestamp() time.Time

type RGB

type RGB = terminal.RGB

RGB represents an RGB color value

type RainbowAnimation

type RainbowAnimation struct {
	Speed    int // How fast the rainbow moves (frames per cycle)
	Length   int // How many characters the rainbow spans
	Reversed bool
}

RainbowAnimation creates a moving rainbow effect

func Rainbow added in v0.0.7

func Rainbow(speed int) *RainbowAnimation

Rainbow creates a new rainbow animation with the specified speed. Speed controls how fast the rainbow moves (lower = faster).

Example:

Text("Hello").Animate(Rainbow(3))
Text("Hello").Animate(Rainbow(3).Reverse())

func (*RainbowAnimation) GetStyle

func (r *RainbowAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*RainbowAnimation) Reverse added in v0.0.7

func (r *RainbowAnimation) Reverse() *RainbowAnimation

Reverse sets the rainbow to move in the opposite direction.

func (*RainbowAnimation) WithLength added in v0.0.7

func (r *RainbowAnimation) WithLength(length int) *RainbowAnimation

WithLength sets how many characters the rainbow spans. If not set, defaults to the text length.

type RainbowBorderAnimation added in v0.0.2

type RainbowBorderAnimation struct {
	Speed    int  // How fast the rainbow moves (frames per cycle)
	Reversed bool // Direction of movement
}

RainbowBorderAnimation creates a rainbow that cycles around the border.

func (*RainbowBorderAnimation) GetBorderStyle added in v0.0.2

func (r *RainbowBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type Recorder

type Recorder = terminal.Recorder

Recorder handles session recording

type RecordingEvent

type RecordingEvent = terminal.RecordingEvent

RecordingEvent represents a single recording event

type RecordingHeader

type RecordingHeader = terminal.RecordingHeader

RecordingHeader represents asciinema v2 header

type RecordingOptions

type RecordingOptions = terminal.RecordingOptions

RecordingOptions configures recording behavior

type RenderContext added in v0.0.2

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

RenderContext provides drawing capabilities and contextual information to views. It flows through the view tree during rendering, giving views access to:

  • Drawing operations (via the embedded RenderFrame)
  • Animation frame counter
  • Focus management

Views should use SubContext() when rendering children to properly scope the drawing area while preserving context information.

func NewRenderContext added in v0.0.2

func NewRenderContext(frame RenderFrame, frameCount uint64) *RenderContext

NewRenderContext creates a new render context. This is typically called by the Runtime at the start of each render cycle.

func (*RenderContext) AbsoluteBounds added in v0.0.2

func (c *RenderContext) AbsoluteBounds() image.Rectangle

AbsoluteBounds returns the absolute screen bounds of this context. Use this for registering interactive regions (click handlers, etc.).

func (*RenderContext) Bounds added in v0.0.2

func (c *RenderContext) Bounds() image.Rectangle

Bounds returns the current drawing bounds.

func (*RenderContext) Fill added in v0.0.2

func (c *RenderContext) Fill(char rune, style Style)

Fill fills the entire context area with a character and style.

func (*RenderContext) FillStyled added in v0.0.2

func (c *RenderContext) FillStyled(x, y, width, height int, char rune, style Style)

FillStyled fills a rectangular area with a character and style.

func (*RenderContext) FocusManager added in v0.0.22

func (c *RenderContext) FocusManager() *FocusManager

FocusManager returns the focus manager for this context, or nil if none.

func (*RenderContext) Frame added in v0.0.2

func (c *RenderContext) Frame() uint64

Frame returns the current animation frame counter. Use this for time-based animations - it increments each tick (typically 30-60 FPS).

func (c *RenderContext) PrintHyperlink(x, y int, link Hyperlink)

PrintHyperlink prints a clickable hyperlink.

func (*RenderContext) PrintHyperlinkFallback added in v0.0.2

func (c *RenderContext) PrintHyperlinkFallback(x, y int, link Hyperlink)

PrintHyperlinkFallback prints a hyperlink in fallback format: "Text (URL)".

func (*RenderContext) PrintStyled added in v0.0.2

func (c *RenderContext) PrintStyled(x, y int, text string, style Style)

PrintStyled prints text at the given position with a style. Text wraps at the frame edge.

func (*RenderContext) PrintTruncated added in v0.0.2

func (c *RenderContext) PrintTruncated(x, y int, text string, style Style)

PrintTruncated prints text at the given position, truncating at the frame edge.

func (*RenderContext) RenderFrame added in v0.0.2

func (c *RenderContext) RenderFrame() RenderFrame

RenderFrame returns the underlying RenderFrame. This is useful for views that need to create custom frame wrappers.

func (*RenderContext) SetCell added in v0.0.2

func (c *RenderContext) SetCell(x, y int, char rune, style Style)

SetCell sets a character at the given position with a style.

func (*RenderContext) Size added in v0.0.2

func (c *RenderContext) Size() (width, height int)

Size returns the width and height of the current drawing area.

func (*RenderContext) SubContext added in v0.0.2

func (c *RenderContext) SubContext(bounds image.Rectangle) *RenderContext

SubContext creates a child context with a sub-region of the drawing area. The bounds are relative to the current context's origin. All context information (frame counter, focus manager, etc.) is preserved.

func (*RenderContext) WithFocusManager added in v0.0.22

func (c *RenderContext) WithFocusManager(fm *FocusManager) *RenderContext

WithFocusManager returns a new context with the given focus manager.

func (*RenderContext) WithFrame added in v0.0.2

func (c *RenderContext) WithFrame(frame RenderFrame) *RenderContext

WithFrame creates a new context using a different RenderFrame but preserving the frame counter and focus manager. This is useful for views that need custom frame wrappers (like scroll views).

type RenderFrame

type RenderFrame = terminal.RenderFrame

RenderFrame represents a rendering surface for a single frame

type RenderMetrics

type RenderMetrics = terminal.RenderMetrics

RenderMetrics tracks performance statistics for the rendering system

type RenderedDiffLine

type RenderedDiffLine struct {
	LineNumOld string          // Old line number (formatted)
	LineNumNew string          // New line number (formatted)
	Segments   []StyledSegment // Styled content segments
	BgColor    *RGB            // Background color for the entire line
}

RenderedDiffLine represents a single rendered diff line with styling

type RenderedMarkdown

type RenderedMarkdown struct {
	Lines []StyledLine
}

RenderedMarkdown contains the fully rendered markdown content

type ResizeEvent

type ResizeEvent struct {
	Time   time.Time
	Width  int // Terminal width in columns
	Height int // Terminal height in rows
}

ResizeEvent is emitted when the terminal window is resized. Applications receive an initial ResizeEvent on startup with the current terminal dimensions.

Example:

func (a *App) HandleEvent(event Event) []Cmd {
    if resize, ok := event.(ResizeEvent); ok {
        a.width = resize.Width
        a.height = resize.Height
    }
    return nil
}

func (ResizeEvent) Timestamp

func (e ResizeEvent) Timestamp() time.Time

type RunOption

type RunOption func(*runConfig)

RunOption is a functional option for configuring Run.

func WithAlternateScreen

func WithAlternateScreen(enabled bool) RunOption

WithAlternateScreen controls whether to use the alternate screen buffer. When enabled (default), the terminal switches to a separate buffer and restores the original content on exit.

func WithBracketedPaste added in v0.0.2

func WithBracketedPaste(enabled bool) RunOption

WithBracketedPaste enables bracketed paste mode. When enabled, the terminal can distinguish pasted text from typed text, allowing proper handling of multi-line pastes.

func WithFPS

func WithFPS(fps int) RunOption

WithFPS sets the frames per second for TickEvents. Default is 30 FPS. Use 60 for smoother animations.

func WithHideCursor

func WithHideCursor(hide bool) RunOption

WithHideCursor controls whether to hide the cursor during rendering. Default is true. Set to false if your application manages cursor visibility.

func WithInput added in v0.0.2

func WithInput(source InputSource) RunOption

WithInput sets a custom input source for the runtime. This is primarily used for testing.

func WithMouseTracking

func WithMouseTracking(enabled bool) RunOption

WithMouseTracking enables mouse event tracking. When enabled, the application will receive MouseEvent events.

func WithPasteTabWidth

func WithPasteTabWidth(width int) RunOption

WithPasteTabWidth configures how tabs in pasted content are handled. If width is 0 (default), tabs are preserved as-is. If width > 0, each tab is converted to that many spaces.

type Runtime

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

Runtime manages the event-driven execution of an Application. It provides a race-free, single-threaded event loop while supporting async operations through the command system.

Architecture:

  • Goroutine 1: Main event loop (processes events sequentially, calls HandleEvent/View)
  • Goroutine 2: Input reader (blocks on stdin, sends KeyEvents)
  • Goroutine 3: Command executor (runs async commands, sends results as events)

This design eliminates race conditions in application code while maintaining responsive UI through non-blocking async operations.

func NewRuntime

func NewRuntime(terminal *Terminal, app any, fps int) *Runtime

NewRuntime creates a new Runtime for the given application.

Parameters:

  • terminal: The Terminal instance to use for rendering and input
  • app: The Application to run
  • fps: Frames per second for TickEvents (30 recommended, 60 for smooth animations)

The runtime does not start automatically. Call Run() to start the event loop.

func (*Runtime) Run

func (r *Runtime) Run() error

Run starts the runtime's event loop and blocks until the application quits. This method is the main entry point for message-driven applications.

Execution flow:

  1. Initialize application (if it implements Initializable)
  2. Register resize handler with terminal
  3. Start three goroutines: event loop, input reader, command executor
  4. Block until QuitEvent is received
  5. Clean up and call Destroy (if implemented)

Returns error if initialization fails.

func (*Runtime) SendEvent

func (r *Runtime) SendEvent(event Event)

SendEvent sends an event to the runtime's event loop. This is useful for custom event sources or testing. It's safe to call from any goroutine.

func (*Runtime) SetInputSource added in v0.0.2

func (r *Runtime) SetInputSource(source InputSource)

SetInputSource sets the input source for the runtime.

func (*Runtime) SetPasteTabWidth

func (r *Runtime) SetPasteTabWidth(width int)

SetPasteTabWidth configures how tabs in pasted content are handled. If width is 0 (default), tabs are preserved as-is. If width > 0, each tab is converted to that many spaces. Must be called before Run().

func (*Runtime) Stop

func (r *Runtime) Stop()

Stop gracefully stops the runtime by sending a QuitEvent. This can be called from application code or externally.

type ScrollAnchor

type ScrollAnchor int

ScrollAnchor determines which part of content to show when content exceeds viewport.

const (
	// ScrollAnchorTop shows content from the top (default scroll behavior).
	ScrollAnchorTop ScrollAnchor = iota
	// ScrollAnchorBottom shows content from the bottom (chat-style, newest at bottom).
	ScrollAnchorBottom
)

type SecureString

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

SecureString provides a wrapper around password data with automatic memory zeroing.

func NewSecureString

func NewSecureString(data []byte) *SecureString

NewSecureString creates a new SecureString from a byte slice.

func (*SecureString) Bytes

func (s *SecureString) Bytes() []byte

Bytes returns the underlying byte slice. Do not modify the returned slice.

func (*SecureString) Clear

func (s *SecureString) Clear()

Clear zeros the password data in memory. After calling Clear, the SecureString should not be used.

func (*SecureString) IsEmpty

func (s *SecureString) IsEmpty() bool

IsEmpty returns true if the password is empty.

func (*SecureString) Len

func (s *SecureString) Len() int

Len returns the length of the password.

func (*SecureString) String

func (s *SecureString) String() string

String returns the password as a string. Note: This creates a copy in memory. Use with caution.

type SizeConstraints

type SizeConstraints struct {
	MinWidth  int
	MinHeight int
	MaxWidth  int // 0 means no maximum
	MaxHeight int // 0 means no maximum
}

SizeConstraints defines minimum and maximum size constraints for a widget. Zero values for MaxWidth/MaxHeight indicate no maximum constraint (unconstrained).

func (SizeConstraints) Constrain

func (sc SizeConstraints) Constrain(size image.Point) image.Point

Constrain applies the constraints to a given size

func (SizeConstraints) HasMaxHeight

func (sc SizeConstraints) HasMaxHeight() bool

HasMaxHeight returns true if there is a maximum height constraint

func (SizeConstraints) HasMaxWidth

func (sc SizeConstraints) HasMaxWidth() bool

HasMaxWidth returns true if there is a maximum width constraint

func (SizeConstraints) IsTight

func (sc SizeConstraints) IsTight() bool

IsTight returns true if min and max are equal (and max > 0)

type SlideAnimation added in v0.0.2

type SlideAnimation struct {
	Speed          int
	BaseColor      RGB
	HighlightColor RGB
	Width          int  // Width of the highlight in characters
	Reverse        bool // True = right to left, false = left to right
}

SlideAnimation creates a highlight that slides across the text

func Slide added in v0.0.7

func Slide(speed int, baseColor, highlightColor RGB) *SlideAnimation

Slide creates a new slide animation with a highlight that moves across text. Speed controls how fast the highlight moves (lower = faster).

Example:

Text("Loading").Animate(Slide(2, gray, white))
Text("Loading").Animate(Slide(2, gray, white).Reversed())

func (*SlideAnimation) GetStyle added in v0.0.2

func (s *SlideAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*SlideAnimation) Reversed added in v0.0.7

func (s *SlideAnimation) Reversed() *SlideAnimation

Reversed sets the slide to move from right to left.

func (*SlideAnimation) WithWidth added in v0.0.7

func (s *SlideAnimation) WithWidth(width int) *SlideAnimation

WithWidth sets the width of the highlight in characters.

type SparkleAnimation added in v0.0.2

type SparkleAnimation struct {
	Speed      int // How often sparkles update
	BaseColor  RGB
	SparkColor RGB
	Density    int // Higher = more sparkles (1-10 recommended)
}

SparkleAnimation creates a twinkling star-like effect where random characters briefly brighten

func Sparkle added in v0.0.7

func Sparkle(speed int, baseColor, sparkColor RGB) *SparkleAnimation

Sparkle creates a new sparkle animation with twinkling star-like effects. Speed controls animation timing (lower = faster).

Example:

Text("Stars").Animate(Sparkle(3, gray, white))
Text("Stars").Animate(Sparkle(3, gray, white).WithDensity(5))

func (*SparkleAnimation) GetStyle added in v0.0.2

func (s *SparkleAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*SparkleAnimation) WithDensity added in v0.0.7

func (s *SparkleAnimation) WithDensity(density int) *SparkleAnimation

WithDensity sets how many sparkles appear (1-10 recommended, higher = more sparkles).

type SparkleBorderAnimation added in v0.0.2

type SparkleBorderAnimation struct {
	Speed       int
	BaseColor   RGB
	SparkColor  RGB
	Density     int // Probability of sparkle (1-10)
	SparkleSize int // Size of each sparkle in characters
}

SparkleBorderAnimation creates random sparkles around the border.

func (*SparkleBorderAnimation) GetBorderStyle added in v0.0.2

func (s *SparkleBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type Spinner deprecated

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

Spinner represents an animated loading spinner widget

Deprecated: Use Loading() for declarative spinner.

func NewSpinner deprecated

func NewSpinner(style SpinnerStyle) *Spinner

NewSpinner creates a new spinner widget

Deprecated: Use Loading() for declarative spinner.

func (*Spinner) Bg added in v0.0.2

func (s *Spinner) Bg(c Color) *Spinner

Bg sets the background color for the spinner.

func (*Spinner) Bold added in v0.0.2

func (s *Spinner) Bold() *Spinner

Bold makes the spinner bold.

func (*Spinner) Dim added in v0.0.2

func (s *Spinner) Dim() *Spinner

Dim makes the spinner dimmed.

func (*Spinner) Draw

func (s *Spinner) Draw(frame RenderFrame, x, y int)

Draw renders the spinner at the specified position

func (*Spinner) Fg added in v0.0.2

func (s *Spinner) Fg(c Color) *Spinner

Fg sets the foreground color for the spinner.

func (*Spinner) Update

func (s *Spinner) Update(now time.Time)

Update advances the spinner animation based on elapsed time. Call this from HandleEvent on TickEvent.

func (*Spinner) WithMessage

func (s *Spinner) WithMessage(message string) *Spinner

WithMessage sets the spinner message

func (*Spinner) WithStyle

func (s *Spinner) WithStyle(style Style) *Spinner

WithStyle sets the spinner style

type SpinnerStyle

type SpinnerStyle struct {
	Frames   []string
	Interval time.Duration
}

SpinnerStyle defines different spinner animations

type StatusItem

type StatusItem struct {
	Key   string
	Value string
	Style Style
	Icon  string
}

StatusItem represents an item in the status bar

type Style

type Style = terminal.Style

Style represents text styling attributes

type StyledLine

type StyledLine struct {
	Segments []StyledSegment
	Indent   int // Indentation level in spaces
}

StyledLine represents a single line of rendered markdown with styled segments

type StyledSegment

type StyledSegment struct {
	Text      string
	Style     Style
	Hyperlink *Hyperlink // Optional hyperlink
}

StyledSegment represents a portion of text with a specific style

type TableColumn

type TableColumn struct {
	Title    string
	Width    int // If 0, auto-calculated based on content
	MinWidth int // Minimum width (won't shrink below this)
}

TableColumn represents a table column configuration.

type Terminal

type Terminal = terminal.Terminal

Terminal represents the terminal abstraction

type TextAnimation

type TextAnimation interface {
	GetStyle(frame uint64, charIndex int, totalChars int) Style
}

TextAnimation defines how text should be animated

type TextInput

type TextInput struct {
	BaseWidget
	Placeholder string
	CursorPos   int // Cursor position in display text

	// Styles
	Style            Style
	PlaceholderStyle Style
	CursorStyle      Style
	PasteStyle       Style // Style for paste placeholders
	OverflowStyle    Style // Style for overflow indicators (▲/▼)

	// Callbacks
	OnChange func(value string)
	OnSubmit func(value string)

	// Password/masking support
	MaskChar  rune // If non-zero, display this character instead of actual text
	MaxLength int  // If non-zero, limit input to this many runes

	// Paste placeholder mode
	PastePlaceholderMode bool // When true, multi-line pastes show as "[pasted N lines]"

	// Multiline mode
	MultilineMode bool // When true, Shift+Enter inserts newlines
	SubmitOnEnter bool // When true, Enter triggers OnSubmit (default: true)

	// Scrolling (for multiline with MaxHeight)
	MaxHeight    int // Maximum visible lines (0 = unlimited)
	ScrollOffset int // First visible line (0-indexed)

	// Cursor appearance
	CursorBlink         bool             // When true, cursor blinks
	CursorBlinkInterval time.Duration    // Blink interval (default 530ms)
	CursorShape         InputCursorStyle // Shape of the cursor (block, underline, bar)
	CursorColor         *Color           // Custom cursor color (nil = use default style)
	// contains filtered or unexported fields
}

TextInput is a simple single-line text input widget

func NewTextInput

func NewTextInput() *TextInput

NewTextInput creates a new text input widget

func (*TextInput) Clear

func (t *TextInput) Clear()

Clear clears all input

func (*TextInput) DisplayText

func (t *TextInput) DisplayText() string

DisplayText returns the text shown to the user (with placeholders for pastes)

func (*TextInput) Draw

func (t *TextInput) Draw(frame RenderFrame)

Draw renders the input

func (*TextInput) HandleKey

func (t *TextInput) HandleKey(event KeyEvent) bool

HandleKey handles key events

func (*TextInput) HandleMouse

func (t *TextInput) HandleMouse(event MouseEvent) bool

HandleMouse handles mouse events

func (*TextInput) HandlePaste

func (t *TextInput) HandlePaste(content string) bool

HandlePaste handles pasted content, using placeholder mode if enabled for multi-line pastes. Returns true if the paste was handled.

func (*TextInput) SetFocused

func (t *TextInput) SetFocused(focused bool)

SetFocused sets focus state

func (*TextInput) SetValue

func (t *TextInput) SetValue(value string)

SetValue sets the input value, clearing any segments

func (*TextInput) Value

func (t *TextInput) Value() string

Value returns the actual value (with full paste content, not placeholders)

func (t *TextInput) WithCursorBlink(enabled bool) *TextInput

WithCursorBlink enables or disables cursor blinking.

func (*TextInput) WithCursorBlinkInterval added in v0.0.2

func (t *TextInput) WithCursorBlinkInterval(interval time.Duration) *TextInput

WithCursorBlinkInterval sets the cursor blink interval. Default is 530ms if not set.

func (*TextInput) WithCursorColor added in v0.0.2

func (t *TextInput) WithCursorColor(color Color) *TextInput

WithCursorColor sets a custom cursor color. If not set, uses the CursorStyle colors (default: white background, black foreground).

func (*TextInput) WithCursorShape added in v0.0.2

func (t *TextInput) WithCursorShape(shape InputCursorStyle) *TextInput

WithCursorShape sets the cursor shape/style. Options are: InputCursorBlock (default), InputCursorUnderline, InputCursorBar.

func (*TextInput) WithMask

func (t *TextInput) WithMask(char rune) *TextInput

WithMask sets a mask character for password input. When set, all characters are displayed as this character instead of the actual text.

func (*TextInput) WithMaxHeight added in v0.0.2

func (t *TextInput) WithMaxHeight(lines int) *TextInput

WithMaxHeight sets the maximum visible height in lines. When content exceeds this, the input becomes scrollable with overflow indicators.

func (*TextInput) WithMaxLength

func (t *TextInput) WithMaxLength(n int) *TextInput

WithMaxLength sets the maximum number of runes allowed in the input.

func (*TextInput) WithMultilineMode

func (t *TextInput) WithMultilineMode(enabled bool) *TextInput

WithMultilineMode enables multiline input where Shift+Enter inserts newlines. Newlines are displayed as the NewlineDisplay string (default "↵").

func (*TextInput) WithPastePlaceholderMode

func (t *TextInput) WithPastePlaceholderMode(enabled bool) *TextInput

WithPastePlaceholderMode enables/disables paste placeholder mode

func (*TextInput) WithPlaceholder

func (t *TextInput) WithPlaceholder(placeholder string) *TextInput

WithPlaceholder sets the placeholder text shown when input is empty.

func (*TextInput) WithStyle

func (t *TextInput) WithStyle(style Style) *TextInput

WithStyle sets the style for the input text.

func (*TextInput) WithSubmitOnEnter

func (t *TextInput) WithSubmitOnEnter(enabled bool) *TextInput

WithSubmitOnEnter sets whether Enter triggers OnSubmit. Default is true. Set to false if you want Enter to do nothing (useful with MultilineMode).

type TickEvent

type TickEvent struct {
	Time  time.Time
	Frame uint64 // Frame counter, increments with each tick
}

TickEvent is emitted periodically based on the runtime's FPS setting. Use this for animations and periodic updates.

The Frame counter increments with each tick, starting from 1. This is useful for frame-based animations that need consistent timing.

Example:

func (a *App) HandleEvent(event Event) []Cmd {
    if tick, ok := event.(TickEvent); ok {
        a.rotation = (a.rotation + 1) % 360
    }
    return nil
}

func (TickEvent) Timestamp

func (e TickEvent) Timestamp() time.Time

type TreeBranchChars

type TreeBranchChars struct {
	Vertical   string // │
	Corner     string // └
	Tee        string // ├
	Horizontal string // ─
}

TreeBranchChars defines the characters used for drawing tree branches.

func ASCIITreeBranchChars

func ASCIITreeBranchChars() TreeBranchChars

ASCIITreeBranchChars returns ASCII tree branch characters.

func DefaultTreeBranchChars

func DefaultTreeBranchChars() TreeBranchChars

DefaultTreeBranchChars returns the default unicode tree branch characters.

type TreeNode

type TreeNode struct {
	// Label is the display text for this node.
	Label string

	// Children contains the child nodes.
	Children []*TreeNode

	// Expanded indicates whether this node's children are visible.
	Expanded bool

	// Data holds arbitrary user data associated with this node.
	Data any
}

TreeNode represents a node in a tree view.

func NewTreeNode

func NewTreeNode(label string) *TreeNode

NewTreeNode creates a new tree node with the given label.

func (*TreeNode) AddChild

func (n *TreeNode) AddChild(child *TreeNode) *TreeNode

AddChild adds a child node and returns it for chaining.

func (*TreeNode) AddChildren

func (n *TreeNode) AddChildren(children ...*TreeNode) *TreeNode

AddChildren adds multiple child nodes.

func (*TreeNode) CollapseAll

func (n *TreeNode) CollapseAll()

CollapseAll collapses this node and all descendants.

func (*TreeNode) ExpandAll

func (n *TreeNode) ExpandAll()

ExpandAll expands this node and all descendants.

func (*TreeNode) IsLeaf

func (n *TreeNode) IsLeaf() bool

IsLeaf returns true if this node has no children.

func (*TreeNode) SetData

func (n *TreeNode) SetData(data any) *TreeNode

SetData sets the user data and returns the node for chaining.

func (*TreeNode) SetExpanded

func (n *TreeNode) SetExpanded(expanded bool) *TreeNode

SetExpanded sets the expanded state.

type TypewriterAnimation added in v0.0.2

type TypewriterAnimation struct {
	Speed       int // Frames per character reveal
	TextColor   RGB
	CursorColor RGB
	Loop        bool // Whether to restart after fully revealed
	HoldFrames  int  // Frames to hold before looping (if Loop is true)
}

TypewriterAnimation reveals text character by character with a blinking cursor

func Typewriter added in v0.0.7

func Typewriter(speed int, textColor, cursorColor RGB) *TypewriterAnimation

Typewriter creates a new typewriter animation that reveals text character by character. Speed controls how fast characters appear (lower = faster).

Example:

Text("Hello, World!").Animate(Typewriter(4, white, green))
Text("Hello, World!").Animate(Typewriter(4, white, green).WithLoop(true))

func (*TypewriterAnimation) GetStyle added in v0.0.2

func (t *TypewriterAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*TypewriterAnimation) WithHoldFrames added in v0.0.7

func (t *TypewriterAnimation) WithHoldFrames(frames int) *TypewriterAnimation

WithHoldFrames sets how many frames to hold before looping (if Loop is true).

func (*TypewriterAnimation) WithLoop added in v0.0.7

func (t *TypewriterAnimation) WithLoop(loop bool) *TypewriterAnimation

WithLoop sets whether the animation should restart after fully revealed.

type VBoxLayout

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

VBoxLayout arranges children vertically in a column, top to bottom. Each child gets its preferred/minimum height, and width fills the container.

func NewVBoxLayout

func NewVBoxLayout(spacing int) *VBoxLayout

NewVBoxLayout creates a new vertical box layout with the specified spacing

func (*VBoxLayout) CalculateMinSize

func (vb *VBoxLayout) CalculateMinSize(children []ComposableWidget) image.Point

CalculateMinSize returns the minimum size needed for all children

func (*VBoxLayout) CalculatePreferredSize

func (vb *VBoxLayout) CalculatePreferredSize(children []ComposableWidget) image.Point

CalculatePreferredSize returns the preferred size for all children

func (*VBoxLayout) Layout

func (vb *VBoxLayout) Layout(containerBounds image.Rectangle, children []ComposableWidget)

Layout positions children vertically within the container bounds

func (*VBoxLayout) LayoutWithConstraints

func (vb *VBoxLayout) LayoutWithConstraints(children []ComposableWidget, constraints SizeConstraints, containerBounds image.Rectangle)

LayoutWithConstraints implements ConstraintLayoutManager.LayoutWithConstraints

func (*VBoxLayout) Measure

func (vb *VBoxLayout) Measure(children []ComposableWidget, constraints SizeConstraints) image.Point

Measure implements ConstraintLayoutManager.Measure

func (*VBoxLayout) WithAlignment

func (vb *VBoxLayout) WithAlignment(align LayoutAlignment) *VBoxLayout

WithAlignment sets the horizontal alignment for children

func (*VBoxLayout) WithDistribute

func (vb *VBoxLayout) WithDistribute(distribute bool) *VBoxLayout

WithDistribute enables distributing extra vertical space among children

type View

type View interface {
	// contains filtered or unexported methods
}

View is the core interface for declarative UI. Views form a tree structure where containers measure and position children.

Methods are unexported - users compose views using builder functions like Text(), Stack(), Group(), etc. rather than implementing View directly.

For custom focusable components, use FocusableView() to wrap any view with focus handling capabilities.

func Background

func Background(char rune, style Style, inner View) View

Background wraps a view with a background fill.

func Empty

func Empty() View

Empty returns a view that renders nothing. Useful for conditional rendering.

func FocusableView added in v0.0.2

func FocusableView(inner View, handler *FocusHandler) View

FocusableView wraps any view to make it participate in focus navigation. The handler defines the focus ID and optional key event handling.

Example:

FocusableView(
    myScrollView,
    &tui.FocusHandler{
        ID: "content-viewer",
        OnKey: func(e tui.KeyEvent) bool {
            if e.Key == tui.KeyArrowDown { scrollY++; return true }
            return false
        },
    },
)

func Height

func Height(h int, inner View) View

Height wraps a view with a fixed height in rows. The view will be exactly this height, clipping or padding as needed.

Example:

Height(10, content)  // Exactly 10 rows tall

func If

func If(condition bool, view View) View

If returns the view if condition is true, otherwise returns Empty. Use this for conditional rendering in declarative UI.

Example:

Stack(
    Text("Always shown"),
    If(app.showWarning, Text("Warning!").Fg(ColorYellow)),
)

func IfElse

func IfElse(condition bool, thenView, elseView View) View

IfElse returns thenView if condition is true, otherwise returns elseView. Use this for conditional rendering with an alternative.

Example:

IfElse(app.isLoggedIn,
    Text("Welcome back!"),
    Text("Please log in"),
)

func MaxHeight

func MaxHeight(h int, inner View) View

MaxHeight wraps a view with a maximum height constraint. The view can be smaller but will not exceed this height.

Example:

MaxHeight(20, content)  // Won't exceed 20 rows

func MaxWidth

func MaxWidth(w int, inner View) View

MaxWidth wraps a view with a maximum width constraint. The view can be smaller but will not exceed this width.

Example:

MaxWidth(80, Text("Long text..."))  // Won't exceed 80 cells

func MinHeight added in v0.0.7

func MinHeight(h int, inner View) View

MinHeight wraps a view with a minimum height constraint. The view can be larger but will not be smaller than this height.

Example:

MinHeight(10, content)  // At least 10 rows tall

func MinSize added in v0.0.7

func MinSize(w, h int, inner View) View

MinSize wraps a view with minimum width and height constraints.

Example:

MinSize(40, 10, content)  // At least 40x10 cells

func MinWidth added in v0.0.7

func MinWidth(w int, inner View) View

MinWidth wraps a view with a minimum width constraint. The view can be larger but will not be smaller than this width.

Example:

MinWidth(40, content)  // At least 40 cells wide

func Padding

func Padding(n int, inner View) View

Padding wraps a view with equal padding on all sides. Padding adds empty space around content, measured in character cells.

Example:

Padding(2, Text("Content"))  // 2 cells of padding on all sides
Example

ExamplePadding demonstrates adding space around views.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	content := tui.Text("Padded content")

	// Equal padding on all sides
	_ = tui.Padding(2, content)

	// Different horizontal and vertical padding
	_ = tui.PaddingHV(4, 1, content)

	// Specific padding on each side
	_ = tui.PaddingLTRB(1, 2, 3, 4, content)

}

func PaddingHV

func PaddingHV(h, v int, inner View) View

PaddingHV wraps a view with horizontal and vertical padding. The first parameter is horizontal (left and right), the second is vertical (top and bottom).

Example:

PaddingHV(4, 1, Text("Content"))  // 4 cells horizontal, 1 cell vertical

func PaddingLTRB

func PaddingLTRB(left, top, right, bottom int, inner View) View

PaddingLTRB wraps a view with specific padding on each side. Parameters are in CSS order: left, top, right, bottom.

Example:

PaddingLTRB(1, 2, 3, 4, Text("Content"))  // Different padding on each side

func Size

func Size(w, h int, inner View) View

Size wraps a view with fixed width and height. Combines Width and Height into a single modifier.

Example:

Size(80, 24, content)  // Exactly 80x24 cells

func Switch

func Switch[T comparable](value T, cases ...CaseView[T]) View

Switch returns the view associated with the matching case value. If no case matches and no Default is provided, returns Empty.

Example:

Switch(app.status,
    Case("loading", Text("Loading...").Fg(ColorYellow)),
    Case("error", Text("Error!").Fg(ColorRed)),
    Case("ready", Text("Ready").Fg(ColorGreen)),
    Default[string](Text("Unknown")),
)

func Width

func Width(w int, inner View) View

Width wraps a view with a fixed width in character cells. The view will be exactly this width, clipping or padding as needed.

Example:

Width(40, Text("This text will be exactly 40 cells wide"))
Example

ExampleWidth demonstrates size constraints.

package main

import (
	"github.com/deepnoodle-ai/wonton/tui"
)

func main() {
	content := tui.Text("This is some text content")

	// Fixed width
	_ = tui.Width(40, content)

	// Maximum width (can be smaller)
	_ = tui.MaxWidth(80, content)

	// Fixed width and height
	_ = tui.Size(40, 10, content)
}

type ViewProvider deprecated

type ViewProvider = Application

ViewProvider is an alias for Application for backward compatibility.

Deprecated: Use Application directly instead.

type WaveAnimation

type WaveAnimation struct {
	Speed     int
	Amplitude float64
	Colors    []RGB
}

WaveAnimation creates a wave-like color effect that flows across text

func Wave added in v0.0.7

func Wave(speed int, colors ...RGB) *WaveAnimation

Wave creates a new wave animation with the specified speed and colors. Speed controls how fast the wave moves (lower = faster). Colors are the colors to cycle through; defaults to magenta/green/purple if none provided.

Example:

Text("Hello").Animate(Wave(5))
Text("Hello").Animate(Wave(5, NewRGB(255, 0, 0), NewRGB(0, 0, 255)))

func (*WaveAnimation) GetStyle

func (w *WaveAnimation) GetStyle(frame uint64, charIndex int, totalChars int) Style

GetStyle returns the style for a character at a given frame

func (*WaveAnimation) WithAmplitude added in v0.0.7

func (w *WaveAnimation) WithAmplitude(amplitude float64) *WaveAnimation

WithAmplitude sets the wave amplitude.

type WaveBorderAnimation added in v0.0.2

type WaveBorderAnimation struct {
	Speed     int
	Colors    []RGB
	WaveWidth int // Width of the color wave in characters
}

WaveBorderAnimation creates a wave that travels around the border.

func (*WaveBorderAnimation) GetBorderStyle added in v0.0.2

func (w *WaveBorderAnimation) GetBorderStyle(frame uint64, part BorderPart, position int, length int) Style

GetBorderStyle returns the style for a border position at a given frame.

type Widget

type Widget interface {
	Draw(frame RenderFrame)
	HandleKey(event KeyEvent) bool
}

Widget represents a UI widget that can be rendered and handle input.

Directories

Path Synopsis
cmd
reviewtests command
reviewtests displays golden test code alongside their snapshots for LLM review.
reviewtests displays golden test code alongside their snapshots for LLM review.

Jump to

Keyboard shortcuts

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