Documentation
¶
Overview ¶
Package app provides a terminal application shell on top of Bubble Tea v2: chrome (header/footer), an overlay stack, keybindings mapped to named commands, and quit handling. It hosts a Content screen and routes input following the conventions shared by the shopware-cli TUIs: overlays capture input and close by returning nil plus an Emit(result) command; ctrl+c runs the quit command, which screens may override.
The package is a trimmed copy of lattice's app shell (github.com/shyim/lattice/app) with theme, focus, jobs, toasts, and drawer support stripped — colors come from the flat internal/tui package instead.
Index ¶
- Constants
- func ApplySizeMsg(c Content, msg tea.Msg, width, mainHeight int)
- func As[T any](msg tea.Msg) (T, bool)
- func At[T any](in []T, i int) (T, bool)
- func CenterPanel(width, height int, content string) string
- func Clamp[T cmp.Ordered](v, lo, hi T) T
- func ContentAs[T Content](c Content) (T, bool)
- func Emit(msg tea.Msg) tea.Cmd
- func Filter[T any](in []T, keep func(T) bool) []T
- func FilterIndex[T any](in []T, keep func(T) bool) []int
- func Frame(width, height int, header, main, footer string) string
- func Index[T comparable](in []T, v T) int
- func IndexFunc[T any](in []T, f func(T) bool) int
- func JoinHorizontal(gap int, blocks ...string) string
- func KeyString(msg tea.KeyPressMsg) string
- func Map[T, U any](in []T, f func(T) U) []U
- func NotifySize(c Content, width, height int)
- func PropagateSize(c Content, width, height int)
- func Ptr[T any](v T) *T
- type App
- func (a *App) Commands() *CommandRegistry
- func (a *App) Content() Content
- func (a *App) Context() Context
- func (a *App) Init() tea.Cmd
- func (a *App) Keys() *KeyMap
- func (a *App) OverlayOpen() bool
- func (a *App) PopOverlay()
- func (a *App) PushOverlay(o Overlay) tea.Cmd
- func (a *App) RegisterCommand(c Command, keys ...string)
- func (a *App) Run() (tea.Model, error)
- func (a *App) RunCommand(id string) tea.Cmd
- func (a *App) SetContent(c Content)
- func (a *App) SetStatus(s string)
- func (a *App) Size() (width, height int)
- func (a *App) Status() string
- func (a *App) SwapContent(c Content) tea.Cmd
- func (a *App) TopOverlay() Overlay
- func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (a *App) View() tea.View
- type Binding
- type Command
- type CommandRegistry
- type Content
- type ContentFunc
- type Context
- type FuncOverlay
- type Harness
- type Host
- type KeyMap
- type Options
- type Overlay
- type OverlayStack
- func (s *OverlayStack) Len() int
- func (s *OverlayStack) Open() bool
- func (s *OverlayStack) Pop()
- func (s *OverlayStack) Push(o Overlay) tea.Cmd
- func (s *OverlayStack) Top() Overlay
- func (s *OverlayStack) Update(msg tea.Msg) (cmd tea.Cmd, closed bool)
- func (s *OverlayStack) View(width, height int) string
- type Region
- type SizePropagator
- type Sizeable
- type StaticContent
Constants ¶
const ( CmdQuit = "app.quit" CmdPopOverlay = "app.overlay.pop" )
Built-in command IDs.
Variables ¶
This section is empty.
Functions ¶
func ApplySizeMsg ¶
ApplySizeMsg is a convenience for Content.Update to react to WindowSizeMsg after chrome has already been accounted for via Context.MainHeight.
func As ¶
As type-asserts a Bubble Tea message.
if res, ok := app.As[versionPickedMsg](msg); ok { ... }
func CenterPanel ¶
CenterPanel places content centered in width×height, for overlays that replace the whole screen.
func Emit ¶
Emit returns a Cmd that delivers msg on the next Update. Overlays use it to hand results to the hosting Content while closing (return nil, Emit(result)).
func FilterIndex ¶
FilterIndex returns indices of elements for which keep returns true. Used by palette/picker style UIs that track filtered row indexes.
func Frame ¶
Frame stacks header, main, and footer into a fixed terminal height. Main is padded or truncated to fill the middle region.
func JoinHorizontal ¶
JoinHorizontal joins blocks side by side with a gap, top-aligned.
func KeyString ¶
func KeyString(msg tea.KeyPressMsg) string
KeyString normalizes a key press for matching. With Caps Lock on, terminals send "ctrl+C" instead of "ctrl+c"; lower-casing keeps shortcuts working.
func Map ¶
func Map[T, U any](in []T, f func(T) U) []U
Map applies f to every element and returns the results.
func NotifySize ¶
NotifySize calls SetSize if c implements Sizeable (non-recursive). Prefer PropagateSize for app content trees.
func PropagateSize ¶
PropagateSize notifies Sizeable and SizePropagator along the content tree. Container content implements SizePropagator to recurse into children.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is a Bubble Tea model that hosts content, chrome, and overlays.
func (*App) Commands ¶
func (a *App) Commands() *CommandRegistry
Commands returns the command registry.
func (*App) OverlayOpen ¶
OverlayOpen reports whether an overlay is active.
func (*App) PushOverlay ¶
PushOverlay shows an overlay on top (captures input).
func (*App) RegisterCommand ¶
RegisterCommand adds a command and optional key binding. Registering with a built-in ID (e.g. CmdQuit) replaces the built-in behavior.
func (*App) RunCommand ¶
RunCommand executes a command by ID.
func (*App) SetContent ¶
SetContent replaces the main content without initializing it. When the terminal size is already known the new content is sized immediately — otherwise it would render unsized until the next resize event.
func (*App) SwapContent ¶
SwapContent replaces the main content and returns its Init cmd. Use this for phase changes (install wizard → dashboard).
func (*App) TopOverlay ¶
TopOverlay returns the top overlay, or nil when none is open.
type Binding ¶
type Binding struct {
Keys []string
Command string
Help string
// Global bindings are always checked, even when an overlay is open.
Global bool
}
Binding maps one or more key strings (as in tea.KeyPressMsg.String()) to a command ID.
func DefaultQuitBindings ¶
func DefaultQuitBindings() []Binding
DefaultQuitBindings returns ctrl+c → app.quit. Unlike plain dashboards, the shopware-cli TUIs contain type-to-filter inputs, so "q" stays a character.
type Command ¶
type Command struct {
// ID is a stable command identifier (e.g. "app.quit").
ID string
// Title is a short human label for help / palettes.
Title string
// Run executes the command. May return a tea.Cmd.
// The App pointer is the host shell.
Run func(a *App) tea.Cmd
}
Command is a named action the app can run from a keybinding or palette.
type CommandRegistry ¶
type CommandRegistry struct {
// contains filtered or unexported fields
}
CommandRegistry holds runnable commands by ID.
func NewCommandRegistry ¶
func NewCommandRegistry() *CommandRegistry
NewCommandRegistry creates an empty registry.
func (*CommandRegistry) All ¶
func (r *CommandRegistry) All() []Command
All returns registered commands.
func (*CommandRegistry) Get ¶
func (r *CommandRegistry) Get(id string) (Command, bool)
Get returns a command by ID.
func (*CommandRegistry) Register ¶
func (r *CommandRegistry) Register(c Command)
Register adds or replaces a command. Re-registering a built-in (e.g. CmdQuit) overrides its behavior — that is how screens intercept quit to cancel a running job instead of exiting.
type Content ¶
type Content interface {
Init() tea.Cmd
Update(msg tea.Msg) (Content, tea.Cmd)
View(ctx Context) string
}
Content is the main screen body hosted inside App. Contents return string views; App wraps them in chrome + tea.View.
type ContentFunc ¶
type ContentFunc struct {
OnInit func() tea.Cmd
OnUpdate func(msg tea.Msg) (Content, tea.Cmd)
OnView func(ctx Context) string
}
ContentFunc adapts functions to Content.
type Context ¶
type Context struct {
// Width and Height are the full terminal dimensions.
Width int
Height int
// MainHeight is the rows available to Content after header/footer chrome.
MainHeight int
// OverlayOpen reports whether a modal overlay currently captures input.
OverlayOpen bool
// Chrome holds the measured header/main/footer regions.
Chrome Region
}
Context is passed to header/footer/content helpers each frame.
type FuncOverlay ¶
type FuncOverlay struct {
Name string
// OnInit optional.
OnInit func() tea.Cmd
// OnUpdate required. Return next=nil to close.
OnUpdate func(msg tea.Msg) (next Overlay, cmd tea.Cmd)
// OnView required; receives the terminal size.
OnView func(width, height int) string
}
FuncOverlay adapts functions into an Overlay (handy for quick dialogs).
func (*FuncOverlay) View ¶
func (f *FuncOverlay) View(width, height int) string
View implements Overlay.
type Harness ¶
type Harness struct {
App *App
}
Harness drives an App without a TTY for tests: construct with a size, Send messages, and assert on View output. The Init command is executed during construction; commands returned by later Update calls are NOT executed automatically — use SendCmd when a test needs the resulting message.
func NewHarness ¶
NewHarness builds an App from opts, runs Init, and delivers the initial window size.
type Host ¶
type Host interface {
Size() (width, height int)
Commands() *CommandRegistry
Keys() *KeyMap
Status() string
SetStatus(s string)
Content() Content
SetContent(c Content)
SwapContent(c Content) tea.Cmd
PushOverlay(o Overlay) tea.Cmd
PopOverlay()
OverlayOpen() bool
RunCommand(id string) tea.Cmd
RegisterCommand(c Command, keys ...string)
}
Host is the shell surface that Content and Overlay implementations use to drive the app without depending on private fields.
*App implements Host. Screens hold a Host so they can open modals, swap phases, and run commands:
type dashboard struct {
host app.Host
}
func (d *dashboard) openPicker() tea.Cmd {
return d.host.PushOverlay(&picker)
}
type KeyMap ¶
type KeyMap struct {
// contains filtered or unexported fields
}
KeyMap is a set of bindings.
type Options ¶
type Options struct {
// Content is the main screen (required for a useful app).
Content Content
// Header renders above the main content each frame (optional).
Header func(ctx Context) string
Footer func(ctx Context) string
// WindowTitle sets the terminal window title when non-empty.
WindowTitle string
// WindowTitleFunc overrides WindowTitle each frame when set.
WindowTitleFunc func(ctx Context) string
// QuitKeys binds extra keys to the quit command (ctrl+c is always bound
// unless DisableDefaultKeys is set).
QuitKeys []string
// DisableDefaultKeys skips the default ctrl+c quit binding.
DisableDefaultKeys bool
// Bindings are extra key bindings.
Bindings []Binding
// FullscreenOverlay when true (default) replaces the entire view with the
// top overlay. When false, the overlay replaces only the main region and
// the header/footer chrome stays visible.
FullscreenOverlay *bool
// AltScreen enables the alternate screen buffer (default true).
AltScreen *bool
// Mouse enables cell-motion mouse when true.
Mouse bool
}
Options configure a new App shell.
type Overlay ¶
type Overlay interface {
// Init is called once when the overlay is pushed.
Init() tea.Cmd
// Update handles a message. Returning a nil overlay closes it.
// Prefer Emit(result) for outcomes instead of getters after close.
Update(msg tea.Msg) (next Overlay, cmd tea.Cmd)
// View renders the overlay for the given terminal size (full screen).
View(width, height int) string
// ID optionally names the overlay for debugging; may be empty.
ID() string
}
Overlay is a modal layer that captures input until it closes.
Contract:
- Update returns the next overlay; nil means "closed"
- Results are delivered with Emit(resultMsg) so the parent handles outcomes
- View receives the terminal size for full-screen layout
type OverlayStack ¶
type OverlayStack struct {
// contains filtered or unexported fields
}
OverlayStack is a LIFO stack of overlays. The top overlay receives input.
func (*OverlayStack) Len ¶
func (s *OverlayStack) Len() int
Len returns the number of open overlays.
func (*OverlayStack) Open ¶
func (s *OverlayStack) Open() bool
Open reports whether any overlay is showing.
func (*OverlayStack) Push ¶
func (s *OverlayStack) Push(o Overlay) tea.Cmd
Push adds an overlay on top and returns its Init cmd.
func (*OverlayStack) Top ¶
func (s *OverlayStack) Top() Overlay
Top returns the top overlay, or nil.
func (*OverlayStack) Update ¶
Update routes a message to the top overlay. If the overlay returns nil, it is popped and closed reports true.
func (*OverlayStack) View ¶
func (s *OverlayStack) View(width, height int) string
View returns the top overlay view for the given size, or empty if none.
type Region ¶
Region sizes for a frame layout.
func ComputeRegion ¶
ComputeRegion allocates vertical space: header and footer keep their natural height (clamped), main gets the remainder.
type SizePropagator ¶
type SizePropagator interface {
PropagateSize(width, height int)
}
SizePropagator is implemented by container Content (tabs, split, phases, …) to recurse size into children. Keeps the host free of concrete container types.
type Sizeable ¶
type Sizeable interface {
SetSize(width, height int)
}
Sizeable content can receive terminal size updates when chrome is measured.
func (d *dashboard) SetSize(w, h int) {
d.listW, d.listH = w/3, h
}
type StaticContent ¶
type StaticContent struct {
Text string
}
StaticContent shows a fixed string (useful for tests and placeholders).