palette

package
v1.43.8 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

palette

A command palette Bubble Tea component with pluggable modes — fuzzy-filter a static list, dispatch an async search, or mix both behind different prefixes.

import "github.com/stripe/stripe-cli/pkg/docs/internal/palette"

var commands = []palette.Item{
    palette.Command{Name: "Open file"},
    palette.Command{Name: "Save"},
    palette.Command{Name: "Quit"},
}

mode := palette.Mode{
    Name: "commands",
    Items: func(_ palette.Model, q string) []palette.Item {
        return palette.FilterFuzzy(commands, q)
    },
}

p := palette.New(palette.WithModes(mode))

Each palette.Mode owns its own Match, Items, and optional async Search or typeable Facets.

Built with

Documentation

Overview

Package palette provides a command-palette bubble for Bubble Tea programs. Hosts compose one or more Modes: each Mode owns its own Match (which inputs it claims), Items (the candidate list), and optionally an async Search dispatcher and typeable Facets. With no WithModes, the palette uses a single empty catch-all mode; the palette renders cleanly but shows no items until hosts wire one up.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseFacets

func ParseFacets(input string, facets []Facet) (text string, parsed map[string][]string)

ParseFacets tokenizes input on whitespace and returns the free-text portion (tokens that aren't registered facets, joined with single spaces) plus a map of facet name → values. Use this from a Mode's Items/Search closures to apply facet filters without re-implementing the parser.

Types

type Command

type Command struct {
	ID   string
	Name string
	Desc string
	// Run is invoked when the user selects this command and presses
	// Enter. It may return nil if there's nothing to dispatch.
	Run func() tea.Cmd
}

Command is a built-in Item for command-style palettes. Implements DefaultItem so the DefaultDelegate renders it without extra work, and carries an optional Run hook the host can fire on Enter.

func (Command) Description

func (c Command) Description() string

Description is rendered as the secondary line by DefaultDelegate.

func (Command) FilterValue

func (c Command) FilterValue() string

FilterValue is what fuzzy matching is performed against.

func (Command) Title

func (c Command) Title() string

Title is rendered as the primary line by DefaultDelegate.

type DefaultDelegate

type DefaultDelegate struct {
	Styles          DelegateStyles
	ShowDescription bool
}

DefaultDelegate renders DefaultItems with a title/description layout and a selection marker. When ShowDescription is false, only the title line is drawn.

func NewDefaultDelegate

func NewDefaultDelegate() DefaultDelegate

NewDefaultDelegate returns a DefaultDelegate with sensible defaults. Selected rows are rendered with reverse-video so the highlight reads against any terminal palette without picking a brand color.

func (DefaultDelegate) Height

func (d DefaultDelegate) Height() int

Height reports two rows when ShowDescription is on, one otherwise.

func (DefaultDelegate) Render

func (d DefaultDelegate) Render(w io.Writer, m Model, index int, item Item)

Render draws one item: a selection marker followed by the title, and (when ShowDescription is on and the item exposes one) a faint description line indented under the title. Selected rows fill the palette's width so the highlight background reaches the right edge. Text is truncated to the palette's current width when known.

func (DefaultDelegate) Spacing

func (d DefaultDelegate) Spacing() int

Spacing reports zero blank rows between items by default.

func (DefaultDelegate) Update

func (d DefaultDelegate) Update(_ tea.Msg, _ *Model) tea.Cmd

Update is a no-op by default. Override by wrapping or replacing.

type DefaultItem

type DefaultItem interface {
	Item
	Title() string
	Description() string
}

DefaultItem is the convention DefaultDelegate knows how to render. Implement this if you want title/description rendering out of the box.

type DelegateStyles

type DelegateStyles struct {
	Title            lipgloss.Style
	Description      lipgloss.Style
	SelectedTitle    lipgloss.Style
	SelectedDesc     lipgloss.Style
	SelectionMarker  string
	UnselectedMarker string
}

DelegateStyles holds the styles used by DefaultDelegate.

type Facet

type Facet struct {
	// Name is the prefix users type before ":" to trigger completion.
	// Should not contain whitespace or ":".
	Name string

	// Desc is an optional context hint shown above the value list
	// while completing this facet.
	Desc string

	// Items returns sync values for the given partial. The host owns
	// the filtering; the palette doesn't post-filter the returned
	// slice.
	Items func(partial string) []Item

	// Resolve dispatches an async value lookup. The returned tea.Cmd
	// must eventually emit a FacetResultMsg whose Facet and Partial
	// match the inputs. The ctx is canceled when the partial changes,
	// the cursor leaves the token, or the palette is Reset.
	Resolve func(ctx context.Context, partial string) tea.Cmd

	// Debounce delays Resolve dispatch after the partial last changed.
	// Ignored when Resolve is nil.
	Debounce time.Duration
}

Facet defines a token-based filter inside a Mode. When the cursor sits inside a "<Name>:<partial>" token in the input, the palette enters a value-completion sub-state listing this facet's matching values. Exactly one of Items or Resolve must be set; if both are, Items wins.

type FacetResultMsg

type FacetResultMsg struct {
	Facet   string
	Partial string
	Results []Item
	Err     error
}

FacetResultMsg is what Facet.Resolve eventually emits. The palette drops it as stale when Facet/Partial no longer match the cursor's current token.

type Item

type Item interface {
	FilterValue() string
}

Item is anything that can appear in the palette list. Both predefined commands and async search results implement it.

func FilterFuzzy

func FilterFuzzy(items []Item, query string) []Item

FilterFuzzy returns items whose FilterValue is a fuzzy-subsequence match for query, ordered by relevance (best first). An empty query returns the input unchanged. Exported so a Mode's Items closure can fuzzy-filter its own item slice without re-implementing the matcher.

type ItemDelegate

type ItemDelegate interface {
	// Height is the number of terminal rows one item occupies.
	Height() int
	// Spacing is the number of blank rows between adjacent items.
	Spacing() int
	// Update receives messages while the delegate is active. Implement
	// item-level keybindings here, or return nil to opt out.
	Update(msg tea.Msg, m *Model) tea.Cmd
	// Render draws one item at the given visible index to w.
	Render(w io.Writer, m Model, index int, item Item)
}

ItemDelegate controls how an Item is rendered and how key events reach the currently selected item. Mirrors bubbles/list.ItemDelegate.

type KeyMap

type KeyMap struct {
	Execute  key.Binding
	Cancel   key.Binding
	Down     key.Binding
	Up       key.Binding
	NextPage key.Binding
	PrevPage key.Binding

	// Navigate is a display-only binding rendered in ShortHelp() as a
	// combined arrow legend (default "↑↓ navigate"). It's never
	// matched in Update — Up/Down/Ctrl+P/Ctrl+N do the actual cursor
	// work. Override the help text (e.g. "↑↓ select") by reassigning
	// this field.
	Navigate key.Binding
}

KeyMap holds the palette's keybindings. Override individual fields to remap; pass the whole struct via WithKeyMap to swap wholesale.

func DefaultKeyMap

func DefaultKeyMap() KeyMap

DefaultKeyMap returns the standard keybindings.

type Mode

type Mode struct {
	// Name identifies the mode for logging and host status displays.
	// Also used as the cache key for Search results — see
	// Model.Results.
	Name string

	// Prompt is the glyph rendered before the input field. Should be
	// the same display width as the configured spinner so the input
	// text doesn't shift when the spinner swaps in during search. An
	// empty Prompt falls back to defaultPrompt ("⣿ ").
	Prompt string

	// Placeholder is the hint text shown in the input while it's empty
	// and this mode is active. Empty falls back to the palette-level
	// placeholder set via WithPlaceholder.
	Placeholder string

	// EmptyMessage is shown in place of the item list when this mode
	// is active, the user has typed a query, and Items returns no
	// candidates. Empty falls back to the palette-level message set
	// via WithEmptyMessage; when both are empty no message is rendered.
	EmptyMessage string

	// Debounce is how long the palette waits after the input stops
	// changing before invoking Search. Zero means dispatch on the
	// next tick. Ignored when Search is nil.
	Debounce time.Duration

	// Match reports whether this mode applies to the given raw input.
	// A nil Match matches anything.
	Match func(input string) bool

	// Query extracts the meaningful query string from the raw input —
	// typically by stripping a leading prefix. A nil Query returns
	// the input unchanged.
	Query func(input string) string

	// Items returns the candidate items for this mode given the
	// palette state and the extracted query. For sync modes it does
	// the filtering inline; for async modes it typically reads from
	// the palette's Results cache that Search populates. A nil Items
	// returns nil.
	Items func(m Model, query string) []Item

	// Search is the async dispatcher. When the input changes inside
	// this mode and the debounce window elapses, the palette calls
	// Search and the returned tea.Cmd must eventually yield a
	// SearchResultMsg with the matching Mode name. The ctx is
	// canceled when a newer search supersedes this one or when the
	// active mode changes — implementations should pass it through
	// to their HTTP/DB call. Nil means the mode is purely synchronous.
	Search func(ctx context.Context, query string) tea.Cmd

	// Facets registers typeable "<Name>:<value>" filters for this
	// mode. When the cursor sits inside such a token, the palette
	// swaps the item list for a value picker scoped to the matching
	// Facet. See palette.ParseFacets to apply parsed filters from a
	// Mode's Items/Search closure.
	Facets []Facet
}

Mode describes how the palette interprets the current input. The active mode is the first one in the configured list whose Match returns true; a nil Match matches anything, so it's typically used as the fallback (last entry).

type Model

type Model struct {
	KeyMap KeyMap
	Styles Styles
	// contains filtered or unexported fields
}

Model is the palette bubble.

func New

func New(opts ...Option) Model

New constructs a palette Model with sensible defaults. Apply Options to override.

func (*Model) Blur

func (m *Model) Blur()

Blur removes keyboard focus from the palette.

func (Model) Cursor

func (m Model) Cursor() int

Cursor returns the absolute index of the highlighted item in the currently visible items list. Stable across page changes and safe to call from inside ItemDelegate.Render.

func (*Model) Focus

func (m *Model) Focus() tea.Cmd

Focus directs keyboard input to the palette.

func (Model) FullHelp

func (m Model) FullHelp() [][]key.Binding

FullHelp returns the expanded key groups for help bubbles displaying the full layout (not used by the palette itself by default, but available for hosts that wire up "?"-toggled help).

func (Model) Init

func (m Model) Init() tea.Cmd

Init is part of the tea.Model contract. The palette emits no startup command — callers compose it into their own program's Init.

func (Model) InnerWidth

func (m Model) InnerWidth() int

InnerWidth is the usable width inside the Container border/padding. Returns 0 when the outer width has not yet been set.

func (Model) IsSelected

func (m Model) IsSelected(visibleIndex int) bool

IsSelected reports whether the given visible-row index in the current page is the highlighted row. ItemDelegate.Render implementations should use this instead of comparing against a raw cursor field — it's correct on every page and zero outside a Render call (so it's safe to call defensively).

