tui

package
v0.18.5 Latest Latest
Warning

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

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

README

internal/tui — the shopware-cli terminal UI library

Shared building blocks for every terminal UI in the CLI: plain-output helpers for commands, Bubble Tea components for the interactive TUIs (project dev, project upgrade), an application shell that hosts them, and overlay kits for the common modal patterns.

internal/tui             flat component library (this package)
internal/tui/app         application shell: chrome, overlay stack, key/command routing
internal/tui/picker      overlay kit: filterable single-select list modal
internal/tui/prompt      overlay kit: multi-button confirm modal
internal/tui/textprompt  overlay kit: single-line text input modal

Conventions

  • Components follow New(Options) → Render() (or View(width) when they size themselves). One component per file, options structs over long parameter lists.
  • Interactive components (FilterList, Task, CredentialStep) are value models: Update returns the new value plus a tea.Cmd, matching Bubble Tea idiom.
  • Colors are package-level adaptive variables (BrandColor, SuccessColor, WarnColor, ErrorColor, TextColor, MutedColor, BorderColor, …) plus the semantic Variant enum with VariantColor(v). There is deliberately no theme abstraction.
  • Overlays (in the kits) close by returning nil from Update and deliver their outcome with app.Emit(ResultMsg); the hosting content handles the result message.

The renders below are real component output (ANSI colors stripped).


Lists & selection

FilterList — type-to-filter windowed list

The shared mechanics behind every picker: filter input, cursor, windowing. Enter/esc stay with the caller, which reads the choice via Selected().

list := tui.NewFilterList(tui.FilterListOptions{
    Items: []tui.FilterItem{
        {Label: "Main Storefront", Detail: "https://shop.example"},
        {Label: "EU Outlet", Detail: "https://eu.example"},
    },
    Header: "Sales channel",
})
list, cmd = list.Update(msg)      // up/down + filter typing
item, index, ok := list.Selected()
view := list.View(44)
> Type to filter

Sales channel
Main Storefront         https://shop.example   ← selected row is highlighted
EU Outlet                 https://eu.example
US Outlet                 https://us.example
RenderSelectList — static cursor list

For short fixed choice lists inside wizard steps (no filtering).

tui.RenderSelectList("Default Language", "Select the primary language", opts, cursor)
Default Language
Select the primary language

● English (UK) (en-GB)
  Deutsch (de-DE)
FilterSelect / FilterMultiSelect — standalone pickers

Self-contained tea.Programs for CLI commands outside a hosted TUI (single or multi select with checkboxes). Inside a hosted TUI use the picker kit instead.

value, err := tui.FilterSelect(ctx, "Select a shop", "Pick the target shop", items)
values, err := tui.FilterMultiSelect(ctx, "Select extensions", "", items)

Status & progress

StateDot / CheckRow — semantic status bullets
tui.StateDot(tui.DotOK)
tui.NewCheckRow(tui.CheckRowOptions{
    State: tui.DotOK, Label: "Git working tree clean", Value: "yes", LabelWidth: 30,
}).Render()
○ pending   ◐ running   ● ok   ● warn   ● error

● Git working tree clean        yes
● PHP version                   8.1.2
StepList — step checklists
tui.NewStepList(tui.StepListOptions{Steps: []tui.StepItem{
    {State: tui.StepStateDone, Label: "Rewrite composer.json"},
    {State: tui.StepStateActive, Label: "composer update", Indicator: spinner.View()},
    {State: tui.StepStatePending, Label: "Install recipes"},
}}).Render()
✓ Rewrite composer.json
⠋ composer update
· Install recipes
StatusStrip — one-line status row
tui.NewStatusStrip(tui.StatusStripOptions{
    Variant: tui.VariantError, Label: "BLOCKED",
    Message: "Fix the failing checks below, then press Recheck.",
}).Render()
BLOCKED   Fix the failing checks below, then press Recheck.
Badges
tui.TextBadge("Step 1/3")                      //  Step 1/3   (inverted block)
tui.StatusBadge("running", tui.SuccessColor)   // ● RUNNING
Command-output status lines (non-TUI)

For plain fmt.Println command output:

fmt.Println(tui.SectionHeadingStyle.Render("Project"))
fmt.Printf("%s PHP version: 8.3\n", tui.CheckOK)
fmt.Println(tui.SuccessLine("Development environment started in 2.4s"))
Project            (bold, underlined)
✓ PHP version: 8.3
⚠ Node missing
✓ Development environment started in 2.4s
✗ Development environment is down
NewBrandSpinner — the shared spinner
sp := tui.NewBrandSpinner() // spinner.Dot in BrandColor
Task — run a command, stream its output

Embeddable "run job, show spinner + log tail" machinery: channel plumbing, capped scrollback, done state. Route TaskLineMsg, TaskDoneMsg, and spinner.TickMsg through Update.

m.task = tui.NewTask("Building Administration...")
cmd := m.task.Start(func() (*exec.Cmd, error) { return buildCmd(), nil })
// in Update:
m.task, cmd = m.task.Update(msg)
// in View:
title := m.task.StatusTitle()   // "⠋ Building Administration..." while running
lines := m.task.Lines()         // capped scrollback
done, err := m.task.Done(), m.task.Err()
RunSpinnerWithLogs — blocking spinner for plain commands

Runs a single *exec.Cmd behind a spinner with a toggleable live log panel (ctrl+l) and prints the last log lines on failure. For CLI commands, not hosted TUIs.

err := tui.RunSpinnerWithLogs(ctx, "Installing dependencies...", cmd)

Input

Checkbox
tui.Checkbox(checked, focused, "Show password")
[x] Show password    (muted; brand + bold when focused)
ButtonRow / ConfirmButtons

Buttons wrap onto extra rows when MaxWidth is set and space runs out.

tui.NewButtonRow(tui.ButtonRowOptions{Labels: labels, Active: 0, MaxWidth: 40}).Render()
tui.ConfirmButtons("Initialize now", "No, skip", confirmYes)
  Stop containers & quit      Quit, keep running      Cancel
  ^^^^^^^^^^^^^^^^^^^^^^ active button is inverted (brand background)
CredentialStep — username/password/show-password fieldset

Embed in a wizard; HandleKey owns focus navigation, enter flow, and validation — the wizard decides what a submit means. Enter submits from the password field and the checkbox, space toggles the checkbox.

type myWizard struct {
    tui.CredentialStep
    step int
}

creds := tui.NewCredentialStep(tui.CredentialStepOptions{
    Username: "admin", Password: "shopware",
    ValidatePassword: validateAdminPassword,
})

cmd, submitted := w.HandleKey(msg)
if submitted { /* advance to the next step */ }
w.Render(&b)
hint := w.FooterHint("Install") // per-focus shortcut bar
Choose a username
admin

Choose a password  at least 8 characters
********

[ ] Show password
Key helpers
tui.KeyString(msg)                       // lower-cased key string ("ctrl+c" even with Caps Lock)
tui.KeyEnter, tui.KeyEsc, tui.KeyTab, …  // key string constants
tui.MoveCursor(cursor, key, count)       // up/down (k/j) with clamping
tui.ConfirmNav(confirmYes, key)          // ←/h = yes, →/l = no, tab toggles

