tui

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package tui provides terminal user interface components.

Package tui provides terminal user interface components.

Index

Constants

View Source
const DefaultAnimation = "outline-in"

DefaultAnimation is the animation used when none is specified.

View Source
const Wordmark = "" +
	"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣶⣶⣶⣶⣶⣶⣦⣤⣀\n" +
	"⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⣿⣿⣿⠿⠿⠛⠛⠛⠻⠿⣿⣿⣿⣦⣀\n" +
	"⠀⠀⠀⠀⠀⢀⣴⣿⣿⡿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⣿⣿⣦⡀\n" +
	"⠀⠀⠀⠀⣴⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⣿⣄\n" +
	"⠀⠀⢀⣼⣿⣿⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⡀⠀⠀⠀⠀⢻⣿⣿⣆\n" +
	"⠀⢀⣾⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⠃⠀⠀⠀⠀⠀⢻⣿⣿⡄\n" +
	"⠀⣼⣿⣿⠃⠀⠀⣠⣶⣿⣷⣦⣄⠀⠀⢀⣼⣿⣿⠃⠀⠀⠀⠀⠀⠀⠀⢿⣿⣿⡀\n" +
	"⢸⣿⣿⠇⠀⢠⣾⣿⡿⠛⠻⣿⣿⣷⣤⣾⣿⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣇\n" +
	"⠈⠉⠉⠀⢠⣿⣿⡟⠁⠀⠀⠈⠻⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣿⣿\n" +
	"⠀⠀⠀⢠⣿⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡇\n" +
	"⠀⠀⠀⢻⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⣿⣿⠇\n" +
	"⠀⠀⠀⠀⠙⠿⣿⣿⣷⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣶⣿⣿⡿⠋\n" +
	"⠀⠀⠀⠀⠀⠀⠈⠛⠿⣿⣿⣿⣿⣶⣶⣶⣶⣶⣶⣶⣶⣶⣿⣿⣿⣿⡿⠟⠉\n" +
	"⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠙⠛⠛⠿⠿⠿⠿⠿⠿⠟⠛⠛⠉⠉"

Wordmark is a braille-art rendering of the Basecamp mountain logo.

View Source
const WordmarkLines = 14

WordmarkLines is the number of lines in the Wordmark.

View Source
const WordmarkWidth = 32

WordmarkWidth is the display width (rune count of widest line) of the Wordmark.

Variables

View Source
var BrandColor = lipgloss.Color("#e8a217")

BrandColor is the fixed Basecamp brand yellow, independent of theme.

View Source
var ErrCanceled = errors.New("prompt canceled")

ErrCanceled is returned when the user dismissed a prompt — Escape or Ctrl+C. It is an answer, and callers are right to treat it as "no" rather than as a failure.

It exists so they can do that *precisely*. huh returns ErrUserAborted only for a real dismissal; a timeout, or a bubbletea or runtime failure, comes back as an ordinary error. Callers that treat every prompt error as cancellation therefore report an answer nobody gave, and exit 0 having done nothing — the same mistake as claiming an install was canceled when the prompt never appeared. Translate once here so no caller has to know huh's error values.

View Source
var ErrNotInteractive = errors.New("not an interactive terminal")

ErrNotInteractive is returned instead of launching a form when stdio cannot drive one. huh runs the form as a bubbletea program, and redirecting stdin does not make that program fail: bubbletea v1 sees a non-terminal stdin and silently opens /dev/tty instead (tea.go:590-613), so the prompt sits waiting on the real terminal — a hang for a caller that redirected stdin precisely because nobody is there to type. Where there is no controlling terminal it fails instead, but only partway through, after the earlier steps have run. Neither outcome is usable, so refuse rather than launch.

The floor lives here rather than at the call sites because a call-site audit is exactly what missed `basecamp setup`: huh calls tea.NewProgram inside form.go, so grepping for the launcher cannot see these functions at all. Gating the constructor bounds where a prompt can be reached, and covers the prompts nobody has written yet.

Functions

func AnimateWordmark added in v0.3.0

func AnimateWordmark(w io.Writer, theme Theme)

AnimateWordmark draws the wordmark with a paint animation. Honors BASECAMP_ANIM to override the default strategy.

func AnimateWordmarkAsync added in v0.3.0

