teaware

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Apr 6, 2026 License: MIT Imports: 7 Imported by: 0

README

teaware

A widget framework for Bubble Tea v2 applications. Provides composable UI components — inputs, buttons, checkboxes, dropdowns — and layout containers (Flex, Grid) with automatic Tab/Shift+Tab focus navigation.

Quick start

package main

import (
    "fmt"
    "os"

    tea "charm.land/bubbletea/v2"
    "github.com/anadale/teaservice/teaware"
)

type model struct{ form *teaware.Form }

func newModel() *model {
    nameInput := teaware.NewTextInput().Placeholder("Your name")
    emailInput := teaware.NewTextInput().Placeholder("Email address")
    grid := teaware.NewGrid().
        Columns(teaware.Auto, teaware.Stretch).
        AddRow(teaware.NewLabel("Name:"), nameInput).
        AddRow(teaware.NewLabel("Email:"), emailInput).
        AddRow(teaware.Span(2, teaware.NewButton("Submit")))
    return &model{form: teaware.NewForm(grid)}
}

func (m *model) Init() tea.Cmd { return m.form.Focus() }
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    if ws, ok := msg.(tea.WindowSizeMsg); ok {
        m.form.SetSize(ws.Width, ws.Height)
        return m, nil
    }
    if k, ok := msg.(tea.KeyPressMsg); ok && k.String() == "esc" && !m.form.HasOpenPopups() {
        return m, tea.Quit
    }
    return m, m.form.Update(msg)
}
func (m *model) View() string { return m.form.View() }

