Documentation
¶
Overview ¶
Package dialog is a domain-free stack of modal overlays for a Bubble Tea application. A Dialog contributes only its parts - a title, a body, and a row of key hints - and stays ignorant of borders, centering, scrolling, and screen size. A Shell supplies that chrome: it caps a dialog to a fraction of the screen and to an absolute maximum, scrolls the body internally when it overflows the cap, and centers the framed box over a backdrop. A Stack owns the ordered dialogs (top is last) plus one Shell, routes every message to the top dialog, and pops it when the dialog reports it is done - handing the popped Dialog back so the caller can read a typed payload off it.
Index ¶
- func RenderHints(keyStyle, textStyle lg.Style, hints []key.Hint) string
- type Confirm
- type Dialog
- type Footered
- type Pick
- type Result
- type ScrollHint
- type SelfFramed
- type Shell
- type ShellConfig
- type Stack
- func (s *Stack) Active() bool
- func (s *Stack) Push(d Dialog)
- func (s *Stack) PushWithGrace(d Dialog)
- func (s *Stack) SetClock(now func() time.Time)
- func (s *Stack) SetShell(shell Shell)
- func (s *Stack) Top() Dialog
- func (s *Stack) Update(msg tea.Msg) (tea.Cmd, Dialog, Result)
- func (s *Stack) View(backdrop string, screenW, screenH int) string
- type Styles
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RenderHints ¶
RenderHints renders a hint row through primer's inline key renderer, the same "(y)es" styling the rest of a TUI uses; no hints means no row. Shared so a Footered dialog pinning its own hint row renders it identically to the Shell's.
Types ¶
type Confirm ¶
type Confirm struct {
// contains filtered or unexported fields
}
Confirm is a generic y/N confirmation dialog. It renders a prompt as its body and resolves to ResultSubmit when accepted or ResultClose when declined, exposing the decision through Confirmed. It defaults to not confirmed, so a dialog abandoned any other way - escape, or a lost overlay the Stack drops - is read as a decline, the safe default for a guard in front of a destructive action.
It carries pointer semantics: Update mutates and returns the same value, so the caller reads Confirmed off the very pointer the Stack pops.
func NewConfirm ¶
NewConfirm returns a Confirm that asks prompt, initially not confirmed.
func (*Confirm) Confirmed ¶
Confirmed reports whether the dialog was accepted. It is meaningful once the Stack has popped the dialog: a Confirm resolved with ResultSubmit reports true, and one that was closed - or is still open - reports false.
func (*Confirm) Content ¶
Content renders the prompt as the body. The Shell frames, centers, and wraps it, so Confirm is box-less and draws no border of its own.
type Dialog ¶
type Dialog interface {
// Title is the heading the Shell renders above the body; "" omits it.
Title() string
// Update routes a message into the dialog. It returns the (possibly
// updated) dialog so value-typed implementations compose cleanly, a command
// to run, and the Result telling the Stack whether to keep or pop it.
Update(msg tea.Msg) (Dialog, tea.Cmd, Result)
// Content renders the body - and only the body - to fit within width
// columns. The Shell frames, scrolls, and centers whatever it returns.
Content(width int) string
// Hints are the key bindings the Shell renders as the dialog's foot row.
Hints() []key.Hint
}
Dialog is one overlay's content, free of any chrome. Implementations render their body into the width the Shell allots and never draw their own border, title bar, or scrollbar - the Shell owns all of that. The interface is deliberately minimal: async and busy state belong to the concrete dialog and its own messages, not to this contract.
type Footered ¶
type Footered interface {
}
Footered is an optional Dialog capability. A dialog that wants a row pinned below its scrollable body - a form's hint/confirm/submitting row that must stay visible however the body scrolls - returns that row here. The Shell scrolls only Content and always renders Footer beneath the viewport (inside the same box). A dialog that does not implement this frames through Content alone, as before. Pairs with ScrollHint, whose region is measured against Content, not the footer.
type Pick ¶
type Pick struct {
// contains filtered or unexported fields
}
Pick is a filterable single-choice dialog: it wraps a searching list.Model, letting the Shell frame and scroll a type-to-filter select list. Typing narrows the list and the arrows move the cursor - routed straight into the list - while enter accepts the highlighted row (ResultSubmit) and esc cancels (ResultClose), the two keys the list itself deliberately leaves to its caller. Selection reports the original row index, so the caller keeps a parallel slice of values and maps the index back on submit.
It carries pointer semantics: Update mutates and returns the same value, so the caller reads Selected off the very pointer the Stack pops.
func NewPick ¶
NewPick returns a Pick over rows, titled title and with the cursor on the first entry. Rows may carry styling; the filter matches their visible text.
func (*Pick) Content ¶
Content renders the list - filter line and matching rows - as the body. The list does not wrap to a width, so the parameter is unused; the Shell frames and scrolls whatever it returns.
func (*Pick) ScrollTo ¶
ScrollTo keeps the highlighted row visible as the cursor moves through a list taller than the box. Implements ScrollHint.
func (*Pick) Selected ¶
Selected returns the original index of the highlighted row; ok is false when the filter matches nothing (or the list is empty). It is meaningful once the Stack has popped the dialog with ResultSubmit.
type Result ¶
type Result int
Result is what a dialog's Update reports back to the Stack: whether the dialog stays open, or is finished and should be popped. Submit and Close are distinct so a caller reading the popped dialog can tell an accepted result from an abandoned one without inspecting the payload.
const ( // ResultNone means the dialog consumed (or ignored) the message and stays // open. ResultNone Result = iota // ResultSubmit means the dialog completed successfully; the Stack pops it // and returns it so the caller can read a typed payload. ResultSubmit // ResultClose means the dialog was dismissed without a result; the Stack // pops it just the same, and the caller distinguishes the two by Result. ResultClose )
func DismissResult ¶
DismissResult is the shared Update rule for momentary dialogs (a help sheet, an activity log): the first key press or mouse click closes them, every other message leaves them open.
type ScrollHint ¶
ScrollHint is an optional Dialog capability. A dialog whose body can grow taller than the Shell's height cap reports the body region it wants kept on screen - a form's focused field, say - so the Shell scrolls to follow it as focus moves rather than stranding the cursor below the fold. top is the region's first body line (0-based, excluding any Shell-drawn title); height is its line span. A dialog that does not implement this, or returns ok false, leaves the viewport at the top (the default before this seam existed).
type SelfFramed ¶
type SelfFramed interface {
SelfFramed() bool
}
SelfFramed is an optional Dialog capability. A dialog that draws its own complete frame - border, title, and sizing - reports true, and the Stack places its Content verbatim over the backdrop instead of framing and scrolling it through the Shell (which would clip and interleave a pre-drawn box). A dialog that does not implement this, or returns false, gets the Shell's chrome as usual.
type Shell ¶
type Shell struct {
// contains filtered or unexported fields
}
Shell frames a dialog: it caps the box to a fraction of the screen and to an absolute maximum, scrolls an overflowing body internally, and centers the result over a backdrop. It carries value semantics and is safe to copy - the Stack holds one by value.
func NewShell ¶
func NewShell(cfg ShellConfig) Shell
NewShell builds a Shell from cfg. It constructs the viewport once so RenderScrollable reuses its default key mappings and configuration.
type ShellConfig ¶
type ShellConfig struct {
Styles Styles
// MaxWidth and MaxHeight cap the box's total size in columns and rows; 0
// disables the cap. They keep a long prefill or a tall body from bleeding
// past a large screen.
MaxWidth int
MaxHeight int
// WidthFraction and HeightFraction cap the box to a share of the screen in
// (0,1]; 0 disables. They bind on small screens where the absolute caps do
// not.
WidthFraction float64
HeightFraction float64
// Margin keeps this many columns and rows clear at the screen edges when
// the screen, not a cap, is the binding constraint.
Margin int
// Scrollbar configures the internal scrollbar's glyphs and geometry.
Scrollbar scrollbar.Config
}
ShellConfig declares a Shell's chrome and sizing bounds.
type Stack ¶
type Stack struct {
// contains filtered or unexported fields
}
Stack owns an ordered set of modal dialogs - the top is the last element, the one the user interacts with - plus the Shell that frames them. Only the top dialog is live; the rest wait beneath it and are redrawn only when they return to the top. The zero value is unusable; construct with New.
func (*Stack) Push ¶
Push opens d as the new top dialog. A push is user-driven, so it also ends any input grace a previous top was holding.
func (*Stack) PushWithGrace ¶
PushWithGrace opens d as the new top dialog behind a brief input grace, for dialogs that open on an async result (a picker pushed when a fetch lands) rather than on a keypress. Keys are absorbed until the keyboard has been quiet for graceQuiet, bounded by graceCeiling from the open; every other message routes normally. When a graced dialog of the same concrete type closed within graceReopenExempt, the grace is skipped - a rapid prompt loop is user-driven by then, and each deliberate key must land.
func (*Stack) SetClock ¶
SetClock replaces the clock the input-grace windows are measured on - the injected-now seam that lets tests drive the grace deterministically instead of sleeping through it.
func (*Stack) SetShell ¶
SetShell swaps the frame the Stack draws its dialogs with, letting the owner refresh chrome styles (e.g. after a theme change) without dropping any open dialog.
func (*Stack) Update ¶
Update routes a message to the top dialog. When it reports ResultSubmit or ResultClose it is popped and returned, so the caller can type-assert it and read a typed payload; otherwise the returned Dialog is nil. While an input grace is active (see PushWithGrace) key presses are absorbed instead of routed; the first key that arrives after the quiet window - or after the ceiling - ends the grace and drives the dialog.
type Styles ¶
type Styles struct {
// Box draws the border and padding around the whole dialog; inject the
// app's overlay style.
Box lg.Style
// Title styles the heading line above the body.
Title lg.Style
// HintKey and HintText style the key and description halves of the foot
// row, matching the rest of the TUI's hint rendering.
HintKey lg.Style
HintText lg.Style
// Scrollbar styles the internal scrollbar shown when the body overflows
// the height cap.
Scrollbar scrollbar.Styles
}
Styles are the Shell's render styles, injected by the owner so the package stays theme-agnostic.