tui

package
v0.47.1 Latest Latest
Warning

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

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

Documentation

Overview

Package tui holds the terminal-presentation rules Draugr applies everywhere it writes for a person: when colour is allowed, what the colours mean, and how to link to more detail.

It exists because those rules were being re-derived per command. The console report, the log handler and the install prompt each had their own copy of the "is this a terminal" check, and two separate ANSI palettes had drifted apart. Output that looks assembled by different people is a real cost for a tool whose terminal *is* the product.

The rules, in one place:

  • colour only when writing to an interactive terminal, and never when NO_COLOR is set (https://no-color.org)
  • a fixed, semantic palette — callers ask for "critical", not for red
  • anything that degrades (colour, hyperlinks) degrades to plain text, so piped output and CI logs stay readable

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ColorEnabled

func ColorEnabled(w io.Writer) bool

ColorEnabled reports whether coloured output is appropriate for w: it must be an interactive terminal, and NO_COLOR must be unset.

func IsTerminal

func IsTerminal(v any) bool

IsTerminal reports whether v (an *os.File, in practice stdin/stdout/stderr) is a character device. Accepts any value so it can answer for a reader (is the user there to be prompted?) as well as a writer (should this be coloured?).

func Pad

func Pad(s string, width int) string

Pad left-aligns s to width, measured on the unstyled text. Padding must be computed before colour is applied, or escape codes inflate the apparent length and columns stop lining up.

Types

type Cell

type Cell struct {
	Text  string
	Style Style
	// URL turns the cell into a hyperlink where the terminal supports it. It costs no width,
	// which is what makes it usable in a table that's already wide.
	URL string
}

Cell is one value in a table row: the text, how it should read, and optionally where it points. The zero value is a plain, unlinked cell, so a caller only names what differs.

func PlainCell

func PlainCell(text string) Cell

PlainCell is a cell that's just text.

func Styled

func Styled(style Style, text string) Cell

Styled is a cell that reads as the given role.

type Painter

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

Painter renders styled text, or plain text when colour isn't appropriate for the destination. The zero value is a valid plain-text painter, so a caller that forgets to construct one degrades safely instead of emitting escape codes into a file.

func Colored

func Colored() Painter

Colored returns a Painter for a caller that has already decided, such as one whose colour setting comes from configuration rather than from inspecting the writer.

func For

func For(w io.Writer) Painter

For returns a Painter suited to w: colour only for an interactive terminal with NO_COLOR unset.

func Plain

func Plain() Painter

Plain returns a Painter that never colours — for tests and for building strings whose destination isn't known yet.

func (Painter) Append

func (p Painter) Append(buf []byte, style Style, s string) []byte

Append is Paint for a caller building a byte buffer — the log handler writes a line per record, and going through strings would allocate on every one.

func (Painter) Enabled

func (p Painter) Enabled() bool

Enabled reports whether this painter emits colour, so callers can skip work that only matters when coloured.

func (p Painter) Link(url, text string) string

Link renders text as an OSC 8 terminal hyperlink to url. Terminals that support it show the text and follow the link on click; everywhere else — an older terminal, a pipe, a CI log — the escape codes are absent and the text stands alone. It therefore costs no width, which is what makes it usable in a table that is already wide.

A caller with nowhere to link should pass an empty url and get the text back.

func (Painter) Paint

func (p Painter) Paint(style Style, s string) string

Paint wraps s in the style's escape codes, or returns it unchanged when colour is off.

type Style

type Style string

Style is a semantic role, not a colour: callers say what a thing *is* and the palette decides how it looks, so the same concept renders identically in every command.

const (
	StyleNone     Style = ""
	StyleCritical Style = "1;31" // bold red
	StyleHigh     Style = "31"
	StyleMedium   Style = "33"
	StyleLow      Style = "2"
	StyleFail     Style = "1;31"
	StylePass     Style = "32"
	StyleAccent   Style = "33" // draws the eye without implying severity
	StyleMuted    Style = "2"  // supporting detail: headers, labels, units
)

The palette. Severity styles mirror the report bands; the rest are roles, not colours.

type Table

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

Table renders aligned columns for a person reading a terminal.

It exists because alignment and colour interact badly: any padding computed after styling counts escape bytes as visible width and the columns drift apart. text/tabwriter has the same flaw — its Escape mechanism hides the bytes from parsing but still measures them — so every command that wanted colour was going to hand-roll its own width arithmetic. Table measures the plain text, pads, and only then paints.

func NewTable

func NewTable(p Painter, headers ...string) *Table

NewTable starts a table written with p. Headers may be omitted for a table whose columns need no labelling; when given, they're dimmed so they frame the data without competing.

func (*Table) Indent

func (t *Table) Indent(prefix string) *Table

Indent sets a prefix for every line, for tables nested under a heading.

func (*Table) Render

func (t *Table) Render(w io.Writer)

Render writes the table. Columns are sized to their widest plain-text value, and the final column is never padded — nothing follows it, and trailing spaces are noise in a diff or a copied-out log.

func (*Table) Row

func (t *Table) Row(cells ...Cell) *Table

Row appends a row. Rows may be shorter than the header — missing cells render empty.

func (*Table) RowWithNote

func (t *Table) RowWithNote(note string, cells ...Cell) *Table

RowWithNote appends a row followed by a dimmed continuation line, aligned under the second column. Use it when a row's own columns can't carry the explanation.

Jump to

Keyboard shortcuts

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