func main() {
    if _, err := tea.NewProgram(newModel()).Run(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
}

Widgets

Widget Constructor Description
Button NewButton(label string) Pressable button
Checkbox NewCheckbox(label string) Toggle checkbox
Dropdown NewDropdown(options []string) Selection dropdown
TextInput NewTextInput() Single-line text input
Textarea NewTextarea() Multi-line text area
Label NewLabel(text string) Static text label
RadioGroup NewRadioGroup(options []string) Mutually exclusive options
Separator NewSeparator(dir Direction) Visual divider line
Spacer NewSpacer() Empty space filler

Layout containers

Flex

Arranges children linearly along a direction.

teaware.NewFlex(teaware.Horizontal).
    Gap(1).
    Add(okBtn).
    Add(cancelBtn, teaware.Grow()) // Grow() makes this child fill remaining space

Use teaware.Vertical for a vertical stack.

Grid

Arranges children in a table with configurable column sizing.

teaware.NewGrid().
    Columns(teaware.Auto, teaware.Stretch, teaware.FixedColumn(10)).
    AddRow(label1, input1, hint1).
    AddRow(teaware.Span(3, fullWidthWidget)) // span 3 columns

Column sizing options: Auto (fit content), Stretch (fill space), FixedColumn(n).

Use .ColumnGap(n) to add blank columns between grid columns.

Focus navigation

Form orchestrates Tab/Shift+Tab automatically across all focusable children:

form := teaware.NewForm(root) // root must implement Container
form.Focus()                  // call once to set initial focus

Widgets implementing FocusOwner participate in focus navigation. Containers implementing Container manage focus within their children before delegating to the parent.

To disable automatic Tab handling: form.WithAutoTab(false).

Handling Esc with open popups

When a dropdown (or other widget with a popup overlay) is open, Esc should close the popup rather than quit the application. Use HasOpenPopups to distinguish the two cases:

case tea.KeyPressMsg:
    if msg.String() == "esc" && !m.form.HasOpenPopups() {
        return m, tea.Quit
    }

HasOpenPopups reflects the state of the last View() call.

Theme

// Apply a custom theme globally (affects all widgets in the process).
teaware.SetTheme(myTheme)

// Read the current theme.
current := teaware.CurrentTheme()

// Build on the default theme.
theme := teaware.DefaultTheme()
theme.Button.Focused = lipgloss.NewStyle().Bold(true).Padding(0, 2)
teaware.SetTheme(theme)

Size

Form implements Sizer. Push the terminal size into the form in response to tea.WindowSizeMsg:

case tea.WindowSizeMsg:
    m.form.SetSize(msg.Width, msg.Height)

Button actions

Use OnClick to attach an action and ButtonStyleSecondary for a less prominent variant:

submitBtn := teaware.NewButton("Submit").OnClick(func() tea.Cmd {
    return func() tea.Msg { return submitMsg{} }
})
cancelBtn := teaware.NewButton("Cancel").
    Style(teaware.ButtonStyleSecondary).
    OnClick(func() tea.Cmd { return tea.Quit })

Examples

Documentation

Overview

Package teaware provides form widgets and layout containers for building terminal user interfaces with the Bubble Tea framework.

Widgets

teaware includes the following interactive widgets:

  • TextInput — single-line text field
  • Textarea — multi-line text area
  • Dropdown — single-select dropdown list
  • Checkbox — boolean toggle rendered as "[ ] Label" / "[x] Label"
  • RadioGroup — single-select group of radio buttons
  • Button — clickable button with primary and secondary styles
  • Label — non-interactive text label
  • Separator — horizontal visual divider
  • Spacer — invisible flexible space for layouts

Layout Containers

Widgets are arranged using layout containers:

  • Flex — row or column layout with proportional sizing
  • Grid — fixed column-definition grid layout
  • [Overlay] — popup layer rendered on top of other content
  • Padding — adds fixed padding around a single widget

Forms

Form is the top-level container that wires together focus management, keyboard navigation (Tab / Shift-Tab), and overlay rendering. Embed any combination of widgets and layout containers:

form := teaware.NewForm(
    teaware.NewGrid(
        teaware.Col(20, teaware.NewLabel("Name:")),
        teaware.Col(40, teaware.NewTextInput()),
    ),
    teaware.NewButton("Submit").OnClick(func() tea.Cmd {
        // handle submit
        return nil
    }),
)

Theming

All widgets read styles from the active Theme. Call SetTheme to apply a custom theme globally; use DefaultTheme as a starting point.

Index

Examples

Constants

This section is empty.

Variables

View Source
var Auto = ColumnDef{/* contains filtered or unexported fields */}

Auto is a ColumnDef that sizes the column to fit its content.

View Source
var FocusNextCmd tea.Cmd = func() tea.Msg { return FocusNextMsg{} }

FocusNextCmd sends FocusNextMsg to signal focus exhaustion in the forward direction.

View Source
var FocusPreviousCmd tea.Cmd = func() tea.Msg { return FocusPreviousMsg{} }

FocusPreviousCmd sends FocusPreviousMsg to signal focus exhaustion in the backward direction.

View Source
var Stretch = ColumnDef{/* contains filtered or unexported fields */}

Stretch is a ColumnDef that takes remaining available space.

Functions

func RegisterOverlay

func RegisterOverlay(absX, absY int, content string)

RegisterOverlay registers floating overlay content at the given absolute coordinates. It is a no-op when no registry has been set.

func SetOverlayRegistry

func SetOverlayRegistry(r OverlayRegistry)

SetOverlayRegistry sets the global overlay registry. Pass nil to clear it. Must be called from the Bubble Tea goroutine (e.g. inside View).

func SetTheme

func SetTheme(t Theme)

SetTheme replaces the active theme. All widgets pick up the new theme on next View(). Must be called from the Bubble Tea goroutine (e.g. inside Update).

Example

ExampleSetTheme shows replacing the global theme with a custom one. All widgets pick up the new theme on the next teaware.Form.View call.

original := teaware.CurrentTheme()
defer teaware.SetTheme(original)

custom := teaware.DefaultTheme()
custom.Checkbox.Checked = "[✓]"
custom.Checkbox.Unchecked = "[ ]"
custom.Button.Focused = lipgloss.NewStyle().
	Padding(0, 3).
	Foreground(lipgloss.Color("15")).
	Background(lipgloss.Color("99"))

teaware.SetTheme(custom)

theme := teaware.CurrentTheme()
fmt.Println("checked symbol:", theme.Checkbox.Checked)
Output:
checked symbol: [✓]

Types

type Button

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

Button is an interactive widget that renders a styled label. Enter and Space triggers the OnClick handler.

func NewButton

func NewButton(label string) *Button

NewButton creates a new Button with the given label.

Example

ExampleNewButton shows a teaware.Button with primary and secondary variants.

package main

import (
	"fmt"

	tea "charm.land/bubbletea/v2"
	"github.com/anadale/teaservice/teaware"
)

func main() {
	var submitted bool

	submit := teaware.NewButton("Submit").
		OnClick(func() tea.Cmd {
			submitted = true
			return nil
		})

	cancel := teaware.NewButton("Cancel").
		Style(teaware.ButtonStyleSecondary).
		OnClick(func() tea.Cmd { return tea.Quit })

	_ = submit
	_ = cancel
	fmt.Println("buttons created, submitted:", submitted)
}
Output:
buttons created, submitted: false

func (*Button) Blur

func (b *Button) Blur()

Blur implements FocusOwner.

func (*Button) Constraints

func (b *Button) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Button) Disabled

func (b *Button) Disabled(v bool) *Button

Disabled sets whether the button is disabled.

func (*Button) Focus

func (b *Button) Focus() tea.Cmd

Focus implements FocusOwner.

func (*Button) Focused

func (b *Button) Focused() bool

Focused implements FocusOwner.

func (*Button) OnClick

func (b *Button) OnClick(fn func() tea.Cmd) *Button

OnClick sets the callback invoked when the button is activated.

func (*Button) SetSize

func (b *Button) SetSize(_, _ int)

SetSize implements Sizer.

func (*Button) Style

func (b *Button) Style(s ButtonStyle) *Button

Style sets the visual variant of the button.

func (*Button) Update

func (b *Button) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (*Button) View

func (b *Button) View() string

View implements Painter.