Layout & chrome

Modal — centered modal box
modal := tui.NewModal(tui.ModalOptions{MaxWidth: 70, AreaWidth: w, AreaHeight: h})
out := modal.Render(content)     // modal.ContentWidth() sizes inner content
     ╭────────────────────────────╮
     │                            │
     │  Are you sure?             │
     │                            │
     │  This cannot be undone.    │
     │                            │
     ╰────────────────────────────╯
TwoColumn — read-only left / user-action right split
tui.NewTwoColumn(tui.TwoColumnOptions{
    Width: 44, LeftWidth: 24, Left: report, Right: actions,
}).Render()
1. Check project readin… │ User action
                         │
● Git clean        yes   │ > ◉ 6.7.4.1  rec…
● Composer         ok    │   ○ 6.6.10.6
tui.NewWizardFrame(tui.WizardFrameOptions{
    Width: 44, Height: 9,
    Title: "Upgrade check", TitleRight: "Target 6.7.4.1",
    Status: statusStrip, Body: body, Footer: shortcuts,
}).Render()
╭──────────────────────────────────────────╮
│ Upgrade check             Target 6.7.4.1 │
├──────────────────────────────────────────┤
│ RUNNING   Checking…                      │
├──────────────────────────────────────────┤
│ body content                             │
├──────────────────────────────────────────┤
│  enter  Continue                         │
╰──────────────────────────────────────────╯
BrandingHeader / PhaseFooter — shared app chrome
tui.BrandingHeader(width)                                  // right-aligned branding line
tui.PhaseFooter(tui.ShortcutBadge("l", "Toggle logs"), "Exit")
 ● Shopware CLI dev · Documentation · GitHub

 l  Toggle logs  │   ctrl+c  Exit

ShortcutBarFit drops trailing entries that don't fit the width.

tui.ShortcutBar(
    tui.Shortcut{Key: "↑/↓", Label: "Choose"},
    tui.Shortcut{Key: "enter", Label: "Confirm"},
    tui.Shortcut{Key: "esc", Label: "Cancel"},
)
 ↑/↓  Choose  │   enter  Confirm  │   esc  Cancel
RenderPhaseCard — centered card with the Shopware mascot

Full-screen phase card (starting/stopping/wizard screens). There is also RenderPhaseCardCowsay(speech, content) with a speech bubble.

tui.RenderPhaseCard(spinner.View() + " Starting Docker containers...")
╭──────────────────────────────────╮
│      (Shopware mascot art)       │
│──────────────────────────────────│
│                                  │
│  ⠋ Starting Docker containers... │
│                                  │
╰──────────────────────────────────╯
Scrollbar — vertical scrollbar column
tui.NewScrollbar(tui.ScrollbarOptions{Total: 30, Visible: 5, Offset: 10, Height: 7}).Render()
↑
┆
█
┆
┆
┆
↓
Table — terminal and JSON command output
table := tui.NewTable(
    tui.TableColumn{Title: "Name", JSONKey: "name"},
    tui.TableColumn{Title: "Compatible", JSONKey: "compatible"},
)
table.AddRow(
    "Example",
    tui.TableCell{Value: true, TerminalText: tui.GreenText.Render("Yes")},
)

format, err := tui.ParseTableFormat(formatFlag)
if err != nil {
    return err
}
if err := table.Write(cmd.OutOrStdout(), format); err != nil {
    return err
}

TableCell keeps the typed JSON value separate from styled or human-readable terminal text. AddRowWithJSON preserves established JSON row contracts that contain more fields than the human-readable table. RenderTable and PrintTable remain available for terminal-only call sites using [][]string.

┌─────────┬────────────┐
│ Name    │ Compatible │
├─────────┼────────────┤
│ Example │ Yes        │
└─────────┴────────────┘
Labels & small helpers
tui.KVRow("Username", "admin")     //   Username              admin
tui.SectionDivider(30)             // ──────────────────────────────
tui.StyledLink(url, label, style)  // OSC-8 terminal hyperlink
tui.FormatLabel("Name", "detail")  // Name (detail)

Text utilities

All ANSI-aware (styled strings measure correctly):

tui.Truncate(s, width)            // truncate with ellipsis
tui.PadRight(s, width)            // pad to a column width
tui.JoinColumns(left, right, gap) // side-by-side blocks
tui.SpreadRow(width, left, right) // left + right anchored to the edges

Log scrollback (shared by every streamed-output screen):

lines = tui.AppendTail(lines, 500, newLine) // capped append
visible := tui.TailLines(lines, height)     // last n lines

Subprocess streaming:

err := tui.StreamCmdOutput(cmd, ch, true)                  // lines → channel, closes on exit
cmd := tui.ReadLineCmd(ch, toMsg, doneMsg)                 // re-arm after every line

The application shell (internal/tui/app)

A minimal Bubble Tea host: it owns the frame (header / main / footer), window title, an overlay stack, size propagation, and key bindings mapped to named commands. Content stays a plain screen.

type wizard struct{ host app.Host }

func (w *wizard) Init() tea.Cmd                             { … }
func (w *wizard) Update(msg tea.Msg) (app.Content, tea.Cmd) { … }
func (w *wizard) View(ctx app.Context) string               { … } // render ctx.Width × ctx.MainHeight

shell := app.New(app.Options{
    Content:         w,
    Header:          func(ctx app.Context) string { return tui.BrandingHeader(ctx.Width) },
    Footer:          func(ctx app.Context) string { return tui.PhaseFooter("", "Exit") },
    WindowTitleFunc: func(app.Context) string { return "my-app" },
})
w.host = shell
shell.Run()

Key points:

  • Overlays capture input while open; non-input messages still reach the Content (background work never stalls behind a modal). Push with host.PushOverlay(o); an overlay closes by returning nil and emitting a result: return nil, app.Emit(myResultMsg{…}).

  • Quit is the re-registerable app.CmdQuit command (default binding ctrl+c). Intercept it to e.g. cancel a running job instead of exiting:

    shell.RegisterCommand(app.Command{ID: app.CmdQuit, Run: func(*app.App) tea.Cmd {
        return m.handleQuit()
    }})
    
  • FullscreenOverlay (default true) makes the top overlay replace the whole view; app.Ptr(false) keeps the chrome and swaps only the main region.

  • SwapContent switches screens (wizard → dashboard) and runs the new content's Init.

  • Sizing: content receives ctx.MainHeight (terminal minus chrome); implement app.Sizeable/app.SizePropagator for automatic propagation.

  • Testing: app.NewHarness(opts, width, height) drives the shell without a TTY — h.Send(msgs…), h.SendCmd(cmd), h.View().


Overlay kits

Opinionated modals built on the shell's overlay contract. All three follow the same shape: New(Options{Key, …}) → push → handle ResultMsg in your Content, matching on Key when several instances exist.

