teamodal

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2026 License: MIT, MIT Imports: 18 Imported by: 0

README

TeaModal — A Modal Dialog Component for Bubble Tea Apps

A TUI modal dialog component for FULL SCREEN Bubble Tea, with centered positioning and clean message consumption pattern.

Note: This component uses absolute positioning and overlay rendering, designed for full-screen terminal UIs (with tea.WithAltScreen()). It is not suitable for inline CLI prompts.

Features

  • Full-Screen TUI Design: Built for terminal UIs with absolute positioning and overlay rendering
  • Centered Positioning: Automatically centers modal dialog in the screen
  • Multiple Modal Types: Yes/No confirmations and OK alerts
  • Button Focus: Tab to switch between buttons, Enter to confirm
  • Mouse Support: Click buttons to select, hover for visual feedback
  • Customizable Styling: Full control over borders, title, message, and button appearance via lipgloss
  • Customizable Keys: Rebind keyboard shortcuts via Keys field
  • Modal Behavior: Clean message consumption pattern - doesn't "infect" parent Update()
  • String Compositing: Overlay approach for seamless integration with parent views
  • Fluent API: Wither methods for runtime property updates

Installation

# Currently part of gommod module
# Future standalone: go get github.com/mikeschinkel/go-tealeaves/teamodal

Quick Start

Yes/No Confirmation Dialog

package main

import (
	"fmt"
	"os"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/mikeschinkel/go-tealeaves/teamodal"
)

type model struct {
	confirmDialog teamodal.ModalModel
	confirmed     bool
}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	var modal tea.Model

	// Let modal handle message first
	modal, cmd = m.confirmDialog.Update(msg)
	if cmd != nil {
		m.confirmDialog = modal.(teamodal.ModalModel)
		return m, cmd
	}

	// Modal didn't handle - parent processes
	switch msg := msg.(type) {
	case teamodal.AnsweredYesMsg:
		m.confirmed = true
		return m, tea.Quit

	case teamodal.AnsweredNoMsg, teamodal.ClosedMsg:
		return m, tea.Quit

	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			return m, tea.Quit
		case "space":
			m.confirmDialog, cmd = m.confirmDialog.Open()
			return m, cmd
		}

	case tea.WindowSizeMsg:
		m.confirmDialog = m.confirmDialog.SetSize(msg.Width, msg.Height)
		return m, nil
	}

	return m, nil
}

func (m model) View() string {
	baseView := "Press Space to open confirmation, q to quit\n\n"

	if m.confirmed {
		baseView += "User confirmed!"
	}

	// Composite modal if open (automatic positioning)
	return m.confirmDialog.OverlayModal(baseView)
}