type ButtonStyle

type ButtonStyle int

ButtonStyle defines the visual variant of a Button.

const (
	ButtonStylePrimary   ButtonStyle = iota // ButtonStylePrimary is the default, accent button style.
	ButtonStyleSecondary                    // ButtonStyleSecondary is the less prominent, outlined button style.
)

Available ButtonStyle values.

type ButtonTheme

type ButtonTheme struct {
	Normal   lipgloss.Style
	Focused  lipgloss.Style
	Disabled lipgloss.Style
}

ButtonTheme defines styles for Button widgets.

type Checkbox

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

Checkbox is an interactive widget that renders as "[ ] Label" or "[x] Label". Space and Enter toggle the checked state.

func NewCheckbox

func NewCheckbox(label string) *Checkbox

NewCheckbox creates a new Checkbox with the given label.

Example

ExampleNewCheckbox shows a teaware.Checkbox with an OnChange callback.

package main

import (
	"fmt"

	tea "charm.land/bubbletea/v2"
	"github.com/anadale/teaservice/teaware"
)

func main() {
	cb := teaware.NewCheckbox("Subscribe to newsletter").
		Checked(true).
		OnChange(func(v bool) tea.Cmd {
			fmt.Println("changed to:", v)
			return nil
		})

	fmt.Println("checked:", cb.IsChecked())
}
Output:
checked: true

func (*Checkbox) Blur

func (c *Checkbox) Blur()

Blur implements FocusOwner.

func (*Checkbox) Checked

func (c *Checkbox) Checked(v bool) *Checkbox

Checked sets the initially checked state (builder).

func (*Checkbox) Constraints

func (c *Checkbox) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Checkbox) Disabled

func (c *Checkbox) Disabled(v bool) *Checkbox

Disabled sets whether the checkbox is disabled.

func (*Checkbox) Focus

func (c *Checkbox) Focus() tea.Cmd

Focus implements FocusOwner.

func (*Checkbox) Focused

func (c *Checkbox) Focused() bool

Focused implements FocusOwner.

func (*Checkbox) IsChecked

func (c *Checkbox) IsChecked() bool

IsChecked returns the current checked state.

func (*Checkbox) OnChange

func (c *Checkbox) OnChange(fn func(bool) tea.Cmd) *Checkbox

OnChange sets a callback invoked when the checked state changes.

func (*Checkbox) SetSize

func (c *Checkbox) SetSize(_, _ int)

SetSize implements Sizer.

func (*Checkbox) Update

func (c *Checkbox) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (*Checkbox) View

func (c *Checkbox) View() string

View implements Painter.

type CheckboxTheme

type CheckboxTheme struct {
	Unchecked string
	Checked   string
	Normal    lipgloss.Style
	Focused   lipgloss.Style
	Disabled  lipgloss.Style
}

CheckboxTheme defines styles and symbols for Checkbox widgets.

type ColumnDef

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

ColumnDef defines a single column's sizing rule.

func FixedColumn

func FixedColumn(n int) ColumnDef

FixedColumn creates a ColumnDef with a fixed character width.

type Constraints

type Constraints struct {
	MinWidth, MinHeight             int
	PreferredWidth, PreferredHeight int
}

Constraints describes a widget's size preferences. Zero value means "no constraint / expand".

type ConstraintsProvider

type ConstraintsProvider interface {
	Constraints() Constraints
}

ConstraintsProvider lets a widget advertise its size preferences to the parent container.

type Container

type Container interface {
	Widget
	FocusNext() tea.Cmd
	FocusPrevious() tea.Cmd
	ReceiveFocusFromPrevious() tea.Cmd // entering via Tab from before this container → focus first child
	ReceiveFocusFromNext() tea.Cmd     // entering via Shift+Tab from after this container → focus last child
}

Container extends Widget with focus navigation methods. Exported so third-party containers can participate in the focus protocol.

type Direction

type Direction int

Direction specifies the main axis of a Flex container.

const (
	Vertical   Direction = iota // Vertical arranges children top-to-bottom.
	Horizontal                  // Horizontal arranges children left-to-right.
)

Available Direction values.

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

Dropdown is an interactive widget that shows a selected value and expands to a popup list when activated.

Closed state: renders "[ Selected ▼ ]"; Enter/Space opens the popup. Open state: registers a floating overlay via the global OverlayRegistry and returns only the closed header view; Up/Down navigate; Enter selects the highlighted option; Esc closes without changing selection.

func NewDropdown

func NewDropdown(options []string) *Dropdown

NewDropdown creates a Dropdown with the given options. The first option is selected by default when options are non-empty.

Example

ExampleNewDropdown shows a teaware.Dropdown with options and an OnChange callback.

package main

import (
	"fmt"

	tea "charm.land/bubbletea/v2"
	"github.com/anadale/teaservice/teaware"
)