func AnimateWordmarkAsync(w io.Writer, theme Theme) (io.Writer, func())

AnimateWordmarkAsync starts the animation in a background goroutine and returns a writer for subsequent output plus a wait function. Output written through the returned writer appears below the animating logo. Call wait before any interactive prompts or output that bypasses the returned writer. Falls back to static render (returning w unchanged and no-op wait) when not a TTY or NO_COLOR is active.

func AnimateWordmarkWith added in v0.3.0

func AnimateWordmarkWith(w io.Writer, theme Theme, strategy string)

AnimateWordmarkWith draws the wordmark using the named animation strategy. Unknown strategy names fall back to DefaultAnimation. Falls back to static render if w is not a TTY or NO_COLOR is active.

func AnimationNames added in v0.3.0

func AnimationNames() []string

AnimationNames returns the registered animation strategy names.

func Confirm

func Confirm(message string, defaultValue bool) (bool, error)

Confirm shows a yes/no confirmation prompt. Escape or Ctrl+C cancels.

func ConfirmDangerous

func ConfirmDangerous(message string) (bool, error)

ConfirmDangerous shows a confirmation prompt for dangerous actions. Escape or Ctrl+C cancels.

func ConfirmSetDefault

func ConfirmSetDefault(valueName string) (bool, error)

ConfirmSetDefault asks the user if they want to save a value as the default. Escape or Ctrl+C cancels.

func DetectDark added in v0.3.0

func DetectDark() bool

DetectDark returns true if the terminal has a dark background. Non-TTY (piped, tests, CI) defaults to true for deterministic output.

func Form

func Form(title string, fields []FormField) (map[string]string, error)

Form shows a multi-field form and returns a map of key -> value.

func Input

func Input(title, placeholder string) (string, error)

Input shows a text input prompt. Escape or Ctrl+C cancels.

func InputRequired

func InputRequired(title, placeholder string) (string, error)

InputRequired shows a required text input prompt. Escape or Ctrl+C cancels.

func MultiSelect

func MultiSelect(title string, options []SelectOption) ([]string, error)

MultiSelect shows a multi-select prompt. Escape or Ctrl+C cancels.

func Note

func Note(title, body string) error

Note shows an informational note. It takes no input, but huh still runs it as a bubbletea program reading os.Stdin, so it needs the same floor.

func RenderSnowglobe added in v0.10.0

func RenderSnowglobe(theme Theme) string

RenderSnowglobe renders the current Basecamp snowglobe mark as static terminal art. Colored terminals use half-block pixels for the blue glass and green mountain; NO_COLOR uses a two-tone text rendering.

func RenderWordmark added in v0.3.0

func RenderWordmark(theme Theme) string

RenderWordmark renders the wordmark in Basecamp brand yellow with "Basecamp" to the right of the logo. Returns plain text with NoColorTheme.

func RunWithSpinner added in v0.10.0

func RunWithSpinner(w io.Writer, theme Theme, message string, task func() error) error

RunWithSpinner displays message while task runs on a terminal, clears the transient line when task finishes, and returns the task error. Fast tasks and non-terminal writers complete without transient output.

func Select

func Select(title string, options []SelectOption) (string, error)

Select shows a single-select prompt. Escape or Ctrl+C cancels.

func SelectScope

func SelectScope() (string, error)

SelectScope shows a prompt for selecting the config scope (global or local). It inherits Select's floor, so it returns ErrNotInteractive off a terminal.

func SelectWithDescription

func SelectWithDescription(title, description string, options []SelectOption) (string, error)

SelectWithDescription shows a select prompt with descriptions. Escape or Ctrl+C cancels.

func TextArea

func TextArea(title, placeholder string) (string, error)

TextArea shows a multiline text input prompt. Escape or Ctrl+C cancels.

func ThemeFilePath added in v0.2.0

func ThemeFilePath() string

ThemeFilePath returns the resolved path to the active theme file, or "" if using defaults or NO_COLOR is set.

Types

type AnimWriter added in v0.3.0

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

AnimWriter is a mutex-protected io.Writer that tracks terminal rows written below the animation area. The animation goroutine uses this count to cursor-up past both the logo and any caller output, then cursor-down to restore the caller's write position. When cols > 0, it accounts for soft-wrapping at the terminal width.

