widgets

package
v0.0.18 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RenderQRCode

func RenderQRCode(content string, size int) string

RenderQRCode generates an ASCII QR code using Unicode block characters. Uses U+2580 (upper half block) and U+2584 (lower half block) to pack two rows of QR modules into one terminal row.

Types

type List

type List struct {
	// RenderRow, when set, replaces this widget's own row drawing. It is
	// given one item and must return exactly itemHeight lines, each
	// already fitted to width. Everything else — cursor, scrolling,
	// click-to-row mapping — is unaffected.
	//
	// This exists so a caller can have a bespoke row without forking the
	// scroll machinery, which is where the fiddly arithmetic lives.
	RenderRow func(item ListItem, selected, focused bool, width int) []string

	Items   []ListItem
	Cursor  int
	Offset  int
	Width   int
	Height  int
	Focused bool

	StyleNormal lipgloss.Style
	StyleActive lipgloss.Style
	StyleTitle  lipgloss.Style
	StyleSub    lipgloss.Style
	StyleMeta   lipgloss.Style
	StyleBadge  lipgloss.Style
	StyleOnline lipgloss.Style

	// StyleEmpty draws the "No items" placeholder. Supplied by the caller
	// like every other style here — this widget used to reach for a
	// hard-coded #565F89, which is how a generic list ended up owning an
	// opinion about the application's palette.
	StyleEmpty lipgloss.Style
	// contains filtered or unexported fields
}

List is a generic scrollable list widget with vim-style navigation.

func NewList

func NewList() List

NewList creates a new list widget.

func (*List) ItemAtRow

func (l *List) ItemAtRow(row int) int