func main() {
	var selected string

	drop := teaware.NewDropdown([]string{"Red", "Green", "Blue"}).
		Value("Green").
		OnChange(func(v string) tea.Cmd {
			selected = v
			return nil
		})

	fmt.Println("value:", drop.GetValue())
	_ = selected
}
Output:
value: Green
func (d *Dropdown) Blur()

Blur implements FocusOwner.

func (d *Dropdown) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (d *Dropdown) Focus() tea.Cmd

Focus implements FocusOwner.

func (d *Dropdown) Focused() bool

Focused implements FocusOwner.

func (d *Dropdown) GetValue() string

GetValue returns the currently selected option or empty string if none.

func (d *Dropdown) OnChange(fn func(string) tea.Cmd) *Dropdown

OnChange sets a callback invoked when the selected option changes.

func (d *Dropdown) SetPosition(x, y int)

SetPosition implements Positioner.

func (d *Dropdown) SetSize(width, _ int)

SetSize implements Sizer.

func (d *Dropdown) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (d *Dropdown) Value(v string) *Dropdown

Value sets the selected option by value (builder).

func (d *Dropdown) View() string

View implements Painter.

type DropdownTheme struct {
	Normal   lipgloss.Style
	Focused  lipgloss.Style
	Disabled lipgloss.Style
	Item     lipgloss.Style
	Selected lipgloss.Style
}

DropdownTheme defines styles for Dropdown widgets.

type Flex

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

Flex arranges children along a Direction with a two-pass layout.

func NewFlex

func NewFlex(dir Direction) *Flex

NewFlex creates a new Flex container with the given direction.

Example

ExampleNewFlex shows a horizontal teaware.Flex container with a gap between children.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	okBtn := teaware.NewButton("OK")
	cancelBtn := teaware.NewButton("Cancel").Style(teaware.ButtonStyleSecondary)

	row := teaware.NewFlex(teaware.Horizontal).
		Gap(2).
		Add(okBtn).
		Add(cancelBtn)

	row.SetSize(40, 1)
	_ = row.Focus()

	fmt.Println("ok focused:", okBtn.Focused())
}
Output:
ok focused: true
Example (Vertical)

ExampleNewFlex_vertical shows a vertical teaware.Flex that stacks widgets.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	col := teaware.NewFlex(teaware.Vertical).
		Gap(1).
		Add(teaware.NewTextInput().Placeholder("Username")).
		Add(teaware.NewTextInput().Placeholder("Password"))

	col.SetSize(30, 4)
	fmt.Println("vertical flex configured")
}
Output:
vertical flex configured

func (*Flex) Add

func (f *Flex) Add(child Painter, opts ...FlexItemOption) *Flex

Add appends a child with optional item options.

func (*Flex) Blur

func (f *Flex) Blur()

Blur implements FocusOwner.

func (*Flex) Constraints

func (f *Flex) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Flex) Focus

func (f *Flex) Focus() tea.Cmd

Focus implements FocusOwner.

func (*Flex) FocusNext

func (f *Flex) FocusNext() tea.Cmd

FocusNext advances focus to the next focusable child, returning FocusNextCmd when exhausted. If the currently focused child is a Container with an active child focus, focus is advanced within that container before moving to the next sibling.

func (*Flex) FocusPrevious

func (f *Flex) FocusPrevious() tea.Cmd

FocusPrevious moves focus to the previous focusable child, returning FocusPreviousCmd when exhausted. If the currently focused child is a Container with an active child focus, focus is moved backward within that container before moving to the previous sibling.

func (*Flex) Focused

func (f *Flex) Focused() bool

Focused implements FocusOwner.

func (*Flex) Gap

func (f *Flex) Gap(n int) *Flex

Gap sets the number of blank units between children along the main axis.

func (*Flex) Padding

func (f *Flex) Padding(values ...int) *Flex

Padding sets the inset around the container content using CSS shorthand notation:

Padding(all)
Padding(vertical, horizontal)
Padding(top, right, bottom, left)

func (*Flex) ReceiveFocusFromNext

func (f *Flex) ReceiveFocusFromNext() tea.Cmd

ReceiveFocusFromNext focuses the last focusable child.

func (*Flex) ReceiveFocusFromPrevious

func (f *Flex) ReceiveFocusFromPrevious() tea.Cmd

ReceiveFocusFromPrevious focuses the first focusable child.

func (*Flex) SetPosition

func (f *Flex) SetPosition(x, y int)

SetPosition implements Positioner and propagates absolute coordinates to children.

func (*Flex) SetSize

func (f *Flex) SetSize(width, height int)

SetSize implements Sizer and triggers layout for all children.

func (*Flex) Update

func (f *Flex) Update(msg tea.Msg) tea.Cmd

Update implements Updater. Key messages are routed to the focused child only; all other messages are broadcast to every child that implements Updater. FocusNextMsg and FocusPreviousMsg are intercepted to advance focus within this container.

func (*Flex) View

func (f *Flex) View() string

View implements Painter.

type FlexItemOption

type FlexItemOption func(*flexItem)