func (*AnimWriter) Wait added in v0.3.0

func (aw *AnimWriter) Wait()

Wait blocks until the animation goroutine finishes. Idempotent.

func (*AnimWriter) Write added in v0.3.0

func (aw *AnimWriter) Write(p []byte) (int, error)

Write writes p through to the underlying writer and counts visual rows consumed, including rows added by soft-wrapping at the terminal width.

type FormField

type FormField struct {
	Key         string
	Title       string
	Placeholder string
	Required    bool
	Default     string
}

FormField represents a field in a form.

type ItemLoader

type ItemLoader func() ([]PickerItem, error)

ItemLoader is a function that loads items asynchronously.

type Picker

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

Picker shows a fuzzy-search picker and returns the selected item.

func NewPicker

func NewPicker(items []PickerItem, opts ...PickerOption) *Picker

NewPicker creates a new picker.

func NewPickerWithLoader

func NewPickerWithLoader(loader ItemLoader, opts ...PickerOption) *Picker

NewPickerWithLoader creates a picker that loads items asynchronously.

func (*Picker) Run

func (p *Picker) Run() (*PickerItem, error)

Run shows the picker and returns the selected item. Returns nil if the user canceled, and ErrNotInteractive when stdio cannot drive a TUI at all — except for the single-item WithAutoSelectSingle fast path, which resolves without a TUI and so works anywhere.

type PickerItem

type PickerItem struct {
	ID          string
	Title       string
	Description string
}

PickerItem represents an item in a picker.

func Pick

func Pick(title string, items []PickerItem) (*PickerItem, error)

Pick is a convenience function for simple picking.

func PickAccount

func PickAccount(accounts []PickerItem) (*PickerItem, error)

PickAccount shows a picker for accounts.

func PickHost

func PickHost(hosts []PickerItem) (*PickerItem, error)

PickHost shows a picker for hosts/environments.

func PickPerson

func PickPerson(people []PickerItem) (*PickerItem, error)

PickPerson shows a picker for people.

func PickProject

func PickProject(projects []PickerItem) (*PickerItem, error)

PickProject shows a picker for projects.

func PickTodolist

func PickTodolist(todolists []PickerItem) (*PickerItem, error)

PickTodolist shows a picker for todolists.

func (PickerItem) FilterValue

func (i PickerItem) FilterValue() string

FilterValue returns the string to filter on.

func (PickerItem) String

func (i PickerItem) String() string

type PickerItemsLoadedMsg

type PickerItemsLoadedMsg struct {
	Items []PickerItem
	Err   error
}

PickerItemsLoadedMsg is sent when items are loaded asynchronously.

type PickerOption

type PickerOption func(*pickerModel)

PickerOption configures a picker.

func WithAutoSelectSingle

func WithAutoSelectSingle() PickerOption

WithAutoSelectSingle automatically selects and returns if only one item exists.

func WithEmptyMessage

func WithEmptyMessage(msg string) PickerOption

WithEmptyMessage sets a custom message shown when no items are available.

func WithHelp

func WithHelp(show bool) PickerOption

WithHelp shows keyboard shortcuts in the picker.

func WithLoading

func WithLoading(msg string) PickerOption

WithLoading sets the picker to start in loading state.

func WithMaxVisible

func WithMaxVisible(n int) PickerOption

WithMaxVisible sets the maximum number of visible items.

func WithPickerTitle

func WithPickerTitle(title string) PickerOption

WithPickerTitle sets the picker title.

func WithRecentItems

func WithRecentItems(items []PickerItem) PickerOption

WithRecentItems prepends recently used items to the list, marked with a prefix.

type SelectOption

type SelectOption struct {
	Value string
	Label string
}

SelectOption represents an option in a select prompt.

type Styles

type Styles struct {

	// Text styles
	Title    lipgloss.Style
	Subtitle lipgloss.Style
	Heading  lipgloss.Style
	Body     lipgloss.Style
	Muted    lipgloss.Style
	Bold     lipgloss.Style
	Success  lipgloss.Style
	Warning  lipgloss.Style
	Error    lipgloss.Style

	// Container styles
	Box   lipgloss.Style
	Card  lipgloss.Style
	Panel lipgloss.Style

	// Interactive styles
	Focused  lipgloss.Style
	Selected lipgloss.Style
	Cursor   lipgloss.Style

	// Status styles
	StatusOK    lipgloss.Style
	StatusError lipgloss.Style
	StatusInfo  lipgloss.Style
	// contains filtered or unexported fields
}

