Documentation
¶
Overview ¶
Package feedback renders lerd's CLI progress output (animated steps, a live line, summaries, prompts, warnings) and owns the shared lerd colour palette. It degrades to plain text when stdout is piped, redirected, or NO_COLOR is set.
Index ¶
- Constants
- Variables
- func AlreadyShown(err error) bool
- func Amber(s string) string
- func AmberIf(on bool, s string) string
- func Animated() bool
- func Begin()
- func Confirm(question string, defaultYes bool) bool
- func Dim(s string) string
- func Done(msg string)
- func Fail(err error)
- func FailOn(w io.Writer, err error)
- func Green(s string) string
- func GreenIf(on bool, s string) string
- func Header(title string)
- func Interactive() bool
- func Line(msg string)
- func LineOn(w io.Writer, msg string)
- func Note(msg string)
- func Prompt(question string, defaultYes bool)
- func Red(s string) string
- func RedIf(on bool, s string) string
- func RenderTable(headers []string, rows [][]string) string
- func SetAnimated(on bool) func()
- func SetInteractive(on bool) func()
- func SetTestWriter(w io.Writer) func()
- func Success(msg string, d time.Duration)
- func Sudo(msg string)
- func Table(headers []string, rows [][]string)
- func Title(s string) string
- func Val(s string) string
- func Warn(format string, a ...any)
- func WarnOn(w io.Writer, format string, a ...any)
- type Live
- type Step
- type Summary
Constants ¶
const ( GlyphOK = "✓" GlyphFail = "✗" GlyphWarn = "⚠" GlyphLock = "🔒" )
Status glyphs for query/report output that renders its own layout rather than using the Step/Live progress flow.
Variables ¶
var ( ColTitle = lipgloss.Color("#FF2D20") // lerd red (Laravel brand) ColDim = lipgloss.Color("#6b7280") // gray-500 ColDivider = lipgloss.Color("#374151") // gray-700 ColRunning = lipgloss.Color("#10b981") // emerald-500 ColStopped = lipgloss.Color("#6b7280") // gray-500 ColFailing = lipgloss.Color("#ef4444") // red-500 ColPaused = lipgloss.Color("#f59e0b") // amber-500 ColGold = lipgloss.Color("#fbbf24") // amber-400 (brighter gold for sudo prompts) // Interactive accent is the brand red so the TUI, CLI feedback, and the web // UI all share one accent rather than diverging on a separate hue. ColAccent = lipgloss.Color("#FF2D20") // lerd red )
Canonical lerd palette (Tailwind hex values). The TUI theme aliases these so CLI feedback and the dashboard share one set of colours.
Functions ¶
func AlreadyShown ¶
AlreadyShown reports whether err, or any error it wraps, was already displayed to the user by a Fail. Callers in main use it to avoid double-printing.
func Animated ¶
func Animated() bool
Animated reports whether output supports in-place animation (a colour TTY). When false, Live degrades to a header line plus one plain line per item.
func Begin ¶
func Begin()
Begin prints a single blank line to separate a feedback block from whatever (a shell prompt, a wizard) came before it.
func Confirm ¶
Confirm prints a styled yes/no prompt (preceded by a blank line) and reads the answer from stdin, returning defaultYes on an empty response. The prompt matches the step styling: a violet "?" lead-in and a dim "Y/n" hint.
func Done ¶
func Done(msg string)
Done prints a green-check completion line with no timing, for operations where elapsed time isn't meaningful.
func Fail ¶
func Fail(err error)
Fail prints a standalone red ✗ line for err to stderr in the same style (and blank-line spacing) as a Live/Step failure. It is for the top-level command handler to render an error a command returned without surfacing it itself, so every failure reads the same whether it came up through a spinner or bubbled out raw. Errors go to stderr so stdout stays clean for piped data. It records err as shown so nothing reprints it.
func FailOn ¶
FailOn is Fail targeted at an explicit writer, colouring only when that writer is a terminal. Lets the top-level handler send errors to stderr and tests capture them in a buffer.
func Green ¶
Green, Red, Amber, and Dim colour a one-off fragment for status reports, honouring NO_COLOR and non-TTY output. Use these so query commands share the progress palette without reaching for raw ANSI codes.
func GreenIf ¶
GreenIf, RedIf, and AmberIf colour s from the palette only when on is true, else return it plain — for renderers that target an arbitrary writer and gate colour per-writer (e.g. doctor forcing plain text into a bug report). When on is true the usual global NO_COLOR/TTY state is still honoured, so a coloured caller never produces escapes the environment asked to suppress.
func Header ¶
func Header(title string)
Header prints a section header — a brand-red "▸ title" with a blank line above and below — to delimit the major phases of a long multi-step command (install, update) so the step lines beneath read as a group instead of one flat wall of output. Routed through emit so it lands cleanly above any live spinner that is still animating.
func Interactive ¶
func Interactive() bool
Interactive reports whether stdout is a terminal, independent of colour. Use it to choose a condensed single-line view over full verbose output: a user who only disabled colour (NO_COLOR) still gets the interactive view rather than the pipe/MCP verbose path. Animated() is the wrong gate for that, since NO_COLOR forces it false even on a real terminal.
func Line ¶
func Line(msg string)
Line prints a standalone step (arrow prefix, no trailing ellipsis) for an already-known fact, e.g. "php 8.4 · node 22 · nginx vhost written".
func LineOn ¶
LineOn is Line targeted at an explicit writer — for an install step that captures or discards its sub-output (e.g. via io.Discard) rather than writing to the live stdout. Mirrors Line's styling so captured output reads the same.
func Note ¶
func Note(msg string)
Note prints a dim, indented sub-detail under a step (e.g. a log hint). It has no glyph so it reads as secondary to the step lines above it.
func Prompt ¶
Prompt renders a styled yes/no question (violet "?", dim "Y/n" hint), preceded by a blank line, WITHOUT reading the answer — for callers that read from a custom source (e.g. /dev/tty so `curl … | bash` prompts still work) instead of stdin. Mirrors Confirm's styling so every prompt looks the same.
func RenderTable ¶
RenderTable returns the table as a string without printing it, for callers that compose it into other output. On a colour TTY it draws a rounded grid with a bold brand-red header and dim borders; with colour off (NO_COLOR or a pipe) it falls back to plain space-aligned columns so scripted output stays parseable. When the natural grid is wider than the terminal the widest columns are truncated with an ellipsis so every row stays on one line and the grid fits the screen whenever the column count leaves room for it.
func SetAnimated ¶
func SetAnimated(on bool) func()
SetAnimated forces Animated() to on (or off) and returns a restore func. Pair it with SetTestWriter so a test can exercise an animated code path while the spinner frames land in a buffer instead of os.Stdout.
func SetInteractive ¶
func SetInteractive(on bool) func()
SetInteractive overrides Interactive for a test and returns a restore func.
func SetTestWriter ¶
SetTestWriter redirects output to w in plain mode (no colour) and returns a restore func. Intended for tests in this and other packages.
func Sudo ¶
func Sudo(msg string)
Sudo prints a privileged-step line: a gold lock glyph and gold message, so the password prompt the next command (mkcert, a sudo write) raises reads as expected rather than a surprise. Use it in place of a bare "[sudo required] …" print so every privileged step looks the same. Routed through emit so it lands cleanly above any live spinner.
func Table ¶
Table prints headers + rows as a single aligned table in the lerd palette so every `lerd … list`/`search` view shares one look instead of hand-rolled printf columns. See RenderTable for the styling and plain-mode fallback.
Types ¶
type Live ¶
type Live struct {
// contains filtered or unexported fields
}
Live is a single in-place progress line with a trailing spinner that accumulates completed items, e.g. "→ configuring .env… ✓ a · b · c ⠙". When it finishes the spinner is dropped and the ✓ line stays put.
func StartLive ¶
StartLive begins a live line with the given label. On a non-animated output it just prints the header and each Add on its own line.
func (*Live) Done ¶
func (l *Live) Done()
Done stops the spinner, leaving the ✓ line (checkbox replaces the loader).
func (*Live) Interrupt ¶
func (l *Live) Interrupt(fn func())
Interrupt suspends the spinner and clears its line so fn can print standalone output (a booting service, a child process's stdout) above the live line without the spinner's \r redraw clobbering it, then lets the spinner resume and redraw beneath. On non-animated output it just runs fn. paused is read and written under the package mu, the same lock draw holds while writing, so no redraw can slip between the clear and fn's output.
type Step ¶
type Step struct {
// contains filtered or unexported fields
}
Step is one line of progress: Start it before the work, then OK/Info/Fail finishes it. On a colour TTY a spinner animates until then; otherwise nothing prints until the finishing call writes the line once.
func Start ¶
Start records a step with the given present-tense label (e.g. "detecting framework") and begins its spinner on an animated terminal.
func StartOn ¶
StartOn is Start with a fixed render target. The spinner and finishing line write to w rather than the live os.Stdout, so the step can animate while the caller redirects os.Stdout elsewhere (e.g. capturing a sub-step's output) without the spinner frames leaking into that redirect.
func (*Step) Interrupt ¶
func (s *Step) Interrupt(fn func())
Interrupt suspends the step's spinner and clears its line so fn can print standalone output above it, mirroring Live.Interrupt. On a non-animated step it just runs fn. paused is read and written under the package mu, the same lock frame() holds, so no redraw slips between the clear and fn's output.
func (*Step) Warn ¶ added in v1.26.1
Warn collapses the step with an amber warning glyph and message, for a non-fatal hiccup (e.g. a best-effort restart that didn't take) that should be noticed but doesn't mean the command itself failed. Unlike Fail it draws no red cross, so it doesn't read as the whole operation having errored.
type Summary ¶
type Summary struct {
// contains filtered or unexported fields
}
Summary is an aligned key/value block printed after an operation completes.