FlexItemOption is applied to an individual child in Add().

func Fixed

func Fixed(n int) FlexItemOption

Fixed sets a child to a fixed size along the main axis.

func Grow

func Grow() FlexItemOption

Grow makes a child stretch to fill remaining space along the main axis.

type FocusNextMsg

type FocusNextMsg struct{}

FocusNextMsg is returned (via FocusNextCmd) when a container has exhausted forward focus traversal. The parent container intercepts this to move focus to the next sibling. Exported so third-party containers can participate.

type FocusOwner

type FocusOwner interface {
	Updater
	Focused() bool
	Focus() tea.Cmd
	Blur()
}

FocusOwner can receive and lose keyboard focus.

type FocusPreviousMsg

type FocusPreviousMsg struct{}

FocusPreviousMsg is returned (via FocusPreviousCmd) when a container has exhausted backward focus traversal.

type Form

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

Form wraps a root Container and implements FocusOwner, Painter, and Sizer. By default it intercepts Tab/Shift+Tab and calls FocusNext/FocusPrevious automatically. Form also acts as an OverlayRegistry: during View() it collects overlays from children and composites them on top of the base view via lipgloss.Compositor.

func NewForm

func NewForm(root Container) *Form

NewForm creates a Form with autoTab enabled.

Example

ExampleNewForm demonstrates building a complete form with a Grid layout. teaware.Form wires focus navigation (Tab / Shift-Tab) and overlay rendering for widgets like teaware.Dropdown.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	nameInput := teaware.NewTextInput().Placeholder("Your name")
	emailInput := teaware.NewTextInput().Placeholder("Email")
	submitBtn := teaware.NewButton("Submit")

	form := teaware.NewForm(
		teaware.NewGrid().
			Columns(teaware.Auto, teaware.Stretch).
			ColumnGap(2).
			AddRow(teaware.NewLabel("Name:"), nameInput).
			AddRow(teaware.NewLabel("Email:"), emailInput).
			AddRow(teaware.Span(2, submitBtn)),
	)
	form.SetSize(60, 10)
	_ = form.Focus()

	fmt.Println("focused:", nameInput.Focused())
}
Output:
focused: true

func (*Form) Blur

func (f *Form) Blur()

Blur implements FocusOwner.

func (*Form) Constraints

func (f *Form) Constraints() Constraints

Constraints implements ConstraintsProvider by returning the root container's constraints.

func (*Form) Focus

func (f *Form) Focus() tea.Cmd

Focus implements FocusOwner by forwarding focus to the first focusable child.

func (*Form) Focused

func (f *Form) Focused() bool

Focused implements FocusOwner.

func (*Form) HasOpenPopups

func (f *Form) HasOpenPopups() bool

HasOpenPopups reports whether any floating overlay was registered during the last View() call. Use this to distinguish between Esc closing a popup vs quitting the application.

func (*Form) RegisterOverlay

func (f *Form) RegisterOverlay(absX, absY int, content string)

RegisterOverlay implements OverlayRegistry. Called by child widgets during View().

func (*Form) SetSize

func (f *Form) SetSize(w, h int)

SetSize implements Sizer. It propagates the dimensions to the root container and resets its position to (0, 0) if the root also implements Positioner.

func (*Form) Update

func (f *Form) Update(msg tea.Msg) tea.Cmd

Update implements Updater. It intercepts FocusNextMsg and FocusPreviousMsg to wrap the focus cycle, and — when autoTab is enabled — handles Tab/Shift+Tab before delegating all other messages to the root container.

func (*Form) View

func (f *Form) View() string

View implements Painter. It renders the root container and composites any registered overlay layers on top of the base view using lipgloss.Compositor.

func (*Form) WithAutoTab

func (f *Form) WithAutoTab(enabled bool) *Form

WithAutoTab enables or disables automatic Tab/Shift+Tab handling.

type Grid

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

Grid arranges children in rows and columns with configurable column sizing.

func NewGrid

func NewGrid() *Grid

NewGrid creates a new empty Grid.

Example

ExampleNewGrid shows a two-column teaware.Grid with Auto and Stretch columns. teaware.Auto sizes to the widest cell in the column; teaware.Stretch fills the remaining space. teaware.Span lets a widget span multiple columns.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	grid := teaware.NewGrid().
		Columns(teaware.Auto, teaware.Stretch).
		ColumnGap(1).
		AddRow(teaware.NewLabel("Name:"), teaware.NewTextInput()).
		AddRow(teaware.NewLabel("Email:"), teaware.NewTextInput()).
		AddRow(teaware.Span(2, teaware.NewSeparator(teaware.Horizontal)))

	grid.SetSize(60, 6)
	fmt.Println("grid configured")
}
Output:
grid configured
Example (FixedColumns)