ItemAtRow returns the index of the item displayed at the given local row (0-based, relative to the top of the list's visible area), or -1.

func (*List) ScrollBy

func (l *List) ScrollBy(n int)

ScrollBy moves the cursor by n items (negative scrolls up).

func (*List) SelectIndex

func (l *List) SelectIndex(i int) bool

SelectIndex moves the cursor to the given item index. Returns false if the index is out of bounds.

func (*List) SelectedID

func (l *List) SelectedID() string

SelectedID returns the ID of the currently selected item.

func (*List) SelectedItem

func (l *List) SelectedItem() *ListItem

SelectedItem returns the currently selected item, or nil.

func (*List) SetItems

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

SetItems replaces all items, keeping cursor in bounds.

func (*List) Update

func (l *List) Update(msg tea.Msg) (selected bool)

Update handles key events for navigation.

func (*List) View

func (l *List) View() string

View renders the list.

type ListItem

type ListItem struct {
	ID       string
	Title    string
	Subtitle string
	Badge    string

	// MetaAt is the instant Meta describes, or 0 when Meta is not a time.
	// A relative label goes stale where an absolute one cannot, so the
	// owner keeps the fact beside the sentence about it.
	MetaAt int32
	Meta   string
	Online bool
	Muted  bool // chat notifications are muted; render dimmed

	// Kind and Saved describe what the item IS, for callers whose rows
	// show a type mark. TUI 2.0's chat list uses them for its sigil, which
	// replaces the avatar as the identity signal — the widget itself never
	// interprets them.
	Kind  int
	Saved bool
}

ListItem represents a single item in a scrollable list.

type Spinner

type Spinner struct {
	Frame  int
	Label  string
	Style  lipgloss.Style
	Active bool
}

Spinner is a loading spinner widget.

func NewSpinner

func NewSpinner(label string) Spinner

NewSpinner creates a new spinner.

func (Spinner) Tick

func (s Spinner) Tick() tea.Cmd

Tick returns a command that triggers the next spinner frame.

func (*Spinner) Update

func (s *Spinner) Update(msg tea.Msg) tea.Cmd

Update advances the spinner frame.

func (*Spinner) View

func (s *Spinner) View() string

View renders the spinner.

type SpinnerTickMsg

type SpinnerTickMsg struct{}

SpinnerTickMsg triggers the next spinner frame.

type Tabs

type Tabs struct {
	Labels []string
	Active int
	Width  int

	StyleTab       lipgloss.Style
	StyleTabActive lipgloss.Style
}

Tabs is a tab bar widget for switching between views.

func NewTabs

func NewTabs(labels []string) Tabs

NewTabs creates a new tab bar.

func (*Tabs) Update

func (t *Tabs) Update(msg tea.Msg) bool

Update handles tab switching via left/right keys or number keys.

func (*Tabs) View

func (t *Tabs) View() string

View renders the tab bar.

type TextArea

type TextArea struct {
	Value       string
	Cursor      int
	Width       int
	Height      int
	Focused     bool
	Placeholder string
	Style       lipgloss.Style

	// StylePlaceholder draws the prompt shown while the field is empty and
	// unfocused. Supplied by the caller like Style — this widget used to
	// reach for a hard-coded #565F89, which is how a generic input ended up
	// holding an opinion about the application's palette.
	StylePlaceholder lipgloss.Style
	// MultiLine makes Value a multi-line buffer: pastes keep their line
	// breaks, InsertNewline is meaningful, and View renders one row per
	// line inside a vertically scrolling window Height rows tall.
	MultiLine bool
	// EchoPassword, on the single-line path, renders a bullet per rune
	// instead of Value so a 2FA password is not visible on screen. The
	// cursor and placeholder (when empty) are unchanged.
	EchoPassword bool
}

TextArea is the shared text input widget. It runs in two shapes, selected by MultiLine:

  • MultiLine false: a single-line input with a horizontally scrolling view window (the search overlay, chatview's find bar, the auth prompts).
  • MultiLine true: a multi-line editor (the composer). Value may contain "\n"; every motion and edit below is line-aware, and the view scrolls vertically to keep the cursor's line on screen.

The shape is a declared property, not something inferred from Height. Height is a layout number: it arrives from a WindowSizeMsg, is zero before the first one, and can be squeezed to 1 on a short terminal. Deriving "is this a multi-line editor" from it meant the composer silently became a single-line widget in exactly those moments — and flattened the newlines out of anything pasted into it.

Key handling is readline/emacs and is unconditional — callers that want vi semantics (the composer) intercept keys before delegating here and drive the exported edit primitives directly.

Bindings are matched on tea.KeyPressMsg.Keystroke(), never on String(). String() returns Key.Text whenever the terminal attached any, so a Kitty-protocol shift+enter (CSI 13;2;13u) reports String() == "\r" while Keystroke() correctly reports "shift+enter" — matching on String() would both miss the binding and insert a stray carriage return. See the keyPress doc comment in internal/app/keymap.go.

func NewTextArea

func NewTextArea() TextArea

func (*TextArea) Backspace

func (t *TextArea) Backspace()

Backspace deletes the rune before the cursor, joining lines when the cursor sits at the start of one.

func (*TextArea) DeleteChar

func (t *TextArea) DeleteChar()

DeleteChar deletes the rune under the cursor (emacs ctrl+d, vi x).

func (*TextArea) DeleteLine

func (t *TextArea) DeleteLine()

DeleteLine removes the whole line under the cursor including its line break (vi dd) and lands the cursor at the start of what is now the current line.

func (*TextArea) DeleteRange

func (t *TextArea) DeleteRange(from, to int)

DeleteRange removes the runes in [from, to) and puts the cursor at from.

func (*TextArea) InsertNewline

func (t *TextArea) InsertNewline()

InsertNewline inserts a hard line break. It is deliberately separate from InsertString/key handling: Enter means "submit" to every component that embeds a TextArea, so a line break can only ever arrive through an explicit call (the composer's ctrl+j / shift+enter chords, or vi's o/O).

func (*TextArea) InsertString

func (t *TextArea) InsertString(s string)

InsertString inserts s at the cursor and leaves the cursor after it.

func (*TextArea) KillToLineEnd

func (t *TextArea) KillToLineEnd()

KillToLineEnd removes the rest of the current line (emacs ctrl+k, vi D). On an already-empty line end it swallows the line break, like readline.

func (*TextArea) KillToLineStart

func (t *TextArea) KillToLineStart()

KillToLineStart removes the current line up to the cursor (emacs ctrl+u).

func (*TextArea) KillWordBack

func (t *TextArea) KillWordBack()

KillWordBack deletes from the start of the previous word to the cursor (emacs ctrl+w).

func (*TextArea) Len

func (t *TextArea) Len() int

Len returns the length of Value in runes.

func (*TextArea) LineBounds

func (t *TextArea) LineBounds() (start, end int)

LineBounds returns the [start, end) rune range of the line holding the cursor. end excludes the terminating "\n".

func (*TextArea) MoveDown

func (t *TextArea) MoveDown()

MoveDown moves to the next line, keeping the column where possible. It is a no-op on the last line.

func (*TextArea) MoveLeft

func (t *TextArea) MoveLeft()

func (*TextArea) MoveLineEnd

func (t *TextArea) MoveLineEnd()

func (*TextArea) MoveLineStart

func (t *TextArea) MoveLineStart()

func (*TextArea) MoveRight

func (t *TextArea) MoveRight()

func (*TextArea) MoveUp

func (t *TextArea) MoveUp()

MoveUp moves to the previous line, keeping the column where possible. It is a no-op on the first line (and therefore in single-line mode).

func (*TextArea) MoveWordBack

func (t *TextArea) MoveWordBack()

MoveWordBack moves to the start of the previous word (vi b).

func (*TextArea) MoveWordForward

func (t *TextArea) MoveWordForward()

MoveWordForward moves to the start of the next word (vi w).

func (*TextArea) Reset

func (t *TextArea) Reset()

func (*TextArea) Update

func (t *TextArea) Update(msg tea.Msg) (submitted bool)

func (*TextArea) View

func (t *TextArea) View() string

func (*TextArea) WordBackStart

func (t *TextArea) WordBackStart() int

WordBackStart returns the offset of the start of the word before the cursor: skip separators, then skip the word itself.

func (*TextArea) WordForwardStart

func (t *TextArea) WordForwardStart() int

WordForwardStart returns the offset of the start of the next word: skip the current word, then skip separators.

Jump to

Keyboard shortcuts

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