func main() {
	modal := teamodal.NewYesNoModal("Do you want to proceed?", &teamodal.ModelArgs{
		ScreenWidth:  80,
		ScreenHeight: 24,
		Title:        "Confirmation",
		DefaultYes:   true,
	})

	p := tea.NewProgram(model{
		confirmDialog: modal,
	}, tea.WithAltScreen())

	if _, err := p.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

OK Alert Dialog

modal := teamodal.NewOKModal("Operation completed successfully!", &teamodal.ModelArgs{
	ScreenWidth:  80,
	ScreenHeight: 24,
	Title:        "Success",
})

API Reference

Types

ModalType
type ModalType int

const (
	ModalTypeOK     ModalType = iota // Single OK button (alert)
	ModalTypeYesNo                   // Yes and No buttons (confirmation)
)
ModalKeyMap
type ModalKeyMap struct {
	Confirm      key.Binding // enter - confirm selection
	Cancel       key.Binding // esc - cancel/close
	NextButton   key.Binding // tab - move to next button (YesNo only)
	PrevButton   key.Binding // shift+tab - move to previous button (YesNo only)
	SelectLeft   key.Binding // left - select left button (Yes) (YesNo only)
	SelectRight  key.Binding // right - select right button (No) (YesNo only)
}

Keyboard bindings for modal dialogs. Access via modal.Keys field. Use DefaultModalKeyMap() to get default bindings.

ModalModel
type ModalModel struct {
	Keys ModalKeyMap // Keyboard bindings (customizable)

	// All fields below are private - access via getter methods
	// Content: Title(), Message(), Type()
	// Button labels: YesLabel(), NoLabel(), OKLabel()
	// State: IsOpen(), FocusButton(), ScreenWidth(), ScreenHeight()
	// Styling: BorderStyle(), TitleStyle(), MessageStyle(), ButtonStyle(), FocusedButtonStyle()
}

Note: All model fields are private. Use getter methods like m.Title(), m.IsOpen(), m.ScreenWidth(), etc. to access values. Use wither methods like m.WithTitle("New Title") to create modified copies.

Messages
// Sent when user confirms with Enter on Yes button
type AnsweredYesMsg struct{}

// Sent when user selects No button and presses Enter
type AnsweredNoMsg struct{}

// Sent when user closes alert (OK) or cancels with Esc
type ClosedMsg struct{}

Constructors

func NewOKModal(message string, args *ModelArgs) ModalModel

func NewYesNoModal(message string, args *ModelArgs) ModalModel

type ModelArgs struct {
	ScreenWidth  int
	ScreenHeight int
	Title        string
	DefaultYes   bool   // For YesNo: default focus to Yes (true) or No (false)
	YesLabel     string // Custom Yes button label
	NoLabel      string // Custom No button label
	OKLabel      string // Custom OK button label

	// Alignment (optional - defaults to lipgloss.Center)
	TextAlign    lipgloss.Position // Horizontal alignment for title and message
	TitleAlign   lipgloss.Position // Horizontal alignment for title (overrides TextAlign)
	MessageAlign lipgloss.Position // Horizontal alignment for message (overrides TextAlign)
	ButtonAlign  lipgloss.Position // Horizontal alignment for buttons (defaults to Center)

	// Optional styling
	BorderStyle        lipgloss.Style
	TitleStyle         lipgloss.Style
	MessageStyle       lipgloss.Style
	ButtonStyle        lipgloss.Style
	FocusedButtonStyle lipgloss.Style
}

Methods

func (m ModalModel) Init() tea.Cmd
func (m ModalModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)
func (m ModalModel) View() string

Standard Bubble Tea model interface.

Important: Update() returns a non-nil tea.Cmd when it handles a message, nil when it doesn't. Parent should check if cmd != nil to determine if message was consumed.

func (m ModalModel) Open() (ModalModel, tea.Cmd)

Opens the modal and returns the updated model.

func (m ModalModel) Close() (ModalModel, tea.Cmd)

Closes the modal and returns the updated model.

func (m ModalModel) SetSize(width, height int) ModalModel

Updates screen dimensions. Call when handling tea.WindowSizeMsg.

Getter Methods (access private fields):

func (m ModalModel) Title() string
func (m ModalModel) Message() string
func (m ModalModel) Type() ModalType
func (m ModalModel) YesLabel() string
func (m ModalModel) NoLabel() string
func (m ModalModel) OKLabel() string
func (m ModalModel) IsOpen() bool
func (m ModalModel) FocusButton() int
func (m ModalModel) ScreenWidth() int
func (m ModalModel) ScreenHeight() int
func (m ModalModel) BorderStyle() lipgloss.Style
func (m ModalModel) TitleStyle() lipgloss.Style
func (m ModalModel) MessageStyle() lipgloss.Style
func (m ModalModel) ButtonStyle() lipgloss.Style
func (m ModalModel) FocusedButtonStyle() lipgloss.Style
func (m ModalModel) TitleAlign() lipgloss.Position
func (m ModalModel) MessageAlign() lipgloss.Position
func (m ModalModel) ButtonAlign() lipgloss.Position

Wither Methods (create modified copies):

func (m ModalModel) WithTitle(title string) ModalModel
func (m ModalModel) WithMessage(message string) ModalModel
func (m ModalModel) WithYesLabel(label string) ModalModel
func (m ModalModel) WithNoLabel(label string) ModalModel
func (m ModalModel) WithOKLabel(label string) ModalModel
func (m ModalModel) WithBorderStyle(style lipgloss.Style) ModalModel
func (m ModalModel) WithTitleStyle(style lipgloss.Style) ModalModel
func (m ModalModel) WithMessageStyle(style lipgloss.Style) ModalModel
func (m ModalModel) WithButtonStyle(style lipgloss.Style) ModalModel
func (m ModalModel) WithFocusedButtonStyle(style lipgloss.Style) ModalModel
func (m ModalModel) WithTextAlign(align lipgloss.Position) ModalModel
func (m ModalModel) WithTitleAlign(align lipgloss.Position) ModalModel
func (m ModalModel) WithMessageAlign(align lipgloss.Position) ModalModel
func (m ModalModel) WithButtonAlign(align lipgloss.Position) ModalModel

Wither methods return a new ModalModel with the specified property modified.

Overlay Functions

func (m ModalModel) OverlayModal(background string) string

Automatically overlays the modal centered on the background view. Handles positioning internally.

Example:

func (m model) View() string {
	baseView := renderYourView()
	return m.modal.OverlayModal(baseView) // Automatic centering
}
Function: OverlayModal (Advanced)
func OverlayModal(background, foreground string, row, col int) string

Manually overlays modal at specified position. Use when you need full control over positioning.

Parameters:

  • background: The base view (fully rendered string with ANSI codes)
  • foreground: The modal view (fully rendered string with ANSI codes)
  • row: Line number in background where foreground row 0 should appear (0-indexed)
  • col: Display column in background where foreground col 0 should appear (0-indexed)

Example:

func (m model) View() string {
	baseView := renderYourView()
	if m.modal.IsOpen() {
		modalView := m.modal.View()
		row, col := calculateCustomPosition()
		return teamodal.OverlayModal(baseView, modalView, row, col)
	}
	return baseView
}
func EnsureTermGetSize(fd uintptr) (w int, h int, ok bool)

Robust terminal size detection with IDE fallbacks. Call before starting your Bubble Tea program if experiencing terminal size detection issues.

Alignment

Control horizontal alignment of text and buttons using standard lipgloss.Position constants.

Buttons default to centered (standard modal UX). Text content can be easily aligned:

// Left-align text content (title and message), buttons stay centered
modal := teamodal.NewYesNoModal("Proceed?", &teamodal.ModelArgs{
	ScreenWidth:  80,
	ScreenHeight: 24,
	Title:        "Confirmation",
	TextAlign:    lipgloss.Left, // Affects title and message
})

// Or control title and message individually
modal := teamodal.NewYesNoModal("Proceed?", &teamodal.ModelArgs{
	ScreenWidth:  80,
	ScreenHeight: 24,
	Title:        "Confirmation",
	TitleAlign:   lipgloss.Center, // Center title
	MessageAlign: lipgloss.Left,   // Left-align message
	// ButtonAlign defaults to Center
})

// Using fluent methods
modal = modal.WithTextAlign(lipgloss.Left)
// Or individually
modal = modal.WithTitleAlign(lipgloss.Center).
              WithMessageAlign(lipgloss.Left)

// Buttons can be overridden if needed (rare)
modal = modal.WithButtonAlign(lipgloss.Right)

Alignment Options:

  • lipgloss.Left - Align to left edge
  • lipgloss.Center - Center align (default)
  • lipgloss.Right - Align to right edge

Keyboard Customization

Customize keyboard shortcuts via the Keys field:

modal := teamodal.NewYesNoModal("Proceed?", &teamodal.ModelArgs{
	ScreenWidth:  80,
	ScreenHeight: 24,
})

// Customize key bindings
modal.Keys.Confirm = key.NewBinding(key.WithKeys("y"))     // 'y' to confirm
modal.Keys.Cancel = key.NewBinding(key.WithKeys("n"))      // 'n' to cancel
modal.Keys.NextButton = key.NewBinding(key.WithKeys("tab"))

Default Key Bindings:

  • enter - Confirm selection
  • esc - Cancel/close
  • tab - Next button (YesNo modals)
  • shift+tab - Previous button (YesNo modals)
  • left - Select Yes button (YesNo modals)
  • right - Select No button (YesNo modals)

Mouse Support

The modal supports full mouse interaction:

  • Click buttons - Click any button to select and confirm
  • Hover feedback - Button focus follows mouse hover (YesNo modals)
  • Automatic detection - Modal tracks its position for accurate click detection

No additional code needed - mouse support works automatically.

Wither Methods

Update modal properties at runtime using fluent wither methods:

// Update content
modal = modal.WithTitle("New Title").
              WithMessage("Updated message")

// Update button labels
modal = modal.WithYesLabel("Accept").
              WithNoLabel("Decline").
              WithOKLabel("Got it")

// Update styling
modal = modal.WithBorderStyle(myBorderStyle).
              WithTitleStyle(myTitleStyle).
              WithFocusedButtonStyle(myFocusStyle)

Available Withers:

  • WithTitle(string), WithMessage(string)
  • WithYesLabel(string), WithNoLabel(string), WithOKLabel(string)
  • WithTextAlign(lipgloss.Position) - Sets both title and message alignment
  • WithTitleAlign(lipgloss.Position), WithMessageAlign(lipgloss.Position), WithButtonAlign(lipgloss.Position)
  • WithBorderStyle(lipgloss.Style), WithTitleStyle(lipgloss.Style), WithMessageStyle(lipgloss.Style)
  • WithButtonStyle(lipgloss.Style), WithFocusedButtonStyle(lipgloss.Style)

All wither methods return a new ModalModel instance (functional style).

Styling

Customize modal appearance by providing styles in ModelArgs:

modal := teamodal.NewYesNoModal("Proceed?", &teamodal.ModelArgs{
	ScreenWidth:  80,
	ScreenHeight: 24,
	Title:        "Confirmation",
	BorderStyle: lipgloss.NewStyle().
		BorderStyle(lipgloss.DoubleBorder()).
		BorderForeground(lipgloss.Color("62")),
	FocusedButtonStyle: lipgloss.NewStyle().
		Bold(true).
		Foreground(lipgloss.Color("230")).
		Background(lipgloss.Color("63")),
})

Default Styles

func DefaultBorderStyle() lipgloss.Style
func DefaultTitleStyle() lipgloss.Style
func DefaultMessageStyle() lipgloss.Style
func DefaultButtonStyle() lipgloss.Style
func DefaultFocusedButtonStyle() lipgloss.Style

Returns the default styles used when no options are provided.

Modal Behavior Pattern

The modal implements clean message consumption without "infecting" the parent:

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	var modal tea.Model

	// Let modal handle first
	modal, cmd = m.confirmDialog.Update(msg)
	if cmd != nil {
		m.confirmDialog = modal.(teamodal.ModalModel)
		return m, cmd  // Modal consumed message - we're done
	}

	// Modal didn't handle - parent processes
	switch msg := msg.(type) {
	case teamodal.AnsweredYesMsg:
		// Handle yes response
		return m, m.handleConfirmation()

	case teamodal.AnsweredNoMsg, teamodal.ClosedMsg:
		// Handle no/cancel response
		return m, nil
	}

	return m, nil
}

Key insight: The modal returns nil cmd when it doesn't handle the message, non-nil when it does. This prevents "modal infection" - parent doesn't need to check modal state for every key.

Design

TeaModal follows the same proven patterns as teafields:

  • Modal Message Consumption: Non-nil cmd signals "I handled this", nil signals "not for me"
  • String Compositing: ANSI-aware overlay using OverlayModal()
  • ClearPath Style: Single return, goto end, no else
  • Functional Options: Methods return new model values

See teafields documentation for more on these patterns.

Examples

See example/ directory for a working demonstration.

License

MIT License - See LICENSE.txt

Documentation

Overview

Package teamodal provides Bubble Tea v2 components for modal dialogs and scrollable list selection.

It includes three primary models:

  • [Model] — a modal overlay with a title, message, and action buttons
  • ChoiceModel — a modal for choosing from a small set of labeled options
  • ListModel — a scrollable, filterable list for picking from many items

Stability