ExampleNewGrid_fixedColumns shows a grid with a fixed-width first column.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	grid := teaware.NewGrid().
		Columns(teaware.FixedColumn(12), teaware.Stretch).
		AddRow(teaware.NewLabel("Name:"), teaware.NewTextInput()).
		AddRow(teaware.NewLabel("Department:"), teaware.NewTextInput())

	grid.SetSize(60, 4)
	fmt.Println("fixed columns configured")
}
Output:
fixed columns configured

func (*Grid) AddRow

func (g *Grid) AddRow(cells ...Painter) *Grid

AddRow appends a row of cells. Use Span() to have a cell span multiple columns.

func (*Grid) Blur

func (g *Grid) Blur()

Blur implements FocusOwner.

func (*Grid) ColumnGap

func (g *Grid) ColumnGap(n int) *Grid

ColumnGap sets the horizontal character gap between columns.

func (*Grid) Columns

func (g *Grid) Columns(cols ...ColumnDef) *Grid

Columns configures column definitions. Pass Auto, Stretch, or FixedColumn(n).

func (*Grid) Constraints

func (g *Grid) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Grid) Focus

func (g *Grid) Focus() tea.Cmd

Focus implements FocusOwner.

func (*Grid) FocusNext

func (g *Grid) FocusNext() tea.Cmd

FocusNext advances focus to the next focusable cell in row-major order. If the currently focused cell is a Container with an active child focus, focus is advanced within that container before moving to the next sibling. Returns FocusNextCmd when all focusable cells are exhausted.

func (*Grid) FocusPrevious

func (g *Grid) FocusPrevious() tea.Cmd

FocusPrevious moves focus to the previous focusable cell in row-major order. If the currently focused cell is a Container with an active child focus, focus is moved backward within that container before moving to the previous sibling. Returns FocusPreviousCmd when all focusable cells are exhausted.

func (*Grid) Focused

func (g *Grid) Focused() bool

Focused implements FocusOwner.

func (*Grid) Padding

func (g *Grid) Padding(values ...int) *Grid

Padding sets the inset around the grid content using CSS shorthand notation:

Padding(all)
Padding(vertical, horizontal)
Padding(top, right, bottom, left)

func (*Grid) ReceiveFocusFromNext

func (g *Grid) ReceiveFocusFromNext() tea.Cmd

ReceiveFocusFromNext focuses the last focusable cell.

func (*Grid) ReceiveFocusFromPrevious

func (g *Grid) ReceiveFocusFromPrevious() tea.Cmd

ReceiveFocusFromPrevious focuses the first focusable cell.

func (*Grid) RowGap

func (g *Grid) RowGap(n int) *Grid

RowGap sets the vertical line gap between rows.

func (*Grid) SetPosition

func (g *Grid) SetPosition(x, y int)

SetPosition implements Positioner and propagates absolute coordinates to children.

func (*Grid) SetSize

func (g *Grid) SetSize(width, height int)

SetSize implements Sizer and triggers two-pass layout for all children.

func (*Grid) Update

func (g *Grid) Update(msg tea.Msg) tea.Cmd

Update implements Updater. Key messages are routed to the focused cell only; all other messages are broadcast to every cell that implements Updater. FocusNextMsg and FocusPreviousMsg are intercepted to advance focus within this container.

func (*Grid) View

func (g *Grid) View() string

View implements Painter.

type InputTheme

type InputTheme struct {
	Normal   lipgloss.Style
	Focused  lipgloss.Style
	Disabled lipgloss.Style
}

InputTheme defines styles for TextInput widgets.

type Label

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

Label is a non-interactive text widget that renders a string with optional styling.

func NewLabel

func NewLabel(text string) *Label

NewLabel creates a new Label with the given text.

Example

ExampleNewLabel shows a non-interactive text label for form layouts.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	label := teaware.NewLabel("Username:")
	fmt.Println("label view:", label.View())
}
Output:
label view: Username:

func (*Label) Constraints

func (l *Label) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Label) SetSize

func (l *Label) SetSize(width, _ int)

SetSize implements Sizer.

func (*Label) Style

func (l *Label) Style(s lipgloss.Style) *Label

Style sets a custom style for this label, overriding the theme default.

func (*Label) View

func (l *Label) View() string

View implements Painter.

type LabelTheme

type LabelTheme struct {
	Normal lipgloss.Style
}

LabelTheme defines styles for Label widgets.

type OverlayRegistry

type OverlayRegistry interface {
	RegisterOverlay(absX, absY int, content string)
}

OverlayRegistry allows widgets to register floating overlay content at absolute coordinates.

type Padding

type Padding struct {
	Top, Right, Bottom, Left int
}

Padding holds inset values for each side of a container.

func NewPadding

func NewPadding(values ...int) Padding

NewPadding creates a Padding using CSS shorthand notation:

NewPadding(all)                     — all four sides equal
NewPadding(vertical, horizontal)    — top/bottom and left/right
NewPadding(top, right, bottom, left)

type Painter

type Painter interface {
	View() string
}

Painter renders itself to a string.

func Span

func Span(cols int, child Painter) Painter

Span wraps child to span the given number of columns in a Grid row.