func (Model) Items

func (m Model) Items() []Item

Items returns the candidate items currently visible in the palette. During facet completion this is the active Facet's value list (sync via Facet.Items or the cached async results); otherwise it's the active Mode's items.

func (Model) Loading

func (m Model) Loading() bool

Loading reports whether a Search is currently in flight.

func (Model) Mode

func (m Model) Mode() Mode

Mode returns the currently active Mode — the first in the configured list whose Match returns true (or whose Match is nil). Returns a zero Mode when no modes are configured.

func (Model) Page

func (m Model) Page() int

Page returns the current page (0-indexed).

func (Model) Query

func (m Model) Query() string

Query returns the active mode's interpretation of the input value (typically with a leading prefix stripped). Falls back to the raw input when the active mode has no Query function.

func (*Model) Reset

func (m *Model) Reset()

Reset clears the input and result state, and cancels any in-flight Search or facet Resolve.

func (Model) Results

func (m Model) Results(modeName string) []Item

Results returns the cached items for the named mode (typically populated by that mode's Search closure via SearchResultMsg). A mode's Items closure usually reads from here.

func (Model) Selected

func (m Model) Selected() Item

Selected returns the highlighted item, or nil if none.

func (*Model) SetHeight

func (m *Model) SetHeight(h int)

SetHeight overrides the palette's outer height. Currently advisory — pagination is driven by WithPageSize.

func (*Model) SetWidth

func (m *Model) SetWidth(w int)

SetWidth overrides the palette's outer width. Useful when the palette is laid out manually (e.g., as a fixed-width modal overlay) rather than filling the host's WindowSizeMsg.

func (Model) ShortHelp

func (m Model) ShortHelp() []key.Binding

ShortHelp returns the compact key list rendered by the help bubble at the bottom of the palette. Combines Up/Down into a single synthetic "↑↓ navigate" entry for legibility; the actual KeyMap bindings remain split since they're separate actions.

func (Model) TotalPages

func (m Model) TotalPages() int

TotalPages returns the number of pages.

func (Model) Update

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

Update handles cursor navigation, the async search lifecycle (debounce → dispatch → result), spinner ticks, and forwards remaining messages to the textinput.

func (Model) Value

func (m Model) Value() string

Value returns the raw input value.

func (Model) View

func (m Model) View() string

View composes the palette layout: an optional title, the text input, and the visible items rendered through the configured ItemDelegate, wrapped in the Container style. Items are passed the inner width so the delegate's selection background can fill the row. The spinner row and paginator footer land in later milestones.

func (Model) Width

func (m Model) Width() int

Width returns the column width available for rendering a row. During ItemDelegate.Render this is the inner row width (set by the palette for that render pass); elsewhere it falls back to InnerWidth so callers can ask "how wide is my content area?" with one method regardless of context.

type Option

type Option func(*Model)

Option configures a palette Model. Apply with New(...Option).

func WithDelegate

func WithDelegate(d ItemDelegate) Option

WithDelegate overrides the ItemDelegate used to render items.

func WithEmptyMessage

func WithEmptyMessage(s string) Option

WithEmptyMessage sets the default text shown in place of the item list when the user has typed a query but the active mode returns no candidates. Modes can override this for their own context via Mode.EmptyMessage. The message is suppressed while a search is in flight (the spinner is the loading indicator) and while a facet completion is active.

func WithHelp

func WithHelp(show bool) Option

WithHelp toggles the short-help row at the bottom of the palette. On by default.

func WithKeyMap

func WithKeyMap(km KeyMap) Option

WithKeyMap overrides the default keybindings.

func WithModes

func WithModes(modes ...Mode) Option

WithModes replaces the default empty mode with the supplied modes, in priority order — the first whose Match returns true wins. Make the last entry a fallback (Match: nil) so some mode always applies.

func WithOnExecute

func WithOnExecute(fn func(Item) tea.Cmd) Option

WithOnExecute registers a callback fired synchronously inside the palette's Update when the user presses Execute (Enter) on a highlighted item. The returned tea.Cmd is batched alongside the SelectedMsg dispatch and any Command.Run() cmd, so hosts can:

  • read the selected Item without pattern-matching on SelectedMsg;
  • return a "close overlay" / "dispatch action" cmd in the same tick the keypress was received;
  • keep Command.Run chaining cleanly (the hook is additive).

Return nil to opt out of adding a cmd for a particular item.

func WithPageSize

func WithPageSize(n int) Option

WithPageSize sets a fixed number of items per page. Pass 0 to auto-fit to the available terminal height (the default).

func WithPaginatorType

func WithPaginatorType(t paginator.Type) Option

WithPaginatorType selects between dot indicators and "1/N" numeric.

func WithPlaceholder

func WithPlaceholder(s string) Option

WithPlaceholder sets the default hint text shown in the input while it's empty. Modes can override this for their own context via Mode.Placeholder.

func WithStyles

func WithStyles(s Styles) Option

WithStyles overrides the default visual styles.

func WithTitle

func WithTitle(s string) Option

WithTitle sets the optional section header rendered above the input. Pass an empty string (the default) for no title row.

type SearchResultMsg

type SearchResultMsg struct {
	Mode    string
	Query   string
	Results []Item
	Err     error
}

SearchResultMsg is the message a Mode's Search closure eventually emits with the items matching a given query. Mode is the Name of the mode that produced this result — the palette uses it to route the items into the right per-mode cache and to reject results from a mode that's no longer active. Err is non-nil if the search failed; Results should be ignored in that case.

type SelectedMsg

type SelectedMsg struct {
	Item Item
}

SelectedMsg is dispatched when the user presses Execute (Enter by default) on a highlighted item. The host program type-switches on Item to decide how to react — close the palette, log, navigate, etc. When the item is a Command with a non-nil Run, the palette also fires Run()'s tea.Cmd in the same batch.

type Styles

type Styles struct {
	// Container wraps the whole palette. The default is a rounded
	// border with no padding — padding is applied manually as a
	// per-line indent so selection backgrounds can fill the full row.
	Container lipgloss.Style
	// Title styles the optional section header at the top of the
	// palette (see WithTitle).
	Title lipgloss.Style
	// Indent is the per-line left margin inside the container, as a
	// literal string. Two spaces by default.
	Indent string
	// Prompt styles the leading mode-prompt glyph rendered in front of
	// the input. Not applied while the spinner is in flight (the
	// spinner owns its own styling).
	Prompt lipgloss.Style
	// Placeholder styles the textinput's placeholder text shown while
	// the input is empty. Propagated into the underlying textinput's
	// Focused / Blurred placeholder styles.
	Placeholder lipgloss.Style
	// EmptyMessage styles the no-results message rendered in place of
	// the item list when the active mode returns no candidates for the
	// current query. See Mode.EmptyMessage and WithEmptyMessage.
	EmptyMessage lipgloss.Style
	// SpinnerLabel styles the text next to the spinner glyph while a
	// search is in flight.
	SpinnerLabel lipgloss.Style
	// FacetHeader styles the facet-completion hint shown in the footer
	// row while the palette is completing a facet token.
	FacetHeader lipgloss.Style
	// Footer wraps the paginator row at the bottom.
	Footer lipgloss.Style
}

Styles holds the lipgloss styles the palette uses to render itself.

func DefaultStyles

func DefaultStyles() Styles

DefaultStyles returns sensible defaults. Override fields individually or pass a whole struct via WithStyles.

Jump to

Keyboard shortcuts

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