This package is provisional as of v0.3.0. The public API may change in minor releases until promoted to stable.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrMissingSentinel     = errors.New("missing required sentinel error")
	ErrTrailingKey         = errors.New("trailing key without value")
	ErrMisplacedError      = errors.New("error in wrong position")
	ErrInvalidArgumentType = errors.New("invalid argument type")
	ErrOddKeyValueCount    = errors.New("odd number of key-value arguments")
	ErrCrossPackageError   = errors.New("error from different doterr package")
)

Sentinel errors for validation failures

View Source
var (
	ErrModal         = errors.New("modal error")
	ErrInvalidBounds = errors.New("invalid bounds error")
	ErrCancelled     = errors.New("cancelled error")
)

Functions

func AppendErr

func AppendErr(errs []error, err error) []error

func CombineErrs

func CombineErrs(errs []error) error

CombineErrs bundles a slice of errors into a single composite error that unwraps to its members. Order is preserved and nils are skipped. Returns nil for an empty/fully-nil slice, or the sole error when there is exactly one.

func DefaultActiveItemStyle

func DefaultActiveItemStyle() lipgloss.Style

DefaultActiveItemStyle returns default styling for the active/in-use item badge

func DefaultBorderStyle

func DefaultBorderStyle() lipgloss.Style

DefaultBorderStyle returns default modal border styling

func DefaultButtonStyle

func DefaultButtonStyle() lipgloss.Style

DefaultButtonStyle returns default button styling

func DefaultCancelKeyStyle

func DefaultCancelKeyStyle() lipgloss.Style

DefaultCancelKeyStyle returns default styling for "[esc]" in the cancel hint

func DefaultCancelTextStyle

func DefaultCancelTextStyle() lipgloss.Style

DefaultCancelTextStyle returns default styling for "Cancel" in the cancel hint

func DefaultCheckedStyle added in v0.6.0

func DefaultCheckedStyle() lipgloss.Style

DefaultCheckedStyle returns default styling for checked checkbox "[✓]"

func DefaultEditItemStyle

func DefaultEditItemStyle() lipgloss.Style

DefaultEditItemStyle returns default styling for inline editing

func DefaultFocusedButtonStyle

func DefaultFocusedButtonStyle() lipgloss.Style

DefaultFocusedButtonStyle returns default focused button styling

func DefaultListFooterStyle

func DefaultListFooterStyle() lipgloss.Style

DefaultListFooterStyle returns default styling for the footer with key hints

func DefaultListItemStyle

func DefaultListItemStyle() lipgloss.Style

DefaultListItemStyle returns default styling for list items

func DefaultListScrollbarStyle

func DefaultListScrollbarStyle() lipgloss.Style

DefaultListScrollbarStyle returns default styling for the scrollbar

func DefaultListScrollbarThumbStyle

func DefaultListScrollbarThumbStyle() lipgloss.Style

DefaultListScrollbarThumbStyle returns default styling for the scrollbar thumb

func DefaultMessageStyle

func DefaultMessageStyle() lipgloss.Style

DefaultMessageStyle returns default message text styling

func DefaultMultiSelectFooterStyle added in v0.6.0

func DefaultMultiSelectFooterStyle() lipgloss.Style

DefaultMultiSelectFooterStyle returns default styling for the multi-select footer note

func DefaultSelectedItemStyle

func DefaultSelectedItemStyle() lipgloss.Style

DefaultSelectedItemStyle returns default styling for the item at cursor position

func DefaultStatusStyle

func DefaultStatusStyle() lipgloss.Style

DefaultStatusStyle returns default styling for the status/feedback line

func DefaultTitleStyle

func DefaultTitleStyle() lipgloss.Style

DefaultTitleStyle returns default title styling

func DefaultUncheckedStyle added in v0.6.0

func DefaultUncheckedStyle() lipgloss.Style

DefaultUncheckedStyle returns default styling for unchecked checkbox "[ ]"

func EnsureTermGetSize

func EnsureTermGetSize(fd uintptr) (w int, h int, ok bool)

EnsureTermGetSize tries hard to get a real terminal size. It treats (0,0,nil) as "not ready yet" and retries briefly.

func ErrValue

func ErrValue[T any](err error, key string) (T, bool)

ErrValue extracts a single metadata value by key with type safety. Returns the value and true if found and the value is of type T. Returns the zero value of T and false if not found or type mismatch.

Example:

status, ok := ErrValue[int](err, "http_status")
if ok {
    fmt.Printf("Status: %d\n", status)
}

name, ok := ErrValue[string](err, "parameter_name")

func Errors

func Errors(err error) []error

Errors returns the errors stored on a doterr entry. If err is a doterr entry, returns its errors. If err is a joined error (has Unwrap() []error), scans immediate children left-to-right and returns errors from the first doterr entry found. Otherwise returns nil. The returned slice preserves insertion order and is a copy.

Note: These errors may be sentinel errors (e.g., ErrModal), custom error types (e.g., *rfc9457.Error), or any other error type stored in the entry.

func FindErr

func FindErr[T error](err error) (out T, ok bool)

FindErr walks an error tree (including errors.Join trees) and returns the first match for target (via errors.As).

func MsgErr

func MsgErr(msg any) error

MsgErr creates an ad-hoc error message without requiring a sentinel error. This is a convenience for rapid development - use sentinels for production code.

Accepts two forms:

  • MsgErr("message") - creates error with the given message
  • MsgErr(err) - wraps existing error, preserving error chain for errors.Is()

Use this when you don't want to define a sentinel immediately. Future tooling can detect MsgErr usage and suggest/generate appropriate sentinel errors.

For errors with metadata, use NewErr with a sentinel, or wrap with WithErr:

err := MsgErr("config invalid")
err = WithErr(err, "path", configPath)

func NewErr

func NewErr(parts ...any) error

NewErr builds a standalone structured entry (no primary cause inside). Accepted parts:

  • error — sentinel/tag (required: at least one, must be first)
  • KV{Key,Value} — explicit key/value
  • "key", value — implicit pair (value can be any type, including error)
  • error — optional trailing cause (joined last via errors.Join)

Pattern: one or more sentinels (error), then zero or more key-value pairs, then optional trailing cause (error). After the first string key, all remaining args must form valid pairs, except for an optional final error. Returns nil if no meaningful parts are provided after validation. Returns a validation error joined with the partial entry if validation fails.

func OverlayModal

func OverlayModal(background, foreground string, row, col int) string

OverlayModal overlays modal view on background view at specified position. Uses ANSI-aware string operations to correctly handle styled text. This follows the proven pattern from teafields (same as OverlayDropdown).

Parameters:

  • background: The base view (fully rendered string with ANSI codes)
  • foreground: The modal view (fully rendered string with ANSI codes)
  • row: Line number in background where foreground row 0 should appear (0-indexed)
  • col: Display column in background where foreground col 0 should appear (0-indexed)

Returns:

  • Composited view with foreground overlaid on background

func WithErr

func WithErr(parts ...any) error

WithErr is a flexible enrichment helper. Typical uses:

// Enrich an existing composite error (err may be an errors.Join tree):
err = WithErr(err, "Foo", 10)

// Build an entry and join a trailing cause in one shot:
err = WithErr("endpoint", ep, ErrTemplate, cause) // 'cause' is last

Behavior:

  1. If the FIRST arg is an error, it is treated as the base error to enrich: • If it is a doterr entry, merge KVs/sentinels into that entry. • If it is a multi-unwrap (errors.Join tree), find the RIGHTMOST doterr entry, merge into it, and rebuild preserving order. • If no doterr entry is found, a new entry will be joined in (see step 3).

  2. After consuming the base (if present), if the LAST remaining arg is an error, it is treated as the CAUSE and joined LAST.

  3. The remaining middle args (if any) are collected into an entry. If we enriched an existing doterr entry in step 1, that merged entry is used; otherwise, a fresh entry is created. If there is a trailing CAUSE from step 2, the result is errors.Join(entry, cause). If there is no cause, the entry is returned.

Note: For inter-function composition, prefer New() with trailing cause:

return NewErr(ErrModal, "key", val, cause) // cause last

Types

type AnsweredNoMsg

type AnsweredNoMsg struct{}

AnsweredNoMsg is sent when user selects No button and presses Enter

type AnsweredYesMsg

type AnsweredYesMsg struct{}