prompt — multi-button confirm
return m, m.host.PushOverlay(prompt.New(prompt.Options{
    ID:      "stop-confirm",
    Title:   "Leaving the workspace",
    Message: "Do you also want to stop the running Docker containers?",
    Danger:  true,
    Choices: []prompt.Choice{
        {ID: "stop", Label: "Stop & quit"},
        {ID: "quit", Label: "Keep running"},
        {ID: "cancel", Label: "Cancel"},
    },
}))

// later:
case prompt.ResultMsg: // msg.Choice is the chosen ID ("" = dismissed with esc)
  ╭──────────────────────────────────────────────────────╮
  │                                                      │
  │  Leaving the workspace                               │
  │  Do you also want to stop the running Docker         │
  │  containers?                                         │
  │                                                      │
  │    Stop & quit      Keep running      Cancel         │
  │                                                      │
  │   ←/→  Select  │   enter  Confirm  │   esc  Cancel   │
  │                                                      │
  ╰──────────────────────────────────────────────────────╯
picker — filterable single-select list
return m, m.host.PushOverlay(picker.New(picker.Options{
    Key:   fieldPHPVersion,
    Title: "PHP Version",
    Items: []picker.Item{{Label: "8.2"}, {Label: "8.3"}, {Label: "8.4"}},
}))

// later:
case picker.ResultMsg: // msg.Value / msg.Index, msg.Cancelled on esc
  ╭──────────────────────────────────────────────────────╮
  │                                                      │
  │  PHP Version                                         │
  │                                                      │
  │  > Type to filter                                    │
  │                                                      │
  │  8.2                                                 │
  │  8.3                                                 │
  │  8.4                                                 │
  │                                                      │
  │   ↑/↓  Choose  │   enter  Confirm  │   esc  Cancel   │
  │                                                      │
  ╰──────────────────────────────────────────────────────╯
textprompt — single-line text input

Secret: true masks the value (password-style echo).

return m, m.host.PushOverlay(textprompt.New(textprompt.Options{
    Key:    fieldBlackfireServerID,
    Title:  "Blackfire Server ID",
    Help:   "Server ID for the Blackfire profiler",
    Value:  current,
    Secret: true,
}))

// later:
case textprompt.ResultMsg: // msg.Value, msg.Cancelled on esc
  ╭──────────────────────────────────────────────────────╮
  │                                                      │
  │  Blackfire Server ID                                 │
  │                                                      │
  │  Server ID for the Blackfire profiler                │
  │                                                      │
  │  > *******                                           │
  │                                                      │
  │   enter  Confirm  │   esc  Cancel                    │
  │                                                      │
  ╰──────────────────────────────────────────────────────╯