type Positioner

type Positioner interface {
	SetPosition(x, y int)
}

Positioner allows a parent container to push absolute screen coordinates into a child widget.

type RadioGroup

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

RadioGroup is an interactive widget that renders a vertical list of options. Each option is displayed as "( ) Option" or "(•) Option". Up/Down navigate between options; Space/Enter selects the focused option.

func NewRadioGroup

func NewRadioGroup(options []string) *RadioGroup

NewRadioGroup creates a new RadioGroup with the given options. The first option is selected by default if options is non-empty.

Example

ExampleNewRadioGroup shows a teaware.RadioGroup with a pre-selected value.

package main

import (
	"fmt"

	tea "charm.land/bubbletea/v2"
	"github.com/anadale/teaservice/teaware"
)

func main() {
	rg := teaware.NewRadioGroup([]string{"Small", "Medium", "Large"}).
		Value("Medium").
		OnChange(func(v string) tea.Cmd {
			fmt.Println("selected:", v)
			return nil
		})

	fmt.Println("value:", rg.GetValue())
}
Output:
value: Medium

func (*RadioGroup) Blur

func (r *RadioGroup) Blur()

Blur implements FocusOwner.

func (*RadioGroup) Constraints

func (r *RadioGroup) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*RadioGroup) Disabled

func (r *RadioGroup) Disabled(v bool) *RadioGroup

Disabled sets whether the radio group is disabled.

func (*RadioGroup) Focus

func (r *RadioGroup) Focus() tea.Cmd

Focus implements FocusOwner.

func (*RadioGroup) Focused

func (r *RadioGroup) Focused() bool

Focused implements FocusOwner.

func (*RadioGroup) GetValue

func (r *RadioGroup) GetValue() string

GetValue returns the currently selected option value, or empty string if none.

func (*RadioGroup) OnChange

func (r *RadioGroup) OnChange(fn func(string) tea.Cmd) *RadioGroup

OnChange sets a callback invoked when the selected option changes.

func (*RadioGroup) SetSize

func (r *RadioGroup) SetSize(_, _ int)

SetSize implements Sizer.

func (*RadioGroup) Update

func (r *RadioGroup) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (*RadioGroup) Value

func (r *RadioGroup) Value(v string) *RadioGroup

Value sets the selected option by value (builder).

func (*RadioGroup) View

func (r *RadioGroup) View() string

View implements Painter.

type RadioTheme

type RadioTheme struct {
	Unselected string
	Selected   string
	Normal     lipgloss.Style
	Focused    lipgloss.Style
	Disabled   lipgloss.Style
}

RadioTheme defines styles and symbols for RadioGroup widgets.

type Separator

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

Separator is a non-interactive line widget: horizontal (─) or vertical (│).

func NewSeparator

func NewSeparator(dir Direction) *Separator

NewSeparator creates a new Separator in the given direction.

Example

ExampleNewSeparator shows horizontal and vertical separators.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	_ = teaware.NewSeparator(teaware.Horizontal)
	_ = teaware.NewSeparator(teaware.Vertical)
	fmt.Println("separators created")
}
Output:
separators created

func (*Separator) Constraints

func (s *Separator) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Separator) SetSize

func (s *Separator) SetSize(width, height int)

SetSize implements Sizer.

func (*Separator) View

func (s *Separator) View() string

View implements Painter.

type SeparatorTheme

type SeparatorTheme struct {
	Normal lipgloss.Style
}

SeparatorTheme defines styles for Separator widgets.

type SizeMode

type SizeMode int

SizeMode controls how a child is sized along the main axis.

const (
	SizeModePreferred SizeMode = iota // SizeModePreferred uses the child's preferred size (or min size if preferred is zero).
	SizeModeFixed                     // SizeModeFixed allocates a fixed number of cells along the main axis.
	SizeModeStretch                   // SizeModeStretch expands the child to fill the remaining space along the main axis.
)

Available SizeMode values.

type Sizer

type Sizer interface {
	SetSize(width, height int)
}

Sizer lets the parent container push the calculated size into the widget.

type Spacer

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

Spacer is a non-interactive widget that fills available space. In horizontal layouts it renders as spaces of the given width, enabling right-alignment. In vertical layouts it renders as an empty line.

func NewSpacer

func NewSpacer() *Spacer

NewSpacer creates a new Spacer.

func (*Spacer) Constraints

func (s *Spacer) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Spacer) SetSize

func (s *Spacer) SetSize(w, h int)

SetSize implements Sizer.

func (*Spacer) View

func (s *Spacer) View() string

View implements Painter.

type TextInput

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

TextInput is a single-line text input widget wrapping charm.land/bubbles/v2/textinput.

func NewTextInput

func NewTextInput() *TextInput

NewTextInput creates a new TextInput with default settings.

Example

ExampleNewTextInput shows configuring a teaware.TextInput with placeholder, initial value, character limit, and an OnChange callback.

package main

