ux

package
v0.8.1 Latest Latest
Warning

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

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

Documentation

Overview

Package ux's PhaseRunner is the sequential phase checklist primitive that sits below the cell-open banner (Banner) and above the structured debug info table (KV). CELL-262.

Design separation between the three ux primitives:

  • Banner — header. One row. Identity (cell · project #bunk).
  • KV — structured info table. N rows of key/value pairs, debug-gated.
  • PhaseRunner — sequential step list. N rows of `✓ name [— detail] <elapsed>`.

Each does ONE job; callers compose them top-to-bottom.

Cooked-mode constraint

PhaseRunner intentionally uses the same ProgressSpinner model as everywhere else in this package: a goroutine ticker that writes `\r\033[K` for line-clearing, never a Bubble Tea program. The reason: cell-open hands the TTY off to `docker exec -it ... claude` at the end. Anything that puts the terminal in raw mode or alt screen (i.e. tea.NewProgram) risks leaving claude with a broken stdio if the lifecycle isn't perfectly drained. Cooked mode is the boring, proven shape.

Sequencing

Phases run synchronously, one at a time. If a phase needs to fan out internally that's fine; PhaseRunner itself never renders concurrent rows (cursor-positioning gymnastics break under interleaved stderr writes).

Wire format

The success row format is `✓ <name>[ — <detail>] <elapsed>` — same as ProgressSpinner.Success because PhaseRunner is a thin convenience wrapper around it. CELL-261's FormatSecretsPhase produces strings that drop into the `<detail>` slot verbatim.

Index

Constants

This section is empty.

Variables

View Source
var (
	StyleInfo    = lipgloss.NewStyle().Foreground(colorInfo)
	StyleSuccess = lipgloss.NewStyle().Foreground(colorSuccess)
	StyleError   = lipgloss.NewStyle().Foreground(colorError)
	StyleWarning = lipgloss.NewStyle().Foreground(colorWarning)
	StyleDebug   = lipgloss.NewStyle().Foreground(colorDebug)
	StyleMuted   = lipgloss.NewStyle().Foreground(colorMuted)
	StyleBold    = lipgloss.NewStyle().Bold(true)
	StyleSection = lipgloss.NewStyle().Bold(true)
	StyleAccent  = lipgloss.NewStyle().Foreground(colorAccent)

	// TableBorder is the default style for lipgloss/table borders.
	TableBorder = lipgloss.NewStyle().Foreground(colorBorder)
)

Styles — exported so commands can reuse instead of defining their own.

View Source
var ErrUserAborted = huh.ErrUserAborted

ErrUserAborted is returned when the user presses Esc during a prompt.

View Source
var LogPlainText bool

LogPlainText disables spinners and uses plain logger output when true. Set before using any ux functions (e.g. when not a TTY or in CI).

View Source
var OutputFormat = "text"

OutputFormat controls how PrintTable and PrintData emit output. Values: "text" (default lipgloss table), "json", "yaml".

View Source
var Verbose bool

Verbose enables streaming of build output to stdout instead of suppressing it. Implies LogPlainText. Set by --debug.

Functions

func Banner(cell, project, bunk string) string

Banner renders the cell-open header — a single-line identity strip used at the top of every `cell <command>` invocation:

cell ▸ DIMM · devcell #304

Color: "cell" + the chevron + "#bunk" use the muted palette so the eye lands on the cell name (brand orange + bold) and project (default fg). Two segments are suppressed when they carry no signal:

  • cell name empty (single-pane default `main`)
  • bunk "0" or "" — the no-multiplexer fallback; surfacing "#0" is noise.

func Debugf added in v0.4.0

func Debugf(format string, a ...any)

Debugf prints a formatted debug message when Verbose (--debug) is enabled.

func FormatSecretsPhase added in v0.8.0

func FormatSecretsPhase(count, failed int) string

FormatSecretsPhase renders the post-resolution *detail* attached to the phase's final ✓ row (label is owned by the PhaseRunner — passing a prefixed label here doubles up as "Loaded secrets — Loaded secrets — 7 resolved"). CELL-261.

Elapsed time is NOT included here — the PhaseRunner appends its own elapsed marker, so embedding one would double-stamp the row.

func GetConfirmation

func GetConfirmation(message string) (bool, error)

GetConfirmation shows an interactive confirmation prompt (defaults to true).

func GetMultiSelection added in v0.4.0

func GetMultiSelection(message string, options []string, defaultOptions []string) ([]string, error)

GetMultiSelection shows an interactive multi-select (checkbox) prompt and returns all selected options. defaultOptions are pre-checked. Returns huh.ErrUserAborted if the user presses Esc or Ctrl+C.

func GetSelection added in v0.3.0

func GetSelection(message string, options []string) (string, error)

GetSelection shows an interactive selection prompt and returns the chosen option.

func GetSelectionKV added in v0.4.0

func GetSelectionKV(message string, options []SelectOption) (string, error)

GetSelectionKV shows an interactive selection with separate display labels and values. Returns the Value of the selected option.

func Info added in v0.4.0

func Info(message string)

Info prints an info-styled message.

Lines use \r\n rather than \n because cell-open writes rows AFTER `docker run -it` has put the host TTY into raw mode (ONLCR cleared). In raw mode a bare \n moves the cursor down one row but leaves the column where it was, producing a staircase. \r\n is correct in both raw and cooked modes (cooked mode's ONLCR only translates lone \n; an explicit CR passes through unchanged).

func InteractiveTable added in v0.5.0

func InteractiveTable(
	headers []string,
	rows [][]string,
	sortHandler func(key SortKey) [][]string,
)

InteractiveTable displays headers+rows in an interactive bubbles/table TUI. sortHandler is called when the user presses a sort key (r/s/z/p) and should return the re-sorted rows. Falls back to PrintTable for non-TTY or non-text mode.

func KV added in v0.8.0

func KV(keyWidth int, key, value string) string

KV renders an aligned key-value row for the cell-open detail block. keyWidth is the *key column width* — the value column starts at `keyWidth + 2` (2-space gap). Keys longer than keyWidth still get a minimum 2-space gap so nothing collides. Pure — no global state.

func PrintBuildErrorHint added in v0.5.0

func PrintBuildErrorHint(hint *BuildErrorHint)

PrintBuildErrorHint renders a user-facing error panel for a build failure hint.

func PrintData added in v0.5.0

func PrintData(v any)

PrintData serialises any Go value in the current OutputFormat. Use this when commands build typed structs (e.g. for models output). In text mode it falls back to JSON so the caller always gets parseable output.

func PrintTable added in v0.5.0

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

PrintTable renders headers+rows in the current OutputFormat. text: lipgloss bordered table. json/yaml: array of objects keyed by header.

func Println

func Println(message string)

Println prints a styled line (or plain info when LogPlainText is set).

func SortKeyString added in v0.5.0

func SortKeyString(key SortKey) string

SortKeyString converts a SortKey to the string value passed to RankModels sortBy.

func SuccessMsg added in v0.4.0

func SuccessMsg(message string)

SuccessMsg prints a success-styled message (standalone, not spinner).

func Warn added in v0.4.0

func Warn(message string)

Warn prints a warning-styled message.

Types

type BuildErrorHint added in v0.5.0

type BuildErrorHint struct {
	Title string
	Body  string
	Fixes []string
}

BuildErrorHint describes a user-facing explanation and fix for a known build failure.

func ClassifyBuildOutput added in v0.5.0

func ClassifyBuildOutput(output string) *BuildErrorHint

ClassifyBuildOutput scans docker build output for known error patterns and returns a user-facing hint. Returns nil when no pattern matches.

type PhaseRunner added in v0.8.0

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

PhaseRunner owns the sequential phase list above the resumed parent spinner. Zero-value is usable; no constructor needed.

func (*PhaseRunner) Phase added in v0.8.0

func (p *PhaseRunner) Phase(name string, fn func() error) error

Phase runs fn under a per-phase ProgressSpinner. On nil-error, lands a permanent `✓ <name> <elapsed>` row and returns nil. On error, lands a permanent `✗ <name> — <err>` row and returns the error verbatim so the caller can propagate or recover with errors.Is.

func (*PhaseRunner) PhaseDetailed added in v0.8.0

func (p *PhaseRunner) PhaseDetailed(name string, fn func() (detail string, err error)) error

PhaseDetailed is the variant for phases that compute a permanent-line suffix on success (e.g. "Loading secrets" returning "7 resolved", "Image pin" returning the short SHA). Empty detail string omits the trailing " — " so single-value rows stay clean.

On error, behaves like Phase: lands `✗ <name> — <err>` and returns the error. The detail value is discarded in the error path because the row already carries the error message.

func (*PhaseRunner) PhaseDetailedRunning added in v0.8.0

func (p *PhaseRunner) PhaseDetailedRunning(running, finalName string, fn func() (detail string, err error)) error

PhaseDetailedRunning is like PhaseDetailed but renders a different label while the spinner is active vs the permanent ✓/✗ row. Use for phases whose in-progress text carries a user prompt that no longer applies once the phase completes (e.g. "Loading secrets (please authorize 1Password)" while running, "Loading secrets — 7 resolved" once done).

Identical behavior to PhaseDetailed on the final row: `✓ <finalName>` / `✓ <finalName> — <detail>` / `✗ <finalName> — <err>`.

func (*PhaseRunner) Seal added in v0.8.0

func (p *PhaseRunner) Seal(name string)

Seal lands the final ✓ row before the host→container handoff. Explicit boundary marker — the call site that follows Seal is the docker exec that takes the TTY. Distinct from Phase because there's no work to wrap; the runner just emits the row.

In practice the caller writes:

pr.Seal("Cell ready")
return execClaude(ctx, ...)   // takes the TTY from the row below

type ProgressSpinner

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

ProgressSpinner displays an animated spinner with a message. In plain-text mode it falls back to simple log lines.

func NewProgressSpinner

func NewProgressSpinner(message string) *ProgressSpinner

NewProgressSpinner creates and starts a spinner, or logs the message if in plain-text mode.

func (*ProgressSpinner) Fail

func (ps *ProgressSpinner) Fail(message string) *ProgressSpinner

Fail stops the spinner and prints a failure message.

func (*ProgressSpinner) Stop

func (ps *ProgressSpinner) Stop()

Stop clears the spinner without leaving any output.

func (*ProgressSpinner) Success

func (ps *ProgressSpinner) Success(message string) *ProgressSpinner

Success stops the spinner and prints a success message.

func (*ProgressSpinner) UpdateText

func (ps *ProgressSpinner) UpdateText(message string) *ProgressSpinner

UpdateText updates the spinner text or prints the message.

type SelectOption added in v0.4.0

type SelectOption struct {
	Label string
	Value string
}

SelectOption pairs a display label with a value for typed selection.

type SortKey added in v0.5.0

type SortKey string

SortKey identifies the column being sorted in the interactive table.

const (
	SortRecommended SortKey = "r"
	SortSWE         SortKey = "s"
	SortSpeed       SortKey = "z"
	SortSize        SortKey = "p"
)

Jump to

Keyboard shortcuts

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