Consumers

  • internal/devtui — the project dev dashboard (tabs, watchers, install & migration wizards) hosted on tui/app.
  • internal/upgradetui — the project upgrade wizard, hosted on tui/app.
  • cmd/** — plain command output uses PrintTable, the status-line helpers, FilterSelect, and RunSpinnerWithLogs.

When adding a component: one file per component, New(Options) constructor, behavior tests next to it, and update this README with a snippet and render.

Documentation

Index

Constants

View Source
const (
	Yes = "yes"
	No  = "no"
)
View Source
const (
	KeyCtrlC    = "ctrl+c"
	KeyEnter    = "enter"
	KeyEsc      = "esc"
	KeyUp       = "up"
	KeyDown     = "down"
	KeyLeft     = "left"
	KeyRight    = "right"
	KeyTab      = "tab"
	KeyShiftTab = "shift+tab"
)

String forms of the special keys as produced by KeyString. Printable keys ("q", "j", "1", …) are matched with plain literals instead.

View Source
const KVKeyWidth = 22

KVKeyWidth is the column width of the key in KVRow. Callers laying out multi-line values indent their continuation lines by it plus KVRowIndent.

View Source
const KVRowIndent = 2

KVRowIndent is the left indentation KVRow puts in front of the key.

View Source
const PhaseCardWidth = 79
View Source
const StreamBufferSize = 50

StreamBufferSize is the channel buffer used for streaming subprocess output lines into a Bubble Tea program.

Variables

View Source
var (
	// LabelStyle renders text in the primary text color.
	LabelStyle = lipgloss.NewStyle().Foreground(TextColor)

	// LinkStyle renders clickable hyperlinks in a muted blue with an underline.
	LinkStyle = lipgloss.NewStyle().Foreground(LinkColor).Underline(true)

	// TitleStyle renders section headings in bold with the primary text color.
	TitleStyle = lipgloss.NewStyle().Bold(true).Foreground(TextColor)
)
View Source
var (
	CheckOK   = GreenText.Render("✓")
	CheckWarn = SecondaryText.Render("⚠")
	CheckFail = RedText.Render("✗")
)

Status glyphs for composing command output lines, e.g. fmt.Printf("%s Project config: %s\n", tui.CheckOK, value).

View Source
var (
	// BrandColor is the Shopware brand blue used for focus, selection
	// markers, and primary buttons.
	BrandColor   = lipgloss.Color("#076FFF")
	SuccessColor = lipgloss.Color("#04B575")
	MutedColor   = adaptiveMuted()
	ErrorColor   = lipgloss.Color("#FF4D4D")

	// TextColor is the primary foreground for labels, headings, and prominent content.
	TextColor = adaptiveColor("#FFFFFF", "#1A1A1A")

	// OnBrandColor is the foreground for text placed on top of BrandColor.
	// BrandColor is a fixed blue regardless of terminal theme, so this stays
	// light in both modes to keep contrast readable.
	OnBrandColor = lipgloss.Color("#FFFDF5")

	// BorderColor is used for box borders, dividers, and separator lines.
	BorderColor = adaptiveColor("#303030", "#BCBCBC")

	// SubtleBgColor is used for inactive tab backgrounds, badges, and other subtle background fills.
	SubtleBgColor = adaptiveColor("#444444", "#D0D0D0")

	// LinkColor is used for clickable hyperlinks.
	LinkColor = adaptiveColor("#5F87FF", "#0550AE")

	// WarnColor is used for warning-level indicators.
	WarnColor = adaptiveColor("#FFAA00", "#B35800")

	// SelectedBgColor is used for highlighted/selected rows in lists and tables.
	SelectedBgColor = adaptiveColor("#303030", "#E4E4E4")

	Checkmark = lipgloss.NewStyle().Foreground(SuccessColor).Bold(true).Render("✓")

	DimStyle  = lipgloss.NewStyle().Foreground(MutedColor)
	BoldStyle = lipgloss.NewStyle().Bold(true)

	SectionTitleStyle = lipgloss.NewStyle().
						Bold(true).
						Foreground(BrandColor)
)

Semantic colors used across shopware-cli TUI components, adapted to the terminal's light or dark background at startup.

View Source
var ActiveButtonStyle = lipgloss.NewStyle().
	Foreground(OnBrandColor).
	Background(BrandColor).
	Bold(true).
	Padding(0, 2)

ActiveButtonStyle renders a focused/primary button.

View Source
var AppVersion = "dev"

AppVersion is the CLI version displayed in headers and branding lines. It is set from cmd/root.go at startup.

View Source
var BlueColor = compat.AdaptiveColor{
	Light: lipgloss.Color("#0063B8"),
	Dark:  lipgloss.Color("#189EFF"),
}
View Source
var BlueText = lipgloss.NewStyle().Foreground(BlueColor)
View Source
var BoldText = lipgloss.NewStyle().Bold(true)
View Source
var DimText = lipgloss.NewStyle().Faint(true)
View Source
var ErrFilterSelectCancelled = errors.New("selection cancelled")

ErrFilterSelectCancelled is returned by FilterSelect when the user dismisses the prompt with Esc or Ctrl+C.

View Source
var GreenText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#047857"),
	Dark:  lipgloss.Color("#04B575"),
})
View Source
var NeutralText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#1F2937"),
	Dark:  lipgloss.Color("#FFFFFF"),
})
View Source
var RecommendedText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#047857"),
	Dark:  lipgloss.Color("#04B575"),
}).Bold(true)
View Source
var RedText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#DC2626"),
	Dark:  lipgloss.Color("#EF4444"),
})
View Source
var SecondaryText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#B8860B"),
	Dark:  lipgloss.Color("#FFD700"),
}).Bold(true)
View Source
var SectionHeadingStyle = lipgloss.NewStyle().Bold(true).Underline(true)

SectionHeadingStyle renders bold, underlined section headings in command output ("Project", "Next steps", "Summary", …).

View Source
var UpdateAvailable func(context.Context) bool

UpdateAvailable blocks until the background update check finishes and reports whether a newer, installable release exists. It is set from cmd/root.go at startup, like AppVersion; when nil, the header skips the update hint entirely.

View Source
var YellowText = lipgloss.NewStyle().Foreground(compat.AdaptiveColor{
	Light: lipgloss.Color("#B8860B"),
	Dark:  lipgloss.Color("#FFD700"),
})

Functions

func AppendTail

func AppendTail(lines []string, keep int, more ...string) []string

AppendTail appends more to lines, dropping the oldest entries once the buffer exceeds keep — the shared scrollback shape of streamed command output. keep <= 0 keeps everything.

func Checkbox

func Checkbox(checked, focused bool, label string) string

Checkbox renders a "[x]"/"[ ]" toggle with its label — muted by default, brand-highlighted when focused.

func ConfirmButtons

func ConfirmButtons(yesLabel, noLabel string, yesActive bool) string

ConfirmButtons renders a yes/no button pair with the active side highlighted.

func ConfirmNav

func ConfirmNav(confirmYes bool, key string) bool

ConfirmNav applies the shared yes/no confirm-row navigation: left (or h) picks yes, right (or l) picks no, tab toggles. Other keys leave the selection unchanged.

func DividerLine

func DividerLine() string

DividerLine renders a horizontal rule spanning most of the terminal width.

func DrainCmdOutput

func DrainCmdOutput(cmd *exec.Cmd, ch chan<- string, useStdout bool) ([]string, error)

DrainCmdOutput streams cmd into ch without closing the channel, so callers can run several processes as one captured stream.

func FailLine

func FailLine(msg string) string

FailLine renders a bold red "✗ message" line.

func FilterMultiSelect

func FilterMultiSelect(ctx context.Context, title, help string, items []FilterMultiSelectItem) ([]string, error)

FilterMultiSelect renders an interactive picker with a type-to-filter input and a windowed list that allows toggling multiple items. It returns the chosen Values in the original item order, or ErrFilterSelectCancelled if the user dismisses the prompt.

Use it from CLI commands that need to pick several entries from a list that may be small or huge. For single selection use FilterSelect instead.

func FilterSelect

func FilterSelect(ctx context.Context, title, help string, items []FilterSelectItem) (string, error)

FilterSelect renders an interactive picker with a type-to-filter input and a windowed list. It returns the chosen Value, or ErrFilterSelectCancelled if the user dismisses the prompt.

Use it from CLI commands that need to pick from a list that may be small or huge. Inside a hosted TUI, embed FilterList instead — this helper owns its own bubbletea program.

func FormatLabel

func FormatLabel(label, detail string) string

FormatLabel renders a "Label (Detail)" string where the label uses the primary text color and the detail is dimmed in parentheses.

func FormatLabelDim

func FormatLabelDim(label, detail string) string

FormatLabelDim renders a "Label (Detail)" string entirely in dimmed style.

func Hyperlink(url, label string) string

Hyperlink returns an OSC 8 hyperlink sequence wrapping label with the given URL.

func JoinColumns

func JoinColumns(left, right string, gap int) string

JoinColumns glues two multi-line strings side by side with a gap, top-aligned; the left block is padded to its widest line.

func KVRow

func KVRow(key, value string) string

KVRow renders a single key-value pair as a line with consistent alignment.

func KeyString

func KeyString(msg tea.KeyPressMsg) string

KeyString returns the string form of a key press, lower-cased. With Caps Lock on, the terminal sends the shortcut's shifted form instead of the plain one - e.g. pressing ctrl+c sends "ctrl+C" and ctrl+p sends "ctrl+P" - which doesn't match lowercase key constants ("ctrl+c", "ctrl+p", ...), so the shortcut silently does nothing. Lower-casing here makes the match work either way.

func MascotCowsay

func MascotCowsay(targetWidth int, text string) string

MascotCowsay renders the Shopware mascot with a speech bubble above it containing the given text. The bubble is centered above the mascot and sized to fit text (wrapping long lines to targetWidth-4).

func MascotStandard

func MascotStandard(targetWidth int) string

MascotStandard renders the Shopware mascot, horizontally centered within targetWidth columns using dot padding.

func MoveCursor

func MoveCursor(cursor int, key string, count int) int

MoveCursor applies an up/down (or k/j) navigation key to a cursor over a list of length count, clamping to [0, count-1]. Keys other than the four navigation keys leave the cursor unchanged.

func NewBrandSpinner

func NewBrandSpinner() spinner.Model

NewBrandSpinner returns the shared brand-colored dot spinner used by the shopware-cli TUIs.

func NewUpdateCheckCmd

func NewUpdateCheckCmd(wait func(context.Context) bool) tea.Cmd

NewUpdateCheckCmd waits for an externally-owned update result and converts it into a message understood by Header.Update.

func NewYesNo

func NewYesNo() *huh.Select[string]

NewYesNo returns a huh Select field with Yes/No options. The value pointer should be initialized to tui.Yes or tui.No to set the default.

func PadRight

func PadRight(s string, width int) string

PadRight pads s with spaces to the given number of terminal columns. Longer strings are returned with a single trailing space instead of truncating — use Truncate first when a hard column limit is needed.

func PanelWidth

func PanelWidth() int

PanelWidth returns the terminal width capped at maxPanelWidth.

func PhaseFooter

func PhaseFooter(hint, exitLabel string) string

PhaseFooter renders the footer row shared by the project dev phase screens and the upgrade wizard: an optional shortcut hint followed by an exit badge (usually "Exit"; "Cancel" while a job is running).

func PrintBanner

func PrintBanner()

func PrintTable

func PrintTable(headers []string, rows [][]string)

PrintTable prints a bordered table to stdout — the shared shape of the CLI's list outputs.

func ReadLineCmd

func ReadLineCmd(ch <-chan string, onLine func(string) tea.Msg, onDone tea.Msg) tea.Cmd

ReadLineCmd returns a tea.Cmd that reads the next line from ch and converts it into a message via onLine, or returns onDone once the channel is closed. Re-issue the command after each received line to keep the stream flowing.

func RenderCancelledMessage

func RenderCancelledMessage() string

RenderCancelledMessage renders a dimmed cancellation notice.

func RenderPanel

func RenderPanel(title, content string, titleColor color.Color) string

RenderPanel renders content inside a full-width bordered box with an optional title.

func RenderPhaseCard

func RenderPhaseCard(content string) string

RenderPhaseCard renders content inside a fixed-width card with the Shopware mascot art at the top, a divider, and the content below.

func RenderPhaseCardCowsay

func RenderPhaseCardCowsay(speech, content string) string

RenderPhaseCardCowsay renders content inside a fixed-width card with the Shopware mascot saying the given text via a speech bubble.

func RenderSelectList

func RenderSelectList(title, description string, options []SelectOption, cursor int) string

RenderSelectList renders a titled option list with a ● selector on the active item. The whole list is rendered; use RenderSelectListWindowed when the option count can grow large.

func RenderSelectListWindowed

func RenderSelectListWindowed(title, description string, options []SelectOption, cursor, maxVisible int) string

RenderSelectListWindowed renders a select list that shows at most maxVisible options at once, scrolling to keep the cursor in view. A maxVisible of 0 (or any value >= len(options)) renders every option. When the list is windowed a "Showing X–Y of N" line is appended so the box height stays fixed regardless of how many options exist.

func RenderStyledLink(url string) string

RenderStyledLink renders a URL as a clickable terminal hyperlink using LinkStyle.

func RenderSuccessPanel

func RenderSuccessPanel(content string) string

RenderSuccessPanel renders a panel with a green "Project Created" title.

func RenderTable

func RenderTable(headers []string, rows [][]string) string

RenderTable renders a bordered table with a header row. Cells may be pre-styled; every cell gets one column of horizontal padding.

func RunSpinnerWithLogs

func RunSpinnerWithLogs(ctx context.Context, title string, cmd *exec.Cmd) error

RunSpinnerWithLogs executes the given command while displaying a spinner and allowing the user to toggle logs with Ctrl+L.

func SectionDivider

func SectionDivider(width int) string

SectionDivider renders a full-width horizontal line in the border color.

func ShopwareTheme

func ShopwareTheme() huh.Theme

ShopwareTheme returns a huh form theme styled with the Shopware brand colors.

func ShortcutBadge

func ShortcutBadge(key, label string) string

ShortcutBadge renders a single keyboard shortcut as a styled badge.

func ShortcutBar

func ShortcutBar(shortcuts ...Shortcut) string

ShortcutBar joins multiple shortcuts into a horizontal bar separated by dividers.

func ShortcutBarFit

func ShortcutBarFit(maxWidth int, shortcuts ...Shortcut) string

ShortcutBarFit renders a shortcut bar that stays on a single row within maxWidth: it falls back to narrower separators when the default bar is too wide and truncates with an ellipsis as a last resort.

func SpreadRow

func SpreadRow(width int, left, right string) string

SpreadRow places left and right on one row, padding the middle with spaces.

func StateDot

func StateDot(s DotState) string

StateDot renders the semantic status bullet used in front of checklist and queue rows.

func StatusBadge

func StatusBadge(status string, c color.Color) string

StatusBadge renders a status indicator in the form "● STATUS".

func StreamCmdOutput

func StreamCmdOutput(cmd *exec.Cmd, ch chan<- string, useStdout bool) error

StreamCmdOutput starts cmd and streams its output line by line into ch, closing the channel when the command finishes. With useStdout true, stderr is merged into stdout; otherwise stdout is merged into stderr. It blocks until the command exits and returns its error.

func StyledLink(url, label string, style lipgloss.Style) string

StyledLink renders a clickable terminal hyperlink with the given lipgloss style.

func SuccessLine

func SuccessLine(msg string) string

SuccessLine renders a bold green "✓ message" line.

func TailLines

func TailLines(lines []string, n int) []string

TailLines returns the last n lines (all of them when n >= len). n <= 0 returns nil.

func TailWrappedLines

func TailWrappedLines(lines []string, maxRows, width int) []string

TailWrappedLines returns the trailing lines that fit into maxRows once wrapping at width is taken into account. Long command output wraps onto several rows, so tailing by slice element alone overflows a fixed-height panel and clips whatever sits at the end. The last line is always included, even when it alone exceeds maxRows.

func TerminalWidth

func TerminalWidth() int

TerminalWidth returns the current terminal width, falling back to maxPanelWidth if it cannot be determined.

func TextBadge

func TextBadge(text string) string

TextBadge renders text on a subtle background with horizontal padding.

func Truncate

func Truncate(s string, width int) string

Truncate shortens s to at most width terminal columns, appending an ellipsis when it cuts. ANSI sequences and wide runes are handled correctly.

func VariantColor

func VariantColor(v Variant) color.Color

VariantColor maps a semantic variant to its color.

Types

type ButtonRow

type ButtonRow struct {
	// contains filtered or unexported fields
}

ButtonRow is a horizontal row of footer buttons with one highlighted.

func NewButtonRow

func NewButtonRow(opts ButtonRowOptions) ButtonRow

NewButtonRow creates a button row.

func (ButtonRow) Render

func (b ButtonRow) Render() string

Render implements the component contract.

type ButtonRowOptions

type ButtonRowOptions struct {
	Labels []string
	// Active is the highlighted button index; -1 highlights none.
	Active int
	// MaxWidth wraps buttons onto additional lines when a row would exceed
	// it — for narrow containers such as a panel's user-action column.
	// <= 0 keeps everything on one row.
	MaxWidth int
}

ButtonRowOptions configure a ButtonRow.

type CheckRow

type CheckRow struct {
	// contains filtered or unexported fields
}

CheckRow is one line of a checklist: a status dot, a padded label, and a value column, e.g. "● Git working tree clean yes".

func NewCheckRow

func NewCheckRow(opts CheckRowOptions) CheckRow

NewCheckRow creates a check row.

func (CheckRow) Render

func (c CheckRow) Render() string

Render implements the component contract.

type CheckRowOptions

type CheckRowOptions struct {
	// State drives the leading status dot.
	State DotState
	// Label is the check description; Value the short right column ("yes",
	// "14", …). Value may be pre-styled by the caller.
	Label string
	Value string
	// LabelWidth aligns the value column across rows.
	LabelWidth int
}

CheckRowOptions configure a CheckRow.

type CredFocus

type CredFocus int

CredFocus identifies which element of a credential step (username field, password field, or the show-password checkbox) currently has focus.

const (
	CredFocusUsername CredFocus = iota
	CredFocusPassword
	CredFocusShowPassword
)

type CredentialStep

type CredentialStep struct {
	// contains filtered or unexported fields
}

CredentialStep is a username/password/show-password fieldset for wizard steps. Embed it in a wizard and route the step's keys through HandleKey; the wizard decides what a submit means.

func NewCredentialStep

func NewCredentialStep(opts CredentialStepOptions) CredentialStep

NewCredentialStep builds the username/password inputs. The password starts masked.

func (*CredentialStep) Blur

func (c *CredentialStep) Blur()

Blur removes focus from both inputs, used when leaving the step.

func (*CredentialStep) Focus

func (c *CredentialStep) Focus(target CredFocus) tea.Cmd

Focus moves focus to the given element, clamping to the valid range and syncing the text input focus. Returns the blink command for the newly focused input (nil for the checkbox).

func (CredentialStep) FocusTarget

func (c CredentialStep) FocusTarget() CredFocus

FocusTarget reports which element currently has focus.

func (CredentialStep) FooterHint

func (c CredentialStep) FooterHint(submitLabel string) string

FooterHint returns the shortcut bar for the step: navigation, the space toggle while the checkbox has focus, and enter for submitLabel on every focus.

func (*CredentialStep) HandleKey

func (c *CredentialStep) HandleKey(msg tea.KeyPressMsg) (cmd tea.Cmd, submitted bool)

HandleKey applies the shared credential-step key handling: tab/down and shift+tab/up move focus, enter advances from the username or submits from the password and the checkbox (after validation), space toggles the checkbox, and any other key types into the focused input. It reports submitted=true when the password passed validation and the step is complete — what a submit means is the embedding wizard's decision.

func (*CredentialStep) HandlePaste

func (c *CredentialStep) HandlePaste(msg tea.PasteMsg) tea.Cmd

HandlePaste routes terminal (bracketed) paste into the focused input. Embedding wizards must call this for tea.PasteMsg — paste does not arrive as key presses, so HandleKey never sees it.

func (CredentialStep) Password

func (c CredentialStep) Password() string

Password returns the current password value.

func (CredentialStep) PasswordErr

func (c CredentialStep) PasswordErr() string

PasswordErr returns the current validation message, empty when valid.

func (CredentialStep) PasswordMasked

func (c CredentialStep) PasswordMasked() bool

PasswordMasked reports whether the password renders masked.

func (CredentialStep) Render

func (c CredentialStep) Render(b *strings.Builder)

Render writes the username/password/show-password block into b. The caller is responsible for the surrounding step heading and footer.

func (*CredentialStep) SetPassword

func (c *CredentialStep) SetPassword(v string)

SetPassword replaces the password value.

func (*CredentialStep) SetUsername

func (c *CredentialStep) SetUsername(v string)

SetUsername replaces the username value.

func (*CredentialStep) ToggleShowPassword

func (c *CredentialStep) ToggleShowPassword()

ToggleShowPassword flips the password between masked and plain text.

func (CredentialStep) Username

func (c CredentialStep) Username() string

Username returns the current username value.

func (*CredentialStep) ValidatePassword

func (c *CredentialStep) ValidatePassword() bool

ValidatePassword runs the configured password check, storing the message in PasswordErr on failure. Returns true when the password is acceptable.

type CredentialStepOptions

type CredentialStepOptions struct {
	// Username and Password pre-fill the inputs.
	Username string
	Password string
	// Placeholders shown when the inputs are empty.
	UsernamePlaceholder string
	PasswordPlaceholder string
	// Prompts rendered in front of the inputs (default none).
	UsernamePrompt string
	PasswordPrompt string
	// Labels rendered above the inputs and the optional hint beside the
	// password label.
	UsernameLabel string
	PasswordLabel string
	PasswordHint  string
	// CharLimit bounds both inputs (default 64).
	CharLimit int
	// ValidatePassword rejects a password on submit; nil accepts anything.
	// The error text is rendered under the password field.
	ValidatePassword func(password string) error
}

CredentialStepOptions configure NewCredentialStep.

type DotState

type DotState int

DotState is the lifecycle state a StateDot visualizes.

const (
	// DotPending renders a dim "○" for steps that have not started.
	DotPending DotState = iota
	// DotRunning renders a warning-colored "◐" for steps in progress.
	DotRunning
	// DotOK renders a green "●".
	DotOK
	// DotWarn renders a yellow "●".
	DotWarn
	// DotError renders a red "●".
	DotError
)

type FilterItem

type FilterItem struct {
	// Label is the row text; Detail is dimmed, right-aligned extra text.
	// Both are matched by the filter.
	Label  string
	Detail string
	// Value is an optional payload identifier for the caller.
	Value string
}

FilterItem is one selectable entry of a FilterList.

type FilterList

type FilterList struct {
	// contains filtered or unexported fields
}

FilterList is a type-to-filter list with a cursor and a windowed view — the shared mechanics behind pickers and palettes. It handles up/down movement and filter input; enter/esc handling stays with the caller, which reads the choice via Selected.

func NewFilterList

func NewFilterList(opts FilterListOptions) FilterList

NewFilterList creates a filter list.

func (FilterList) Init

func (l FilterList) Init() tea.Cmd

Init returns the cursor blink command for the filter input.

func (FilterList) Items

func (l FilterList) Items() []FilterItem

Items returns all items the list was created with.

func (FilterList) Len

func (l FilterList) Len() int

Len returns the number of items matching the current filter.

func (FilterList) Selected

func (l FilterList) Selected() (FilterItem, int, bool)

Selected returns the item under the cursor and its index in Items.

func (FilterList) Update

func (l FilterList) Update(msg tea.Msg) (FilterList, tea.Cmd)

Update handles cursor movement and filter typing. Enter and esc are not consumed — the caller decides what selection and dismissal mean.

func (FilterList) View

func (l FilterList) View(width int) string

View renders the filter input, the visible window of rows, and a "Showing x of y" line when the list overflows the window.

type FilterListOptions

type FilterListOptions struct {
	Items []FilterItem
	// PageSize is the number of visible rows (default 10).
	PageSize int
	// Placeholder for the filter input.
	Placeholder string
	// InitialIndex preselects an item by its index in Items.
	InitialIndex int
	// Header is an optional column-header line rendered above the rows.
	Header string
}

FilterListOptions configure a FilterList.

type FilterMultiSelectItem

type FilterMultiSelectItem struct {
	Label  string
	Detail string
	Value  string
}

FilterMultiSelectItem is a single row presented by FilterMultiSelect. Label is the primary text, Detail is an optional secondary line (e.g. a path). Both are matched against the filter query. Value is the string returned when the user selects this item.

type FilterSelectItem

type FilterSelectItem struct {
	Label  string
	Detail string
	Value  string
}

FilterSelectItem is a single row presented by FilterSelect. Label is the primary text, Detail is an optional secondary line (e.g. a URL). Both are matched against the filter query. Value is the string returned when the user confirms this item.

type Header struct {
	// contains filtered or unexported fields
}

Header is the branding header row the dev dashboard, the upgrade wizard, and the plugin-migrate wizard render as shell chrome. It follows the Bubble Tea component shape (Init/Update/View) so message-driven header state can be added here without rewiring the hosts.

func NewHeader

func NewHeader() Header

NewHeader creates the shared branding header.

func (Header) Init

func (h Header) Init() tea.Cmd

Init starts the update-hint wait; every host that renders the header gets the hint without extra wiring. Returns nil when no check is configured.

func (Header) Update

func (h Header) Update(msg tea.Msg) (Header, tea.Cmd)

Update applies an update result received by the parent TUI model.

func (Header) View

func (h Header) View(width int) string

View renders the header row: the branding line right-aligned within width.

type LineWriter

type LineWriter struct {
	// contains filtered or unexported fields
}

LineWriter converts a byte stream into per-line emit calls — the io.Writer side of streaming subprocess output.

func NewLineWriter

func NewLineWriter(emit func(string)) *LineWriter

NewLineWriter creates a line writer that calls emit for every full line.

func (*LineWriter) Flush

func (w *LineWriter) Flush()

Flush emits any trailing output that did not end in a newline.

func (*LineWriter) Write

func (w *LineWriter) Write(p []byte) (int, error)

Write implements io.Writer.

type Modal struct {
	// contains filtered or unexported fields
}

Modal is a bordered box centered within an area — the container used by overlay dialogs (pickers, detail popups, confirmations).

func NewModal

func NewModal(opts ModalOptions) Modal

NewModal creates a modal container.

func (Modal) ContentWidth

func (m Modal) ContentWidth() int

ContentWidth returns the columns available to content inside the box (border and horizontal padding subtracted). Build content against this width before calling Render.

func (Modal) Render

func (m Modal) Render(content string) string

Render draws the content inside the centered, bordered box.

func (Modal) Width

func (m Modal) Width() int

Width returns the modal box width.

type ModalOptions

type ModalOptions struct {
	// MaxWidth caps the modal box width; the box also never exceeds the area
	// width minus a small margin.
	MaxWidth int
	// AreaWidth and AreaHeight define the region the modal centers within,
	// usually the full terminal or the host's main region.
	AreaWidth  int
	AreaHeight int
}

ModalOptions configure a Modal.

type Scrollbar

type Scrollbar struct {
	// contains filtered or unexported fields
}

Scrollbar is a vertical scrollbar column: arrows at both ends, a dashed track, and a solid thumb. It renders empty when everything fits.

func NewScrollbar

func NewScrollbar(opts ScrollbarOptions) Scrollbar

NewScrollbar creates a scrollbar.

func (Scrollbar) Render

func (s Scrollbar) Render() string

Render implements the component contract.

type ScrollbarOptions

type ScrollbarOptions struct {
	// Total is the item count; Visible how many fit; Offset the first shown.
	Total   int
	Visible int
	Offset  int
	// Height is the rendered height in rows (arrows included).
	Height int
}

ScrollbarOptions configure a Scrollbar.

type SelectList

type SelectList struct {
	// contains filtered or unexported fields
}

SelectList is a stateful, keyboard-navigable single-select list. It owns the cursor and windowing so callers only forward key strings and render. Use it for any fixed-height option picker (e.g. the project upgrade version select).

func NewSelectList

func NewSelectList(title, description string, options []SelectOption, maxVisible int) *SelectList

NewSelectList builds a SelectList. maxVisible caps how many options are shown at once (0 = show all); the window scrolls to keep the cursor visible.

func (*SelectList) Cursor

func (l *SelectList) Cursor() int

Cursor returns the index of the currently highlighted option.

func (*SelectList) HandleKey

func (l *SelectList) HandleKey(key string) bool

HandleKey applies a navigation key (up/down/k/j, pgup/pgdown, home/g, end/G) and reports whether it moved or consumed the cursor. Keys the list does not own (enter, esc, …) return false so the caller can act on them.

func (*SelectList) Selected

func (l *SelectList) Selected() (SelectOption, bool)

Selected returns the currently highlighted option and whether one exists (false when the list is empty).

func (*SelectList) Shortcuts

func (l *SelectList) Shortcuts() []Shortcut

Shortcuts returns the navigation hints to show in a footer. PgUp/PgDn is only included when the list is windowed.

func (*SelectList) View

func (l *SelectList) View() string

View renders the list at its current cursor position.

type SelectOption

type SelectOption struct {
	Label  string
	Detail string
}

SelectOption describes a single option in a select list.

type Shortcut

type Shortcut struct {
	Key   string
	Label string
}

Shortcut describes a single keyboard shortcut to display in a footer bar.

type Shortcuts

type Shortcuts struct {
	// contains filtered or unexported fields
}

Shortcuts is a horizontal bar of keyboard shortcut badges.

func NewShortcuts

func NewShortcuts(opts ShortcutsOptions) Shortcuts

NewShortcuts creates a shortcut bar.

func (Shortcuts) Render

func (s Shortcuts) Render() string

Render implements the component contract.

type ShortcutsOptions

type ShortcutsOptions struct {
	Items []Shortcut
	// MaxWidth keeps the bar on a single row within this width: narrower
	// separators first, ellipsis truncation as a last resort. <= 0 means
	// unconstrained.
	MaxWidth int
}

ShortcutsOptions configure a Shortcuts bar.

type StatusStrip

type StatusStrip struct {
	// contains filtered or unexported fields
}

StatusStrip is a one-line status row: a bold colored label followed by a plain message, e.g. "BLOCKED 3 extensions need fixes …".

func NewStatusStrip

func NewStatusStrip(opts StatusStripOptions) StatusStrip

NewStatusStrip creates a status strip.

func (StatusStrip) Render

func (s StatusStrip) Render() string

Render implements the component contract.

type StatusStripOptions

type StatusStripOptions struct {
	// Variant selects the semantic color of the label.
	Variant Variant
	// Label is the bold colored badge text, e.g. "BLOCKED" or "READY".
	Label string
	// Message is the plain explanation after the label.
	Message string
}

StatusStripOptions configure a StatusStrip.

type StepItem

type StepItem struct {
	Label string
	State StepState
	// Indicator overrides the state indicator, e.g. a spinner frame for the
	// active step.
	Indicator string
}

StepItem is one row in a StepList.

type StepList

type StepList struct {
	// contains filtered or unexported fields
}

StepList renders a vertical checklist of steps with per-state indicators.

func NewStepList

func NewStepList(opts StepListOptions) StepList

NewStepList creates a step list.

func (StepList) Render

func (l StepList) Render() string

Render implements the component contract.

type StepListOptions

type StepListOptions struct {
	Steps []StepItem
}

StepListOptions configure a StepList.

type StepState

type StepState int

StepState is the display state of one StepItem.

const (
	StepStatePending StepState = iota
	StepStateActive
	StepStateDone
)

type Table

type Table struct {
	// contains filtered or unexported fields
}

Table contains one output-neutral table definition.

func NewTable

func NewTable(columns ...TableColumn) *Table

NewTable creates a table with the given columns.

func (*Table) AddRow

func (t *Table) AddRow(values ...any)

AddRow appends a row. Row width is validated when the table is rendered.

func (*Table) AddRowWithJSON

func (t *Table) AddRowWithJSON(jsonValue any, values ...any)

AddRowWithJSON appends a row with a custom JSON representation. Prefer AddRow when JSON should contain exactly the configured columns. This method supports established JSON contracts that contain more data than the corresponding human-readable table.

func (*Table) Render

func (t *Table) Render(format TableFormat) (string, error)

Render renders the table in the requested format.

func (*Table) Write

func (t *Table) Write(w io.Writer, format TableFormat) error

Write renders the table and writes it with a trailing newline.

type TableCell

type TableCell struct {
	Value        any
	TerminalText string
}

TableCell holds a raw value for JSON output and, optionally, a separate representation for terminal output. TerminalText is useful for colors, human-readable booleans, and other presentation that should not leak into machine-readable output.

type TableColumn

type TableColumn struct {
	Title   string
	JSONKey string
}

TableColumn configures one terminal table column and its corresponding JSON object key.

type TableFormat

type TableFormat string

TableFormat is an output format supported by Table.

const (
	TableFormatTable TableFormat = "table"
	TableFormatJSON  TableFormat = "json"
)

func ParseTableFormat

func ParseTableFormat(value string) (TableFormat, error)

ParseTableFormat validates a table output format.

type Task

type Task struct {
	Title string
	// contains filtered or unexported fields
}

Task runs one command in the background and accumulates its streamed output — the shared "run job, show spinner + log tail" machinery behind task screens. Embed it in a model, call Start, and route TaskLineMsg, TaskDoneMsg, and spinner.TickMsg through Update.

func NewTask

func NewTask(title string) Task

NewTask creates an idle task.

func (Task) Done

func (t Task) Done() bool

Done reports whether the command has finished.

func (Task) Err

func (t Task) Err() error

Err returns the command's error, nil while running or on success.

func (Task) Lines

func (t Task) Lines() []string

Lines returns the accumulated output scrollback.

func (*Task) Start

func (t *Task) Start(factory func() (*exec.Cmd, error)) tea.Cmd

Start launches the command produced by factory and begins streaming its combined output. The returned command batch keeps the stream flowing and the spinner ticking; completion arrives as a TaskDoneMsg.

func (Task) StatusTitle

func (t Task) StatusTitle() string

StatusTitle returns the task title, prefixed with the spinner while the command is still running.

func (Task) Update

func (t Task) Update(msg tea.Msg) (Task, tea.Cmd)

Update handles the task's stream, completion, and spinner messages.

type TaskDoneMsg

type TaskDoneMsg struct{ Err error }

TaskDoneMsg is delivered when a task's command has finished and its output has been fully drained.

type TaskLineMsg

type TaskLineMsg struct{ Line string }

TaskLineMsg carries one line of a running task's output.

type TwoColumn

type TwoColumn struct {
	// contains filtered or unexported fields
}

TwoColumn renders two blocks side by side separated by a vertical divider, top-aligned and padded to equal height. Wizard panels use it for the read-only left / user-action right split.

func NewTwoColumn

func NewTwoColumn(opts TwoColumnOptions) TwoColumn

NewTwoColumn creates a two-column layout.

func (TwoColumn) Render

func (c TwoColumn) Render() string

Render implements the component contract.

type TwoColumnOptions

type TwoColumnOptions struct {
	// Width is the total width; LeftWidth the columns left of the divider.
	// The divider consumes three columns (" │ ").
	Width     int
	LeftWidth int
	Left      string
	Right     string
}

TwoColumnOptions configure a TwoColumn layout.

type UpdateAvailableMsg

type UpdateAvailableMsg struct{}

UpdateAvailableMsg is delivered when the asynchronous update check finds a newer CLI version. Hosts forward it to Header.Update like any other message.

type Variant

type Variant int

Variant is a semantic color role used by feedback components such as StatusStrip.

const (
	VariantPrimary Variant = iota
	VariantSuccess
	VariantWarning
	VariantError
	VariantMuted
	// VariantInfo renders informational accents (blue, same as Primary).
	VariantInfo
)

type WizardFrame

type WizardFrame struct {
	// contains filtered or unexported fields
}

WizardFrame is a bordered wizard panel: title row, optional status row, body, and footer, each separated by horizontal rules. It always renders exactly Height rows.

func NewWizardFrame

func NewWizardFrame(opts WizardFrameOptions) WizardFrame

NewWizardFrame creates a wizard frame.

func (WizardFrame) Render

func (f WizardFrame) Render() string

Render implements the component contract.

type WizardFrameOptions

type WizardFrameOptions struct {
	Width  int
	Height int
	// Title is the panel title in the frame's top row.
	Title string
	// TitleRight is an optional right-aligned label, e.g. "Target 6.7.11.0".
	TitleRight string
	// Status is an optional status row (see StatusStrip) below the title,
	// separated by horizontal rules. Multi-line values render as several rows.
	Status string
	// Body fills the frame's main area, padded or truncated to fit.
	Body string
	// Footer renders at the bottom, separated by a horizontal rule. Empty
	// omits the footer section and lets the body use the full height.
	Footer string
}

WizardFrameOptions configure a WizardFrame.

Directories

Path Synopsis
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.
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.
Package picker provides a filterable single-select list overlay for the tui/app shell — a type-to-filter input over a windowed list, rendered as a centered modal.
Package picker provides a filterable single-select list overlay for the tui/app shell — a type-to-filter input over a windowed list, rendered as a centered modal.
Package pluginmigrate implements the interactive `project autofix composer-plugins` wizard: it scans custom/ for extensions Composer does not manage, classifies them against the Shopware Store packagist, and migrates them (Store requires or path repositories) with rollback on failure.
Package pluginmigrate implements the interactive `project autofix composer-plugins` wizard: it scans custom/ for extensions Composer does not manage, classifies them against the Shopware Store packagist, and migrates them (Store requires or path repositories) with rollback on failure.
Package prompt provides a multi-button confirm overlay for the tui/app shell — for questions where a plain yes/no is not enough (e.g.
Package prompt provides a multi-button confirm overlay for the tui/app shell — for questions where a plain yes/no is not enough (e.g.
Package textprompt provides a single-line text-input overlay for the tui/app shell — the third of the overlay kits next to prompt (buttons) and picker (filterable list).
Package textprompt provides a single-line text-input overlay for the tui/app shell — the third of the overlay kits next to prompt (buttons) and picker (filterable list).
Package upgrade implements the interactive Shopware upgrade wizard (`shopware-cli project upgrade`): a full-screen Bubble Tea program that walks through readiness checks, target version selection, extension compatibility, plan review, and the guided upgrade execution.
Package upgrade implements the interactive Shopware upgrade wizard (`shopware-cli project upgrade`): a full-screen Bubble Tea program that walks through readiness checks, target version selection, extension compatibility, plan review, and the guided upgrade execution.

Jump to

Keyboard shortcuts

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