AnsweredYesMsg is sent when user confirms with Enter on Yes button

type ChoiceCancelledMsg

type ChoiceCancelledMsg struct{}

ChoiceCancelledMsg is sent when the modal is cancelled (Esc key)

type ChoiceKeyMap

type ChoiceKeyMap struct {
	NextButton key.Binding // Tab, Right arrow
	PrevButton key.Binding // Shift+Tab, Left arrow
	Confirm    key.Binding // Enter
	Cancel     key.Binding // Esc
}

ChoiceKeyMap defines key bindings for ChoiceModel

func DefaultChoiceKeyMap

func DefaultChoiceKeyMap() ChoiceKeyMap

DefaultChoiceKeyMap returns default key bindings for ChoiceModel

type ChoiceModel

type ChoiceModel struct {
	Keys ChoiceKeyMap
	// contains filtered or unexported fields
}

ChoiceModel is a Bubble Tea model for multi-option confirmation dialogs

func NewChoiceModel

func NewChoiceModel(args *ChoiceModelArgs) (m ChoiceModel)

NewChoiceModel creates a new multi-option choice modal

func (ChoiceModel) Close

func (m ChoiceModel) Close() (ChoiceModel, tea.Cmd)

Close closes the modal and returns updated model

func (ChoiceModel) FocusButton

func (m ChoiceModel) FocusButton() int

FocusButton returns the index of the currently focused button

func (ChoiceModel) Init

func (m ChoiceModel) Init() tea.Cmd

Init implements tea.Model - returns nil (no initial command)

func (ChoiceModel) IsOpen

func (m ChoiceModel) IsOpen() bool

IsOpen returns whether the modal is currently open

func (ChoiceModel) Open

func (m ChoiceModel) Open() (ChoiceModel, tea.Cmd)

Open opens the modal and returns updated model

func (ChoiceModel) OverlayModal

func (m ChoiceModel) OverlayModal(background string) (view string)

OverlayModal renders the modal centered over the background view. Handles positioning automatically based on screen dimensions.

func (ChoiceModel) SetSize

func (m ChoiceModel) SetSize(width, height int) ChoiceModel

SetSize sets screen dimensions

func (ChoiceModel) Update