import (
	"fmt"

	tea "charm.land/bubbletea/v2"
	"github.com/anadale/teaservice/teaware"
)

func main() {
	var lastValue string

	input := teaware.NewTextInput().
		Placeholder("Search…").
		Value("initial").
		CharLimit(100).
		OnChange(func(v string) tea.Cmd {
			lastValue = v
			return nil
		})

	fmt.Println("value:", input.GetValue())
	_ = lastValue
}
Output:
value: initial

func (*TextInput) Blur

func (t *TextInput) Blur()

Blur implements FocusOwner.

func (*TextInput) CharLimit

func (t *TextInput) CharLimit(n int) *TextInput

CharLimit sets the maximum number of characters allowed.

func (*TextInput) Constraints

func (t *TextInput) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*TextInput) Focus

func (t *TextInput) Focus() tea.Cmd

Focus implements FocusOwner.

func (*TextInput) Focused

func (t *TextInput) Focused() bool

Focused implements FocusOwner.

func (*TextInput) GetValue

func (t *TextInput) GetValue() string

GetValue returns the current value.

func (*TextInput) OnChange

func (t *TextInput) OnChange(fn func(string) tea.Cmd) *TextInput

OnChange sets a callback invoked when the value changes and passes validation.

func (*TextInput) OnSubmit

func (t *TextInput) OnSubmit(fn func(string) tea.Cmd) *TextInput

OnSubmit sets a callback invoked when Enter is pressed.

func (*TextInput) Placeholder

func (t *TextInput) Placeholder(s string) *TextInput

Placeholder sets the placeholder text shown when the input is empty.

func (*TextInput) SetSize

func (t *TextInput) SetSize(width, _ int)

SetSize implements Sizer.

func (*TextInput) Update

func (t *TextInput) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (*TextInput) Validate

func (t *TextInput) Validate(fn func(string) error) *TextInput

Validate sets a validation function called on every value change. When it returns a non-nil error, OnChange is not fired.

func (*TextInput) Value

func (t *TextInput) Value(s string) *TextInput

Value sets the current value (builder).

func (*TextInput) View

func (t *TextInput) View() string

View implements Painter.

type Textarea

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

Textarea is a multi-line text input widget wrapping charm.land/bubbles/v2/textarea.

func NewTextarea

func NewTextarea() *Textarea

NewTextarea creates a new Textarea with default settings.

Example

ExampleNewTextarea shows a multi-line teaware.Textarea.

package main

import (
	"fmt"

	"github.com/anadale/teaservice/teaware"
)

func main() {
	area := teaware.NewTextarea().
		Placeholder("Enter notes…").
		Lines(4)

	fmt.Println("lines configured:", area.GetValue() == "")
}
Output:
lines configured: true

func (*Textarea) Blur

func (t *Textarea) Blur()

Blur implements FocusOwner.

func (*Textarea) Constraints

func (t *Textarea) Constraints() Constraints

Constraints implements ConstraintsProvider.

func (*Textarea) Focus

func (t *Textarea) Focus() tea.Cmd

Focus implements FocusOwner.

func (*Textarea) Focused

func (t *Textarea) Focused() bool

Focused implements FocusOwner.

func (*Textarea) GetValue

func (t *Textarea) GetValue() string

GetValue returns the current value.

func (*Textarea) Lines

func (t *Textarea) Lines(n int) *Textarea

Lines sets the preferred height in content lines (not counting borders).

func (*Textarea) OnChange

func (t *Textarea) OnChange(fn func(string) tea.Cmd) *Textarea

OnChange sets a callback invoked when the value changes.

func (*Textarea) Placeholder

func (t *Textarea) Placeholder(s string) *Textarea

Placeholder sets the placeholder text shown when the textarea is empty.

func (*Textarea) SetSize

func (t *Textarea) SetSize(width, height int)

SetSize implements Sizer.

func (*Textarea) Update

func (t *Textarea) Update(msg tea.Msg) tea.Cmd

Update implements Updater.

func (*Textarea) View

func (t *Textarea) View() string

View implements Painter.

type Theme

type Theme struct {
	Label           LabelTheme
	Separator       SeparatorTheme
	Input           InputTheme
	Button          ButtonTheme
	ButtonSecondary ButtonTheme
	Checkbox        CheckboxTheme
	Radio           RadioTheme
	Dropdown        DropdownTheme
}

Theme defines styles for all widget types.

func CurrentTheme

func CurrentTheme() Theme

CurrentTheme returns the active theme.

func DefaultTheme

func DefaultTheme() Theme

DefaultTheme returns the default TurboVision-inspired theme. Buttons use solid fill with no border; checkbox/radio symbols are configurable.

type Updater

type Updater interface {
	Update(tea.Msg) tea.Cmd
}

Updater handles bubbletea messages.

type Widget

type Widget interface {
	FocusOwner
	Painter
	Sizer
	ConstraintsProvider
}

Widget is the full interface for interactive widgets.

Jump to

Keyboard shortcuts

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