What this does not do¶
output is a small module with a deliberately narrow surface. This page lists
what it will not do, so you can stop looking for an option that is not there and
decide whether to work around it or reach for something else.
Each entry says whether the absence is a deliberate boundary or simply unimplemented.
Can I change the format for a single call?¶
No. The format is fixed on the Renderer at construction and every method reads
it. There is no per-call format argument and no setter.
Deliberate. A command that emitted JSON from one call and text from the next would produce a stream no consumer can parse. When you genuinely need two formats at once — machine-readable results on stdout, human feedback on stderr — build two renderers, which is the same idiom used to separate the streams:
out := output.New(output.WithWriter(os.Stdout), output.WithFormat(f))
feed := output.New(output.WithWriter(os.Stderr))
Can I get the Response envelope in YAML?¶
No. Emit and EmitError write JSON or nothing; in every other format they
return nil having written nothing.
Deliberate. The envelope exists to give scripts one stable shape, and one
shape means one encoding. If you need status and command in YAML, put them
on a struct of your own and pass it to Write.
Can I turn colour off?¶
Not with a flag or an environment variable. NO_COLOR, TERM=dumb and
WithInteractive(false) all leave the ANSI escape sequences in place, because
styling comes from the theme's lipgloss.Style values and those are rendered
unconditionally.
Unimplemented. The workaround is a theme whose styles are bare
lipgloss.NewStyle() values — see
theming.
Can I choose the Markdown style Render uses?¶
No. Render and RenderMarkdown pick glamour's built-in dark or light
style from the detected terminal background, defaulting to dark. There is no
option for a custom glamour stylesheet, and the Theme does not affect Markdown
rendering at all.
Unimplemented. Call glamour directly if you need a specific stylesheet.
Can I change the progress bar's width or reporting interval?¶
No. The bar is a fixed 40 characters wide, and the non-interactive form logs one
line at each 10% boundary. Neither is configurable; only the glyphs are, via
ProgressFilled, ProgressEmpty and ProgressHead.
Unimplemented. The bar does not adapt to terminal width either — on a narrow terminal the line wraps.
Can I right-align a column, or wrap a long cell?¶
No to both. Every cell in a text or Markdown table is left-aligned, and content
too wide for its column is truncated, never wrapped onto a second line.
There is no alignment field on Column and no wrapping option.
Unimplemented. Column.Width sets a fixed width, not a minimum or a maximum,
and truncates the header to match.
Can I sort by a field that is not a displayed column?¶
No. WithSortBy matches a column Header in the resolved column set, and
sorting happens on the rendered cell text after any Formatter has run. A value
that is not a column cannot be a sort key.
Deliberate for the column part — the table sorts what it shows — but the
consequence is a sharp edge: a Formatter that appends a unit turns a numeric
sort lexical. Sort on a raw column and display a formatted one.
Can I use a dotted path to a nested field?¶
No. Column.Field is a single struct field name or a single map key.
Field: "User.Name" finds no field called User.Name and renders an empty
cell; Field: "User" renders the struct with %v, giving {a}.
Unimplemented. Flatten the row into a view struct before rendering, or supply
a Formatter that does the reach:
output.Column{Header: "User", Field: "User", Formatter: func(v any) string {
u, _ := v.(inner)
return u.Name
}}
Embedded (promoted) fields are the exception — they are visible to the tag scan and work without a path.
Can I pass table options through Write?¶
No. When the format is csv, tsv or markdown, Write calls Table with no
options. Column definitions, sorting and header suppression are unavailable on
that path.
Unimplemented. Call Table directly when you need options, and use Write
for the general case:
if r.Format() == output.FormatText {
_ = r.Write(data, textFunc)
} else {
_ = r.Table(rows, output.WithSortBy("Name"))
}
Can I render a table one row at a time?¶
No. Table takes a whole slice, reflects over it, extracts every cell into
strings and only then measures and writes. There is no streaming or incremental
API, and no row limit — a million-row slice is a million rows in memory twice.
Deliberate for the aligned text format, which cannot know a column's width
until it has seen every value. It is a real cost for csv, tsv and json,
which could in principle stream; for large exports, write those formats
yourself.
Can two goroutines render at once?¶
Not safely, in general. Status and Progress each carry a mutex and are safe
for concurrent use individually. A Renderer is only as safe as the writer it
was given, and nothing coordinates between helpers: two Status instances, or a
Status and a Progress, sharing a writer will interleave and corrupt each
other's in-place redraws.
Run one piece of live feedback at a time. Spin in particular takes over the
terminal with a Bubble Tea program and must not overlap with another spinner.
Can I page long output, or add borders and footers to a table?¶
No. There is no pager integration, no table borders, no footer or summary row, no column grouping and no cell spanning. The text table is header, rows, three spaces between columns.
Deliberate. The module's job is to make one value renderable in six formats,
not to be a terminal layout engine. Reach for lipgloss/table or bubbletea
when the output is really a UI.
Does it validate the --output flag value?¶
Only if you check. ParseFormat returns (FormatText, false) for anything it
does not recognise and never errors, and cobra.Format discards that boolean —
so --output jsno silently produces text.
Unimplemented. Register the flag with RegisterOutputFlag so the usage string
lists the valid values, and validate in your own PreRunE if a typo should be
fatal:
Is anything localised?¶
No. Formats() values, the Response envelope's status strings, the progress
bar's n/total (pct%) suffix and the .../... done/... failed spinner lines
are fixed English ASCII. There is no message catalogue and no hook to supply
one.
Deliberate for the machine-facing strings — status: "success" is a contract,
not prose. Unimplemented for the human-facing ones.