func (m ChoiceModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model (FOLLOWS ClearPath)

func (ChoiceModel) View

func (m ChoiceModel) View() tea.View

View implements tea.Model (FOLLOWS ClearPath)

func (ChoiceModel) WithTheme added in v0.4.0

func (m ChoiceModel) WithTheme(theme teautils.Theme) ChoiceModel

WithTheme returns a copy of the ChoiceModel with styles derived from the given theme. Individual With*Style() calls take precedence if called after.

type ChoiceModelArgs

type ChoiceModelArgs struct {
	ScreenWidth    int
	ScreenHeight   int
	Title          string         // Optional title above message
	Message        string         // Main message text
	Options        []ChoiceOption // 2-5 options
	DefaultIndex   int            // Which button is focused initially (0-based)
	Orientation    Orientation    // Horizontal (default) or Vertical
	ShowBrackets   *bool          // Show "[ ]" around labels; nil = true for Horizontal, false for Vertical
	AllowCancel    *bool          // Allow Esc to cancel; nil defaults to true
	ShowCancelHint *bool          // Show "[esc] Cancel" hint below options; nil defaults to AllowCancel

	// Style overrides (optional - defaults will be used if not provided)
	BorderStyle        lipgloss.Style
	TitleStyle         lipgloss.Style
	MessageStyle       lipgloss.Style
	ButtonStyle        lipgloss.Style
	FocusedButtonStyle lipgloss.Style
	CancelKeyStyle     lipgloss.Style
	CancelTextStyle    lipgloss.Style
}

ChoiceModelArgs contains initialization arguments for ChoiceModel

type ChoiceOption

type ChoiceOption struct {
	Label  string // Display text, use caps for hotkey affordance: "Reorganize & Exit"
	Hotkey rune   // Optional: 'r' (case-insensitive, triggers without Tab+Enter)
	ID     string // Returned in ChoiceSelectedMsg to identify selection
}

ChoiceOption represents a single choice in the modal. Use capitalization in Label to indicate the hotkey visually (e.g. "Reorganize & Exit").

type ChoiceSelectedMsg

type ChoiceSelectedMsg struct {
	OptionID string // The ID of the selected option
	Index    int    // The 0-based index of the selected option
}

ChoiceSelectedMsg is sent when a choice is selected (Enter on focused button or hotkey)

type ClosedMsg

type ClosedMsg struct{}

ClosedMsg is sent when user closes an alert (OK button) or cancels with Esc

type ConfirmModel added in v0.4.0

type ConfirmModel struct {
	Keys ModalKeyMap // Keyboard bindings
	// contains filtered or unexported fields
}

ConfirmModel is a Bubble Tea model for modal dialogs

func NewOKModal

func NewOKModal(message string, args *ConfirmModelArgs) (m ConfirmModel)

NewOKModal creates a new OK-only modal (alert dialog)

func NewYesNoModal

func NewYesNoModal(message string, args *ConfirmModelArgs) (m ConfirmModel)

NewYesNoModal creates a new Yes/No confirmation modal

func (ConfirmModel) BorderStyle added in v0.4.0

func (m ConfirmModel) BorderStyle() lipgloss.Style

BorderStyle returns the border style

func (ConfirmModel) ButtonAlign added in v0.4.0

func (m ConfirmModel) ButtonAlign() lipgloss.Position

ButtonAlign returns the button alignment (defaults to Center if not set)

func (ConfirmModel) ButtonStyle added in v0.4.0

func (m ConfirmModel) ButtonStyle() lipgloss.Style

ButtonStyle returns the unfocused button style

func (ConfirmModel) Close added in v0.4.0

func (m ConfirmModel) Close() (ConfirmModel, tea.Cmd)

Close closes the modal and returns updated model

func (ConfirmModel) FocusButton added in v0.4.0

func (m ConfirmModel) FocusButton() int

FocusButton returns the index of the currently focused button

func (ConfirmModel) FocusedButtonStyle added in v0.4.0

func (m ConfirmModel) FocusedButtonStyle() lipgloss.Style

FocusedButtonStyle returns the focused button style

func (ConfirmModel) Init added in v0.4.0

func (m ConfirmModel) Init() tea.Cmd

Init implements tea.Model - returns nil (no initial command)

func (ConfirmModel) IsOpen added in v0.4.0

func (m ConfirmModel) IsOpen() bool

IsOpen returns whether the modal is currently open

func (ConfirmModel) Message added in v0.4.0

func (m ConfirmModel) Message() string

Message returns the modal message

func (ConfirmModel) MessageAlign added in v0.4.0

func (m ConfirmModel) MessageAlign() lipgloss.Position

MessageAlign returns the message alignment (defaults to Center if not set)

func (ConfirmModel) MessageStyle added in v0.4.0

func (m ConfirmModel) MessageStyle() lipgloss.Style

MessageStyle returns the message style

func (ConfirmModel) NoLabel added in v0.4.0

func (m ConfirmModel) NoLabel() string

NoLabel returns the No button label

func (ConfirmModel) OKLabel added in v0.4.0

func (m ConfirmModel) OKLabel() string

OKLabel returns the OK button label

func (ConfirmModel) Open added in v0.4.0

func (m ConfirmModel) Open() (ConfirmModel, tea.Cmd)

Open opens the modal and returns updated model

func (ConfirmModel) OverlayModal added in v0.4.0

func (m ConfirmModel) OverlayModal(background string) (view string)

OverlayModal renders the modal centered over the background view. Handles positioning automatically based on screen dimensions.

func (ConfirmModel) ScreenHeight added in v0.4.0

func (m ConfirmModel) ScreenHeight() int

ScreenHeight returns the screen height

func (ConfirmModel) ScreenWidth added in v0.4.0

func (m ConfirmModel) ScreenWidth() int

ScreenWidth returns the screen width

func (ConfirmModel) SetSize added in v0.4.0

func (m ConfirmModel) SetSize(width, height int) ConfirmModel

SetSize sets screen dimensions

func (ConfirmModel) Title added in v0.4.0

func (m ConfirmModel) Title() string

Title returns the modal title

func (ConfirmModel) TitleAlign added in v0.4.0

func (m ConfirmModel) TitleAlign() lipgloss.Position

TitleAlign returns the title alignment (defaults to Center if not set)

func (ConfirmModel) TitleStyle added in v0.4.0

func (m ConfirmModel) TitleStyle() lipgloss.Style

TitleStyle returns the title style

func (ConfirmModel) Type added in v0.4.0

func (m ConfirmModel) Type() ModalType

Type returns the modal type

func (ConfirmModel) Update added in v0.4.0

func (m ConfirmModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update implements tea.Model (FOLLOWS ClearPath)

func (ConfirmModel) View added in v0.4.0

func (m ConfirmModel) View() tea.View

View implements tea.Model (FOLLOWS ClearPath)

func (ConfirmModel) WithBorderStyle added in v0.4.0

func (m ConfirmModel) WithBorderStyle(style lipgloss.Style) ConfirmModel

WithBorderStyle returns a copy with the specified border style

func (ConfirmModel) WithButtonAlign added in v0.4.0

func (m ConfirmModel) WithButtonAlign(align lipgloss.Position) ConfirmModel

WithButtonAlign sets the horizontal alignment for the buttons

func (ConfirmModel) WithButtonStyle added in v0.4.0

func (m ConfirmModel) WithButtonStyle(style lipgloss.Style) ConfirmModel

WithButtonStyle returns a copy with the specified button style

func (ConfirmModel) WithFocusedButtonStyle added in v0.4.0

func (m ConfirmModel) WithFocusedButtonStyle(style lipgloss.Style) ConfirmModel

WithFocusedButtonStyle returns a copy with the specified focused button style

func (ConfirmModel) WithMessage added in v0.4.0

func (m ConfirmModel) WithMessage(message string) ConfirmModel

WithMessage returns a copy with the specified message

func (ConfirmModel) WithMessageAlign added in v0.4.0

func (m ConfirmModel) WithMessageAlign(align lipgloss.Position) ConfirmModel

WithMessageAlign sets the horizontal alignment for the message

func (ConfirmModel) WithMessageStyle added in v0.4.0

func (m ConfirmModel) WithMessageStyle(style lipgloss.Style) ConfirmModel

WithMessageStyle returns a copy with the specified message style

func (ConfirmModel) WithNoLabel added in v0.4.0

func (m ConfirmModel) WithNoLabel(label string) ConfirmModel

WithNoLabel returns a copy with the specified No button label

func (ConfirmModel) WithOKLabel added in v0.4.0

func (m ConfirmModel) WithOKLabel(label string) ConfirmModel

WithOKLabel returns a copy with the specified OK button label

func (ConfirmModel) WithTextAlign added in v0.4.0

func (m ConfirmModel) WithTextAlign(align lipgloss.Position) ConfirmModel

WithTextAlign sets the horizontal alignment for text content (title and message, not buttons)

func (ConfirmModel) WithTheme added in v0.4.0

func (m ConfirmModel) WithTheme(theme teautils.Theme) ConfirmModel

WithTheme returns a copy of the ConfirmModel with styles derived from the given theme. Individual With*Style() calls take precedence if called after.

func (ConfirmModel) WithTitle added in v0.4.0

func (m ConfirmModel) WithTitle(title string) ConfirmModel

WithTitle returns a copy with the specified title

func (ConfirmModel) WithTitleAlign added in v0.4.0

func (m ConfirmModel) WithTitleAlign(align lipgloss.Position) ConfirmModel

WithTitleAlign sets the horizontal alignment for the title

func (ConfirmModel) WithTitleStyle added in v0.4.0

func (m ConfirmModel) WithTitleStyle(style lipgloss.Style) ConfirmModel

WithTitleStyle returns a copy with the specified title style

func (ConfirmModel) WithYesLabel added in v0.4.0

func (m ConfirmModel) WithYesLabel(label string) ConfirmModel

WithYesLabel returns a copy with the specified Yes button label

func (ConfirmModel) YesLabel added in v0.4.0

func (m ConfirmModel) YesLabel() string

YesLabel returns the Yes button label

type ConfirmModelArgs added in v0.4.0

type ConfirmModelArgs struct {
	ScreenWidth  int
	ScreenHeight int
	Title        string
	DefaultYes   bool // For YesNo modals: default focus to Yes (true) or No (false)
	YesLabel     string
	NoLabel      string
	OKLabel      string

	// Alignment (optional - defaults to lipgloss.Center)
	// Use TextAlign to set both title and message, or individual fields for fine control
	TextAlign    lipgloss.Position // Horizontal alignment for title and message
	TitleAlign   lipgloss.Position // Horizontal alignment for title (overrides TextAlign)
	MessageAlign lipgloss.Position // Horizontal alignment for message (overrides TextAlign)
	ButtonAlign  lipgloss.Position // Horizontal alignment for buttons (defaults to Center)

	// Styling (optional - defaults will be used if not provided)
	BorderStyle        lipgloss.Style
	TitleStyle         lipgloss.Style
	MessageStyle       lipgloss.Style
	ButtonStyle        lipgloss.Style
	FocusedButtonStyle lipgloss.Style
}

ConfirmModelArgs contains initialization arguments for ConfirmModel

type DeleteItemRequestedMsg

type DeleteItemRequestedMsg[T ListItem] struct {
	Item T
}

DeleteItemRequestedMsg is sent when user presses 'd' to delete an item

type EditCompletedMsg

type EditCompletedMsg[T ListItem] struct {
	Item     T
	NewLabel string
}

EditCompletedMsg is sent when user completes inline editing with Enter. Contains the item being edited and the new label text.

type ErrKV

type ErrKV interface {
	Key() string
	Value() any
}

ErrKV represents a key/value metadata pair. Keys are preserved in insertion order, and values may be of any type.

func AnyKV

func AnyKV(key string, value any) ErrKV

AnyKV creates a KV with any value type. Use this for custom types or when the specific type constructor doesn't exist.

func AppendKV

func AppendKV(kvs []ErrKV, parts ...any) []ErrKV

AppendKV appends key-value pairs to a slice of KV values. It accepts variadic arguments in three forms:

  1. Individual KV values: AppendKV(kvs, StringKV("foo", "bar"))
  2. String key-value pairs: AppendKV(kvs, "foo", "bar")
  3. Lazy ErrKV functions: AppendKV(kvs, func()ErrKV{ return StringKV("expensive", compute()) })

This function is useful for accumulating metadata throughout a function before creating an error:

var kvs []ErrKV
kvs = AppendKV(kvs, "user_id", userID)
if complexCondition {
    kvs = AppendKV(kvs, "reason", "complex")
}
return NewErr(ErrFailed, kvs, cause)

String key-value pairs must come in even counts (odd counts panic in debug mode). Lazy functions (func()ErrKV) are wrapped and not evaluated until error creation.

func BoolKV

func BoolKV(key string, value bool) ErrKV

BoolKV creates a KV with a bool value.

func ErrMeta

func ErrMeta(err error) []ErrKV

ErrMeta returns the key/value pairs stored on all doterr entries in the error tree. If err is a doterr entry, returns its metadata. If err is a joined error (has Unwrap() []error), scans all children recursively and collects metadata from all doterr entries found, preserving order. Otherwise returns nil. The returned slice preserves insertion order across all entries.

func ErrorKV

func ErrorKV(key string, value error) ErrKV

ErrorKV creates a KV with an error value. Use this when you want to include an error as metadata (not as a cause).

func Float64KV

func Float64KV(key string, value float64) ErrKV

Float64KV creates a KV with a float64 value.

func Int64KV

func Int64KV(key string, value int64) ErrKV

Int64KV creates a KV with an int64 value.

func IntKV

func IntKV(key string, value int) ErrKV

IntKV creates a KV with an int value.

func StringKV

func StringKV(key, value string) ErrKV

StringKV creates a KV with a string value.

type IsDotErrEntry

type IsDotErrEntry = interface {
	IsDotErrEntry()
	HasKV(key string, value any) bool
	MatchKV(key string, value any) bool
}

type ItemSelectedMsg

type ItemSelectedMsg[T ListItem] struct {
	Item T
}

ItemSelectedMsg is sent when user presses Space to preview-select an item. The dialog remains open for further browsing.

type ListAcceptedMsg

type ListAcceptedMsg[T ListItem] struct {
	Item T
}

ListAcceptedMsg is sent when user presses Enter to accept the current selection and close the dialog. Contains the accepted item (cursor item if no active item).

type ListCancelledMsg

type ListCancelledMsg struct{}

ListCancelledMsg is sent when user presses Esc to close without selection

type ListItem

type ListItem interface {
	// ID returns a unique identifier for the item
	ID() string
	// Label returns the display text for the item
	Label() string
	// IsActive returns true if this item is the currently active/selected item
	// (distinct from cursor position - this marks the "in use" item)
	IsActive() bool
}

ListItem is the constraint interface for items displayed in ListModel

type ListKeyMap

type ListKeyMap struct {
	Up      key.Binding // Up arrow, k - move cursor up
	Down    key.Binding // Down arrow, j - move cursor down
	Preview key.Binding // Space - preview select (mark active, keep browsing)
	Accept  key.Binding // Enter - commit selection and close
	New     key.Binding // a - request new item creation
	Edit    key.Binding // e, F2 - request edit of item at cursor
	Delete  key.Binding // d - request deletion of item at cursor
	Help    key.Binding // ? - toggle help visor
	Cancel  key.Binding // Esc - close without selection
}

ListKeyMap defines the key bindings for list modal dialogs

func DefaultListKeyMap

func DefaultListKeyMap() ListKeyMap

DefaultListKeyMap returns the default key bindings for list modals

type ListModel

type ListModel[T ListItem] struct {
	// Public
	Keys   ListKeyMap
	Logger *slog.Logger
	// contains filtered or unexported fields
}

ListModel is a Bubble Tea model for list modal dialogs with CRUD operations

func NewListModel

func NewListModel[T ListItem](items []T, args *ListModelArgs) (m ListModel[T])

NewListModel creates a new ListModel with the given items

func (ListModel[T]) ActiveItem

func (m ListModel[T]) ActiveItem() (item T)

ActiveItem returns the item where IsActive() returns true

func (ListModel[T]) ActiveItemStyle

func (m ListModel[T]) ActiveItemStyle() lipgloss.Style

ActiveItemStyle returns the active item style

func (ListModel[T]) BorderStyle

func (m ListModel[T]) BorderStyle() lipgloss.Style

BorderStyle returns the border style

func (ListModel[T]) ClearStatus

func (m ListModel[T]) ClearStatus() ListModel[T]

ClearStatus clears the status message

func (ListModel[T]) Close

func (m ListModel[T]) Close() ListModel[T]

Close closes the modal and returns updated model

func (ListModel[T]) Cursor

func (m ListModel[T]) Cursor() int

Cursor returns the current cursor position

func (ListModel[T]) FooterStyle

func (m ListModel[T]) FooterStyle() lipgloss.Style

FooterStyle returns the footer style

func (ListModel[T]) Init

func (m ListModel[T]) Init() tea.Cmd

Init implements tea.Model - returns nil (no initial command)

func (ListModel[T]) IsOpen

func (m ListModel[T]) IsOpen() bool

IsOpen returns whether the modal is currently open

func (ListModel[T]) ItemStyle

func (m ListModel[T]) ItemStyle() lipgloss.Style

ItemStyle returns the item style

func (ListModel[T]) Items

func (m ListModel[T]) Items() []T

Items returns the current list items

func (ListModel[T]) LabelWidth

func (m ListModel[T]) LabelWidth() int

LabelWidth returns the current label width

func (ListModel[T]) Offset

func (m ListModel[T]) Offset() int

Offset returns the current scroll offset

func (ListModel[T]) Open

func (m ListModel[T]) Open() ListModel[T]

Open opens the modal and returns updated model

func (ListModel[T]) OverlayModal

func (m ListModel[T]) OverlayModal(background string) (view string)

OverlayModal renders the modal centered over the background view

func (ListModel[T]) SelectedItem

func (m ListModel[T]) SelectedItem() (item T)

SelectedItem returns the item at the cursor position

func (ListModel[T]) SelectedItemStyle

func (m ListModel[T]) SelectedItemStyle() lipgloss.Style

SelectedItemStyle returns the selected item style

func (ListModel[T]) SetCursor

func (m ListModel[T]) SetCursor(index int) ListModel[T]

SetCursor sets the cursor to the specified index

func (ListModel[T]) SetCursorToLast

func (m ListModel[T]) SetCursorToLast() ListModel[T]

SetCursorToLast sets the cursor to the last item

func (ListModel[T]) SetItems

func (m ListModel[T]) SetItems(items []T) ListModel[T]

SetItems updates the list items

func (ListModel[T]) SetSize

func (m ListModel[T]) SetSize(width, height int) ListModel[T]

SetSize sets screen dimensions

func (ListModel[T]) SetStatus

func (m ListModel[T]) SetStatus(msg string) ListModel[T]

SetStatus sets a status/feedback message to display above the footer

func (ListModel[T]) Title

func (m ListModel[T]) Title() string

Title returns the modal title

func (ListModel[T]) TitleStyle

func (m ListModel[T]) TitleStyle() lipgloss.Style

TitleStyle returns the title style

func (ListModel[T]) Update

func (m ListModel[T]) Update(msg tea.Msg) (ListModel[T], tea.Cmd)

Update implements tea.Model (FOLLOWS ClearPath)

func (ListModel[T]) View

func (m ListModel[T]) View() tea.View

View implements tea.Model (FOLLOWS ClearPath)

func (ListModel[T]) WithActiveItemStyle

func (m ListModel[T]) WithActiveItemStyle(style lipgloss.Style) ListModel[T]

WithActiveItemStyle returns a copy with the specified active item style

func (ListModel[T]) WithBorderStyle

func (m ListModel[T]) WithBorderStyle(style lipgloss.Style) ListModel[T]

WithBorderStyle returns a copy with the specified border style

func (ListModel[T]) WithFooterStyle

func (m ListModel[T]) WithFooterStyle(style lipgloss.Style) ListModel[T]

WithFooterStyle returns a copy with the specified footer style

func (ListModel[T]) WithItemStyle

func (m ListModel[T]) WithItemStyle(style lipgloss.Style) ListModel[T]

WithItemStyle returns a copy with the specified item style

func (ListModel[T]) WithLabelWidth

func (m ListModel[T]) WithLabelWidth(width int) ListModel[T]

WithLabelWidth returns a copy with the specified label width for consistent sizing

func (ListModel[T]) WithLogger

func (m ListModel[T]) WithLogger(logger *slog.Logger) ListModel[T]

WithLogger returns a copy with the specified logger for debugging

func (ListModel[T]) WithMaxVisible

func (m ListModel[T]) WithMaxVisible(max int) ListModel[T]

WithMaxVisible returns a copy with the specified max visible items

func (ListModel[T]) WithSelectedItemStyle

func (m ListModel[T]) WithSelectedItemStyle(style lipgloss.Style) ListModel[T]

WithSelectedItemStyle returns a copy with the specified selected item style

func (ListModel[T]) WithTheme added in v0.4.0

func (m ListModel[T]) WithTheme(theme teautils.Theme) ListModel[T]

WithTheme returns a copy of the ListModel with styles derived from the given theme. Individual With*Style() calls take precedence if called after.

func (ListModel[T]) WithTitle

func (m ListModel[T]) WithTitle(title string) ListModel[T]

WithTitle returns a copy with the specified title

func (ListModel[T]) WithTitleStyle

func (m ListModel[T]) WithTitleStyle(style lipgloss.Style) ListModel[T]

WithTitleStyle returns a copy with the specified title style

type ListModelArgs

type ListModelArgs struct {
	ScreenWidth  int
	ScreenHeight int
	Title        string
	MaxVisible   int // Default: 8
	LabelWidth   int // Fixed width for labels/edit field. 0 = use max item label width

	// Optional style overrides
	BorderStyle       lipgloss.Style
	TitleStyle        lipgloss.Style
	ItemStyle         lipgloss.Style
	SelectedItemStyle lipgloss.Style
	ActiveItemStyle   lipgloss.Style
	FooterStyle       lipgloss.Style
}

ListModelArgs provides optional configuration for ListModel

type ModalKeyMap

type ModalKeyMap struct {
	Confirm     key.Binding // enter - confirm selection
	Cancel      key.Binding // esc - cancel/close
	NextButton  key.Binding // tab - move to next button (YesNo only)
	PrevButton  key.Binding // shift+tab - move to previous button (YesNo only)
	SelectLeft  key.Binding // left - select left button (Yes) (YesNo only)
	SelectRight key.Binding // right - select right button (No) (YesNo only)
}

ModalKeyMap defines the key bindings for modal dialogs

func DefaultModalKeyMap

func DefaultModalKeyMap() ModalKeyMap

DefaultModalKeyMap returns the default key bindings for modals

type ModalType

type ModalType int

ModalType represents the type of modal dialog

const (
	// ModalTypeOK shows a single OK button (alert dialog)
	ModalTypeOK ModalType = iota
	// ModalTypeYesNo shows Yes and No buttons (confirmation dialog)
	ModalTypeYesNo
)

type MultiSelectButton added in v0.6.0

type MultiSelectButton struct {
	Label  string // Display text for the button
	Hotkey rune   // Optional single-char hotkey (case-insensitive)
	ID     string // Returned in message to identify which button
}

MultiSelectButton represents a button in the multi-select modal

type MultiSelectButtonPressedMsg added in v0.6.0

type MultiSelectButtonPressedMsg[T MultiSelectItem] struct {
	ButtonID string // Which button was pressed
	Selected []T    // Items that were checked at the time
}

MultiSelectButtonPressedMsg is sent when user presses a button (any button). All buttons emit this message; the consumer maps ButtonID to behavior. Esc is the only path to MultiSelectCancelledMsg.

type MultiSelectCancelledMsg added in v0.6.0

type MultiSelectCancelledMsg struct{}

MultiSelectCancelledMsg is sent when user presses Esc

type MultiSelectItem added in v0.6.0

type MultiSelectItem interface {
	// ID returns a unique identifier for the item
	ID() string
	// Label returns the display text for the item
	Label() string
}

MultiSelectItem is the constraint interface for items displayed in MultiSelectModel

type MultiSelectKeyMap added in v0.6.0

type MultiSelectKeyMap struct {
	Up         key.Binding // up, k — cursor up (list focus)
	Down       key.Binding // down, j — cursor down (list focus)
	Toggle     key.Binding // space — toggle checkbox (list focus)
	Confirm    key.Binding // enter — toggle (list focus) or activate (button focus)
	Cancel     key.Binding // esc — cancel
	NextFocus  key.Binding // tab — cycle: list → button0 → ... → list
	PrevFocus  key.Binding // shift+tab — reverse cycle
	NextButton key.Binding // right, l — between buttons only
	PrevButton key.Binding // left, h — between buttons only
}

MultiSelectKeyMap defines key bindings for MultiSelectModel

func DefaultMultiSelectKeyMap added in v0.6.0

func DefaultMultiSelectKeyMap() MultiSelectKeyMap

DefaultMultiSelectKeyMap returns default key bindings for MultiSelectModel

type MultiSelectModel added in v0.6.0

type MultiSelectModel[T MultiSelectItem] struct {
	// Public
	Keys   MultiSelectKeyMap
	Logger *slog.Logger
	// contains filtered or unexported fields
}

MultiSelectModel is a Bubble Tea model for multi-select checkbox list modals

func NewMultiSelectModel added in v0.6.0

func NewMultiSelectModel[T MultiSelectItem](items []T, args *MultiSelectModelArgs) (m MultiSelectModel[T])

NewMultiSelectModel creates a new MultiSelectModel with the given items

func (MultiSelectModel[T]) BorderStyle added in v0.6.0

func (m MultiSelectModel[T]) BorderStyle() lipgloss.Style

BorderStyle returns the border style

func (MultiSelectModel[T]) ButtonStyle added in v0.6.0

func (m MultiSelectModel[T]) ButtonStyle() lipgloss.Style

ButtonStyle returns the button style

func (MultiSelectModel[T]) CheckedStyle added in v0.6.0

func (m MultiSelectModel[T]) CheckedStyle() lipgloss.Style

CheckedStyle returns the checked checkbox style

func (MultiSelectModel[T]) Close added in v0.6.0

func (m MultiSelectModel[T]) Close() (MultiSelectModel[T], tea.Cmd)

Close closes the modal

func (MultiSelectModel[T]) Cursor added in v0.6.0

func (m MultiSelectModel[T]) Cursor() int

Cursor returns the current cursor position

func (MultiSelectModel[T]) FocusedButtonStyle added in v0.6.0

func (m MultiSelectModel[T]) FocusedButtonStyle() lipgloss.Style

FocusedButtonStyle returns the focused button style

func (MultiSelectModel[T]) FooterStyle added in v0.6.0

func (m MultiSelectModel[T]) FooterStyle() lipgloss.Style

FooterStyle returns the footer style

func (MultiSelectModel[T]) Init added in v0.6.0

func (m MultiSelectModel[T]) Init() tea.Cmd

Init implements tea.Model - returns nil (no initial command)

func (MultiSelectModel[T]) IsOpen added in v0.6.0

func (m MultiSelectModel[T]) IsOpen() bool

IsOpen returns whether the modal is currently open

func (MultiSelectModel[T]) ItemStyle added in v0.6.0

func (m MultiSelectModel[T]) ItemStyle() lipgloss.Style

ItemStyle returns the item style

func (MultiSelectModel[T]) MessageStyle added in v0.6.0

func (m MultiSelectModel[T]) MessageStyle() lipgloss.Style

MessageStyle returns the message style

func (MultiSelectModel[T]) Open added in v0.6.0

func (m MultiSelectModel[T]) Open() (MultiSelectModel[T], tea.Cmd)

Open opens the modal, initializes state, and calculates position

func (MultiSelectModel[T]) OverlayModal added in v0.6.0

func (m MultiSelectModel[T]) OverlayModal(background string) (view string)

OverlayModal renders the modal centered over the background view

func (MultiSelectModel[T]) Selected added in v0.6.0

func (m MultiSelectModel[T]) Selected() []T

Selected returns the currently checked items

func (MultiSelectModel[T]) SelectedItemStyle added in v0.6.0

func (m MultiSelectModel[T]) SelectedItemStyle() lipgloss.Style

SelectedItemStyle returns the selected item style

func (MultiSelectModel[T]) SetItems added in v0.6.0

func (m MultiSelectModel[T]) SetItems(items []T) MultiSelectModel[T]

SetItems updates the list items and resets selection state

func (MultiSelectModel[T]) SetSize added in v0.6.0

func (m MultiSelectModel[T]) SetSize(width, height int) MultiSelectModel[T]

SetSize sets screen dimensions

func (MultiSelectModel[T]) TitleStyle added in v0.6.0

func (m MultiSelectModel[T]) TitleStyle() lipgloss.Style

TitleStyle returns the title style

func (MultiSelectModel[T]) UncheckedStyle added in v0.6.0

func (m MultiSelectModel[T]) UncheckedStyle() lipgloss.Style

UncheckedStyle returns the unchecked checkbox style

func (MultiSelectModel[T]) Update added in v0.6.0

func (m MultiSelectModel[T]) Update(msg tea.Msg) (MultiSelectModel[T], tea.Cmd)

Update implements tea.Model (FOLLOWS ClearPath)

func (MultiSelectModel[T]) View added in v0.6.0

func (m MultiSelectModel[T]) View() tea.View

View implements tea.Model (FOLLOWS ClearPath)

func (MultiSelectModel[T]) WithBorderStyle added in v0.6.0

func (m MultiSelectModel[T]) WithBorderStyle(style lipgloss.Style) MultiSelectModel[T]

WithBorderStyle returns a copy with the specified border style

func (MultiSelectModel[T]) WithButtonStyle added in v0.6.0

func (m MultiSelectModel[T]) WithButtonStyle(style lipgloss.Style) MultiSelectModel[T]

WithButtonStyle returns a copy with the specified button style

func (MultiSelectModel[T]) WithCheckedStyle added in v0.6.0

func (m MultiSelectModel[T]) WithCheckedStyle(style lipgloss.Style) MultiSelectModel[T]

WithCheckedStyle returns a copy with the specified checked checkbox style

func (MultiSelectModel[T]) WithFocusedButtonStyle added in v0.6.0

func (m MultiSelectModel[T]) WithFocusedButtonStyle(style lipgloss.Style) MultiSelectModel[T]

WithFocusedButtonStyle returns a copy with the specified focused button style

func (MultiSelectModel[T]) WithFooter added in v0.6.0

func (m MultiSelectModel[T]) WithFooter(footer string) MultiSelectModel[T]

WithFooter returns a copy with the specified footer

func (MultiSelectModel[T]) WithFooterStyle added in v0.6.0

func (m MultiSelectModel[T]) WithFooterStyle(style lipgloss.Style) MultiSelectModel[T]

WithFooterStyle returns a copy with the specified footer style

func (MultiSelectModel[T]) WithItemStyle added in v0.6.0

func (m MultiSelectModel[T]) WithItemStyle(style lipgloss.Style) MultiSelectModel[T]

WithItemStyle returns a copy with the specified item style

func (MultiSelectModel[T]) WithLogger added in v0.6.0

func (m MultiSelectModel[T]) WithLogger(logger *slog.Logger) MultiSelectModel[T]

WithLogger returns a copy with the specified logger for debugging

func (MultiSelectModel[T]) WithMaxVisible added in v0.6.0

func (m MultiSelectModel[T]) WithMaxVisible(max int) MultiSelectModel[T]

WithMaxVisible returns a copy with the specified max visible items

func (MultiSelectModel[T]) WithMessage added in v0.6.0

func (m MultiSelectModel[T]) WithMessage(message string) MultiSelectModel[T]

WithMessage returns a copy with the specified message

func (MultiSelectModel[T]) WithMessageStyle added in v0.6.0

func (m MultiSelectModel[T]) WithMessageStyle(style lipgloss.Style) MultiSelectModel[T]

WithMessageStyle returns a copy with the specified message style

func (MultiSelectModel[T]) WithSelectedItemStyle added in v0.6.0

func (m MultiSelectModel[T]) WithSelectedItemStyle(style lipgloss.Style) MultiSelectModel[T]

WithSelectedItemStyle returns a copy with the specified selected item style

func (MultiSelectModel[T]) WithTheme added in v0.6.0

func (m MultiSelectModel[T]) WithTheme(theme teautils.Theme) MultiSelectModel[T]

WithTheme returns a copy of the MultiSelectModel with styles derived from the given theme. Individual With*Style() calls take precedence if called after.

func (MultiSelectModel[T]) WithTitle added in v0.6.0

func (m MultiSelectModel[T]) WithTitle(title string) MultiSelectModel[T]

WithTitle returns a copy with the specified title

func (MultiSelectModel[T]) WithTitleStyle added in v0.6.0

func (m MultiSelectModel[T]) WithTitleStyle(style lipgloss.Style) MultiSelectModel[T]

WithTitleStyle returns a copy with the specified title style

func (MultiSelectModel[T]) WithUncheckedStyle added in v0.6.0

func (m MultiSelectModel[T]) WithUncheckedStyle(style lipgloss.Style) MultiSelectModel[T]

WithUncheckedStyle returns a copy with the specified unchecked checkbox style

type MultiSelectModelArgs added in v0.6.0

type MultiSelectModelArgs struct {
	ScreenWidth              int
	ScreenHeight             int
	Title                    string              // Title text at top of modal
	Message                  string              // Optional description above the list
	Footer                   string              // Optional note below the list (e.g. warning text)
	Buttons                  []MultiSelectButton // Action buttons (2-4)
	AllChecked               bool                // Default: true — all items start checked
	MaxVisible               int                 // Default: 8 — scrollable if more items
	NoCancel                 bool                // Don't auto-append Cancel button (default: false = auto-cancel)
	CancelEmitsButtonPressed bool                // Cancel button emits ButtonPressedMsg instead of CancelledMsg

	// Style overrides (optional — defaults applied)
	BorderStyle        lipgloss.Style
	TitleStyle         lipgloss.Style
	MessageStyle       lipgloss.Style
	FooterStyle        lipgloss.Style
	ItemStyle          lipgloss.Style
	SelectedItemStyle  lipgloss.Style
	CheckedStyle       lipgloss.Style
	UncheckedStyle     lipgloss.Style
	ButtonStyle        lipgloss.Style
	FocusedButtonStyle lipgloss.Style
}

MultiSelectModelArgs provides optional configuration for MultiSelectModel

type NewItemRequestedMsg

type NewItemRequestedMsg struct{}

NewItemRequestedMsg is sent when user presses 'a' to create new item

type Orientation

type Orientation int

Orientation controls how choice buttons are laid out

const (
	Horizontal Orientation = iota // Default: buttons in a row
	Vertical                      // Buttons stacked vertically
)

type ProgressBackgroundMsg

type ProgressBackgroundMsg struct{}

ProgressBackgroundMsg is sent when user sends a progress operation to background

type ProgressCancelledMsg

type ProgressCancelledMsg struct{}

ProgressCancelledMsg is sent when user cancels a progress modal with Esc

type ProgressModel added in v0.7.0

type ProgressModel struct {
	Keys ProgressModelKeyMap
	// contains filtered or unexported fields
}

ProgressModel is a modal for showing an in-progress operation with Cancel and Background options. Used for AI-powered operations, long-running tasks, etc.

func NewProgressModel added in v0.7.0

func NewProgressModel(args *ProgressModelArgs) ProgressModel

NewProgressModel creates a new progress modal. The title should describe what is in progress (e.g., "Commit Message").

func (ProgressModel) Close added in v0.7.0

func (m ProgressModel) Close() ProgressModel

Close closes the modal and returns updated model

func (ProgressModel) Init added in v0.7.0

func (m ProgressModel) Init() tea.Cmd

Init implements tea.Model

func (ProgressModel) IsOpen added in v0.7.0

func (m ProgressModel) IsOpen() bool

IsOpen returns whether the modal is currently open

func (ProgressModel) Open added in v0.7.0

func (m ProgressModel) Open() ProgressModel

Open opens the modal and returns updated model

func (ProgressModel) OverlayModal added in v0.7.0

func (m ProgressModel) OverlayModal(background string) (view string)

OverlayModal renders the modal centered over the background view

func (ProgressModel) SetBackgroundEnabled added in v0.7.0

func (m ProgressModel) SetBackgroundEnabled(enabled bool) ProgressModel

SetBackgroundEnabled sets whether the Background option is shown

func (ProgressModel) SetSize added in v0.7.0

func (m ProgressModel) SetSize(width, height int) ProgressModel

SetSize sets screen dimensions

func (ProgressModel) SetTitle added in v0.7.0

func (m ProgressModel) SetTitle(title string) ProgressModel

SetTitle sets the modal title

func (ProgressModel) Update added in v0.7.0

func (m ProgressModel) Update(msg tea.Msg) (ProgressModel, tea.Cmd)

Update implements tea.Model

func (ProgressModel) View added in v0.7.0

func (m ProgressModel) View() tea.View

View renders the modal

type ProgressModelArgs added in v0.7.0

type ProgressModelArgs struct {
	ScreenWidth       int
	ScreenHeight      int
	Title             string
	BackgroundEnabled bool // Whether to show [b] Background option
}

ProgressModelArgs contains initialization arguments for ProgressModel

type ProgressModelKeyMap added in v0.7.0

type ProgressModelKeyMap struct {
	Cancel     key.Binding
	Background key.Binding
}

ProgressModelKeyMap defines the key bindings for the progress modal

func DefaultProgressModelKeyMap added in v0.7.0

func DefaultProgressModelKeyMap() ProgressModelKeyMap

DefaultProgressModelKeyMap returns the default key bindings

Jump to

Keyboard shortcuts

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