Documentation
¶
Overview ¶
Package output renders command results in either pretty TTY form or stable machine-readable JSON. The renderer is created once per command in PersistentPreRunE and passed via the Runtime.
Contract:
- stdout is data.
- stderr is chatter (Success / Info / Hint messages, spinners, errors).
- --json never auto-activates from pipe detection; the user opts in.
- --no-color (or NO_COLOR env) disables ALL ANSI output, including text-style attributes like bold (not just color).
Index ¶
- Variables
- func EscapeC1JSON(b []byte) []byte
- func HelpCommand(color bool, s string) string
- func HelpDim(color bool, s string) string
- func HelpHeader(color bool, s string) string
- func Hyperlink(styledLabel, url string) string
- func Sanitize(s string) string
- func SanitizeLine(s string) string
- type Card
- type CardLine
- type CardSection
- type CardTable
- type Chip
- type ChipTone
- type Renderer
- func (r *Renderer) AlwaysWarn(msg string)
- func (r *Renderer) Answer(key, value string)
- func (r *Renderer) Blank()
- func (r *Renderer) Error(msg string)
- func (r *Renderer) Field(key, value string, note ...string)
- func (r *Renderer) Hint(msg string)
- func (r *Renderer) Info(msg string)
- func (r *Renderer) IsJSON() bool
- func (r *Renderer) Lead(text string)
- func (r *Renderer) Link(url string) string
- func (r *Renderer) LinkLine(url string)
- func (r *Renderer) LinkText(label, url string) string
- func (r *Renderer) NoColor() bool
- func (r *Renderer) Notice(lines ...string)
- func (r *Renderer) Paint(t Tone, text string) string
- func (r *Renderer) Panel(lines ...string) string
- func (r *Renderer) Plan(steps []string)
- func (r *Renderer) Render(v any) error
- func (r *Renderer) RenderCard(c Card) error
- func (r *Renderer) RenderJSON(v any) error
- func (r *Renderer) RenderTable(t Table) error
- func (r *Renderer) Stderr() io.Writer
- func (r *Renderer) Success(msg string)
- func (r *Renderer) Title(msg string)
- func (r *Renderer) Warn(msg string)
- type Table
- type Tone
Constants ¶
This section is empty.
Variables ¶
var ( // BrandRed is RevenueCat red — identity moments only (titles, accents), // never errors, so red text still means something is wrong. BrandRed = lipgloss.AdaptiveColor{Light: "#D40017", Dark: "#F2545B"} // ErrorRed is deeper than brand red so failures read as failures. ErrorRed = lipgloss.AdaptiveColor{Light: "#B70004", Dark: "#F03F3C"} GreenOK = lipgloss.AdaptiveColor{Light: "#00845E", Dark: "#11D483"} WarnAmber = lipgloss.AdaptiveColor{Light: "#AC592D", Dark: "#E79462"} InfoBlue = lipgloss.AdaptiveColor{Light: "#4F62C4", Dark: "#7D8EF2"} // AccentViolet is the interaction accent (focus, selection, cursors) — // RC violet. Red is reserved: brand red for section/title landmarks // only, error red for failures only, because in terminals colored red // text reads as danger regardless of brand. AccentViolet = lipgloss.AdaptiveColor{Light: "#7C5CA8", Dark: "#A987D1"} // NeutralGray is chrome: borders, archived states, secondary chips. NeutralGray = lipgloss.AdaptiveColor{Light: "#8A8A8A", Dark: "#585858"} )
RevenueCat brand palette, taken from the dashboard's design tokens (revenuecat-app src/styles/theme.css + palette-primitives.css). Each color carries a light- and dark-background variant; lipgloss picks per terminal and degrades gracefully on 256-color terminals.
var ( StyleTitle = lipgloss.NewStyle().Bold(true) StyleAccent = lipgloss.NewStyle().Foreground(BrandRed).Bold(true) StyleSuccess = lipgloss.NewStyle().Foreground(GreenOK).Bold(true) StyleWarn = lipgloss.NewStyle().Foreground(WarnAmber) StyleError = lipgloss.NewStyle().Foreground(ErrorRed).Bold(true) StyleInfo = lipgloss.NewStyle().Foreground(InfoBlue) StyleDim = lipgloss.NewStyle().Faint(true) // StyleCommand is the interaction accent for commands the user can type. StyleCommand = lipgloss.NewStyle().Foreground(AccentViolet) )
Shared semantic styles. Commands and TUIs should use these instead of hand-picking ANSI colors so the whole CLI reads as one product.
var ErrBadFormat = errors.New("invalid --format expression")
ErrBadFormat signals an unparseable --format expression. CLI maps this to exit 2 (usage error) so agents can distinguish bad input from runtime failures.
Functions ¶
func EscapeC1JSON ¶ added in v0.1.4
EscapeC1JSON rewrites UTF-8-encoded C1 codepoints (0xC2 0x80–0x9F; in valid UTF-8, 0xC2 only ever appears as that lead byte) as JSON \u escapes: encoding/json escapes C0 on the wire but leaves C1 as raw bytes.
func HelpCommand ¶
func HelpHeader ¶
func Hyperlink ¶
Hyperlink wraps styledLabel in an OSC 8 terminal hyperlink pointing at url. Supporting terminals make it clickable; others render the label text. This is the one place the OSC 8 escape lives.
func Sanitize ¶ added in v0.1.4
Sanitize strips control characters (C0 except \n and \t, DEL, and C1) from a value before it is rendered as human output. API values are arbitrary text and must render as visible characters only — they must never be able to move the cursor or address the terminal.
func SanitizeLine ¶ added in v0.1.4
SanitizeLine is Sanitize for single-line contexts — table cells, labels, breadcrumbs, chips — where a newline would fake extra rows and a tab would shift columns; both collapse to a space.
Types ¶
type Card ¶
type Card struct {
// Title is the bold one-liner at the top (e.g. "cus_abc · iOS · US").
Title string
// Subtitle is a dimmer second line (e.g. "first seen 2024-12-18 · last seen 2025-05-15").
Subtitle string
// Sections render in order. Use Chips for entitlements, Table for
// subscriptions/purchases, Lines for arbitrary k:v pairs.
Sections []CardSection
// Raw is the structured payload returned under --json.
Raw any
}
Card is a structured detail view used by commands like `rc customers show`. It's an alternative to RenderTable for "one entity, many facets" output. JSON mode bypasses Card entirely and emits Raw via Render().
Sections render in the order they're added. Empty sections are skipped so the output adapts when subresources are missing.
type CardSection ¶
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
func NewRenderer ¶
func (*Renderer) AlwaysWarn ¶
AlwaysWarn writes a warning to stderr even in --json mode.
func (*Renderer) Answer ¶
Answer is the durable receipt for an answered prompt. Interactive forms erase themselves when they complete, so every decision the user makes is echoed back as a permanent transcript line.
func (*Renderer) Blank ¶
func (r *Renderer) Blank()
Blank prints an empty separator line between logical sections.
func (*Renderer) Field ¶
Field prints an aligned key/value line for status blocks: dim key, normal value, indented under a Title.
func (*Renderer) Hint ¶
Hint is guidance about what to do next — a whole line, dimmed, so it never competes with results.
func (*Renderer) Lead ¶
Lead is the orienting sentence(s) under a Title: what this flow is for and what the pieces mean, in plain language. Normal weight — education is content, not chrome — and wrapped for readability. One Lead per flow, at the top; stay terse afterwards.
func (*Renderer) Link ¶
Link renders a URL as a clickable, underlined brand-accent hyperlink. Falls back to the plain URL when color is off (our proxy for a dumb/non-interactive terminal), so nothing leaks escape codes into piped or --no-color output.
func (*Renderer) LinkLine ¶
LinkLine prints a single indented, clickable link on its own line (stderr chatter), for "open this URL" moments in guided flows.
func (*Renderer) LinkText ¶
LinkText renders a clickable hyperlink (OSC 8) with a custom label instead of the raw URL, so long auth URLs don't dominate the output. With color off it falls back to "label (url)" so the URL stays copyable.
func (*Renderer) NoColor ¶
NoColor reports whether all ANSI output is disabled (--no-color or NO_COLOR), so callers composing their own escapes (e.g. OSC 8 hyperlinks) can skip them.
func (*Renderer) Notice ¶
Notice renders a must-read callout: a colored bar block that stands out from the narration without being an error. Use for trust and safety statements at the moment they matter (e.g. before a credential prompt) — never dim these.
func (*Renderer) Panel ¶
Panel wraps pre-styled lines in a rounded border box sized to fit its content. The frame is drawn uncolored when color is disabled.
func (*Renderer) Plan ¶
Plan renders the guided-command plan: a titled, numbered list of the steps a consequential flow is about to take. Keep steps terse and state-aware — list what will happen this run, not every possibility.
func (*Renderer) Render ¶
Render writes the primary result. JSON mode emits a stable envelope so agents can rely on shape. When --format is set alongside --json, the envelope is fed through gojq; each output value is emitted on its own line.
func (*Renderer) RenderCard ¶
RenderCard writes a Card to stdout in TTY mode or hands off to Render() in JSON mode. Status helpers continue to go to stderr.
func (*Renderer) RenderJSON ¶
RenderJSON always emits JSON, even in human mode — for commands whose output IS structured data (rc schema, rc commands, SDK payload dumps).
func (*Renderer) RenderTable ¶
func (*Renderer) Stderr ¶
Stderr returns the chatter writer so guided-flow components (tui.Flow, ledger, rail prompts) render to the same stream as the rest of the CLI — and are captured in tests — instead of writing to os.Stderr directly.
func (*Renderer) Success ¶
Message methods sanitize their text: call sites routinely interpolate API values, and stderr is still a terminal.
type Table ¶
type Table struct {
Columns []string
Rows [][]string
// Raw is the structured value returned under --json. Required so JSON
// output stays useful (a table of strings would lose typing).
Raw any
}
Table is a column-oriented view rendered as a simple aligned table in TTY mode and as the raw underlying value in --json mode. Header order is the caller's responsibility — map iteration would be unstable.