Files
terminal/inline/README.md
T
2026-07-12 18:41:05 -04:00

5.1 KiB
Raw Blame History

inline

Styled text and in-place progress in the normal terminal scrollback. No raw mode, no alternate screen, no input handling, no cursor hiding — the shell keeps owning the terminal. Intended for CLI tools (package managers, service tooling, build scripts) that want color and live status without a full-screen TUI.

Unix only (//go:build unix). Depends on the parent terminal package for color types, capability detection, and RGB → 256 mapping.

Model

Output is split into two zones:

installed openssl          ← permanent lines (Log) — scroll normally
installed zlib
⠹ installing curl          ← live block (Update) — rewritten in place
[██████░░░░░░░░] 2/5

Log prints permanent lines above the live block; Update replaces the live block by cursor-up + clear + rewrite; Done erases the block and optionally prints final lines. Interleaving is handled internally — Log during an active live block erases, prints, and redraws in one flush.

API

Printer

Method Description
New(w io.Writer) *Printer Creates a printer. Terminal detection via size probe; color defaults on for terminals with NO_COLOR unset. Safe for concurrent use.
Log(format string, a ...any) Prints one permanent line above the live block (Printf semantics, newline appended).
Update(lines ...string) Replaces the live block, rewriting in place. No-op on non-terminal output.
Done(final ...string) Erases the live block and prints final permanent lines. Call before exit.
Paint(s string, st Style) string Returns s wrapped in SGR codes for the detected color mode, or unchanged when color is off.
SetColor(on bool) Overrides color detection (e.g. force styling into a pipe for less -R). Affects Paint only; Update remains terminal-gated.
Size() (w, h int) Current terminal dimensions, 80×24 when unknown.

Style

Value type, zero value is unstyled, builder-composable:

Function Description
Fg(c terminal.RGB) Style Starts a style with foreground color.
(s Style) Bg(c terminal.RGB) Style Adds background color.
(s Style) Attr(a terminal.Attr) Style Adds attribute bits (AttrBold, AttrDim, ...).
warn := inline.Fg(terminal.Amber).Attr(terminal.AttrBold)
p.Log("%s low disk space", p.Paint("warning:", warn))

True color terminals get 38;2;R;G;B; 256-color terminals get 38;5;N via Redmean mapping — same degradation path as the parent package.

Progress helpers

Pure string builders, no Printer required:

Function Description
Bar(width int, pct float64, chars [3]rune) string Progress bar of width cells, pct clamped to [0,1], half-cell resolution via the partial rune.
BarBlock Default character set [3]rune{'█', '▌', '░'}.
Spinner(frame int) string Braille spinner frame for a monotonic counter.

Compose with Paint for colored bars:

line := "[" + p.Paint(inline.Bar(30, pct, inline.BarBlock), barStyle) + "]"

Non-terminal output

When output is a pipe or file (CI, redirection): Update is a no-op, Paint returns input unchanged, Log and Done print plain sequential text. A tool using inline degrades to ordinary log output with no code changes.

Example

Simulated package installation — spinner, overall progress bar, permanent completion lines:

package main

import (
	"fmt"
	"os"
	"time"

	"github.com/lixenwraith/terminal"
	"github.com/lixenwraith/terminal/inline"
)

func main() {
	p := inline.New(os.Stdout)

	name := inline.Fg(terminal.LightSkyBlue).Attr(terminal.AttrBold)
	okSt := inline.Fg(terminal.LimeGreen).Attr(terminal.AttrBold)
	dim := inline.Fg(terminal.IronGray)

	pkgs := []string{"openssl", "zlib", "curl", "git", "go"}
	frame := 0

	for i, pkg := range pkgs {
		const steps = 25
		for s := range steps {
			pct := (float64(i) + float64(s)/steps) / float64(len(pkgs))
			p.Update(
				inline.Spinner(frame)+" installing "+p.Paint(pkg, name),
				"["+inline.Bar(32, pct, inline.BarBlock)+"] "+
					p.Paint(fmt.Sprintf("%d/%d", i+1, len(pkgs)), dim),
			)
			frame++
			time.Sleep(40 * time.Millisecond)
		}
		p.Log("%s %s", p.Paint("✓", okSt), pkg)
	}

	p.Done(p.Paint("✓ 5 packages installed", okSt))
}

Run in a terminal: the two-line status block animates in place while completion lines accumulate above it. Piped (go run . | cat): only the completion lines and the final summary appear, unstyled.

Notes

  • Width is measured in runes (unicode/utf8); East Asian wide characters and combining marks are not width-aware — same limitation as tui.
  • Live block lines must occupy one visual row each: no \n, tabs, or control characters. Lines are truncated to terminal width automatically; embedded SGR from Paint is preserved through truncation.
  • The live block is clamped to terminal height 1 rows (newest lines kept).
  • Pass external strings (package names, paths) as Log arguments, never as the format string.
  • Ctrl-C mid-update leaves the live block on screen but the terminal in a normal state — no raw mode or screen buffer to restore.