Styles holds the styled components for the TUI.

func NewStyles

func NewStyles() *Styles

NewStyles creates a new Styles with the default dark theme.

func NewStylesWithTheme

func NewStylesWithTheme(theme Theme) *Styles

NewStylesWithTheme creates a new Styles with a custom theme.

func (*Styles) RenderCheckbox

func (s *Styles) RenderCheckbox(checked bool, label string) string

RenderCheckbox renders a checkbox item.

func (*Styles) RenderKeyValue

func (s *Styles) RenderKeyValue(key, value string) string

RenderKeyValue renders a key-value pair.

func (*Styles) RenderStatus

func (s *Styles) RenderStatus(ok bool, message string) string

RenderStatus renders a status message with appropriate styling.

func (*Styles) RenderTitle

func (s *Styles) RenderTitle(title string, subtitle ...string) string

RenderTitle renders a title with optional subtitle.

func (*Styles) Theme

func (s *Styles) Theme() Theme

Theme returns the current theme.

func (*Styles) UpdateTheme added in v0.2.0

func (s *Styles) UpdateTheme(theme Theme)

UpdateTheme re-applies a theme to the existing Styles in place. Because all components hold a *Styles pointer, the next View() call picks up the new colors with zero propagation.

type Theme

type Theme struct {
	Dark       bool // how this theme was resolved (for downstream LightDark calls)
	Primary    color.Color
	Secondary  color.Color
	Success    color.Color
	Warning    color.Color
	Error      color.Color
	Muted      color.Color
	Background color.Color
	Foreground color.Color
	Border     color.Color
	RoomColors []color.Color
}

Theme defines the color palette for the TUI.

func DefaultTheme

func DefaultTheme(dark bool) Theme

DefaultTheme returns the default basecamp theme resolved for the given background.

func LoadThemeFromFile

func LoadThemeFromFile(path string, dark bool) (Theme, error)

LoadThemeFromFile parses a colors.toml file and returns a Theme.

func LoadUserTheme

func LoadUserTheme(dark bool) (Theme, error)

LoadUserTheme attempts to load a theme from the user's basecamp config. The theme directory can be a symlink to another theme system.

func NoColorTheme

func NoColorTheme() Theme

NoColorTheme returns a theme with empty colors (honors NO_COLOR standard). lipgloss.NoColor{} means "no styling", resulting in plain text output.

func ResolveTheme

func ResolveTheme(dark bool) Theme

ResolveTheme loads a theme with the following precedence:

  1. NO_COLOR env var set → returns NoColorTheme (industry standard)
  2. BASECAMP_THEME env var → parse custom colors.toml file
  3. User theme from ~/.config/basecamp/theme/colors.toml
  4. Default basecamp theme

On systems like Omarchy, users can symlink to their system theme:

ln -s ~/.config/omarchy/current/theme ~/.config/basecamp/theme

type TraceFunc added in v0.3.0

type TraceFunc func(grid [][]rune, numLines int) []paintCell

TraceFunc computes the order in which non-blank cells are revealed. It receives the rune grid and returns cells in first-visit order.

Directories

Path Synopsis
Package empty provides empty state messages for TUI components.
Package empty provides empty state messages for TUI components.
Package format provides formatting helpers for TUI components.
Package format provides formatting helpers for TUI components.
Package recents provides a store for tracking recently used items.
Package recents provides a store for tracking recently used items.
Package resolve provides interactive prompts for resolving missing CLI options.
Package resolve provides interactive prompts for resolving missing CLI options.
Package workspace provides the persistent TUI application.
Package workspace provides the persistent TUI application.
chrome
Package chrome provides always-visible shell components for the workspace.
Package chrome provides always-visible shell components for the workspace.
views
Package views provides the individual screens for the workspace TUI.
Package views provides the individual screens for the workspace TUI.
widget
Package widget provides reusable TUI components.
Package widget provides reusable TUI components.

Jump to

Keyboard shortcuts

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