Documentation
¶
Overview ¶
Package ui centralizes agentsync's terminal presentation: semantic color, a curated glyph vocabulary, and small layout primitives (sections, status lines, aligned labels). It is the single place that decides whether to emit ANSI, so every command renders through a *Printer and the color/glyph/spacing language stays consistent across `status`, `diff`, `doctor`, and `apply`.
Two independent axes:
- Color is TTY-gated. `--color=always|never` forces it; `auto` (the default) enables color only when the output is a terminal and NO_COLOR (https://no-color.org) is unset. Non-TTY output (pipes, files, tests) is therefore byte-for-byte plain — color never leaks into a redirect.
- Glyphs are always Unicode. The ✓ / ◐ / ✗ vocabulary already appears in the translation report and the capability matrix; keeping it unconditional means piped output reads the same as the screen and existing fixtures hold. Color, not glyph choice, is what degrades.
Color is reserved for state: a green ✓ means synced, a red ✗ means drift. It is never decoration. Everything still parses with color stripped.
Index ¶
- Constants
- func Pad(s string, width int) string
- type ColorMode
- type Printer
- func (p *Printer) Blue(s string) string
- func (p *Printer) Bold(s string) string
- func (p *Printer) Color() bool
- func (p *Printer) Cyan(s string) string
- func (p *Printer) Faint(s string) string
- func (p *Printer) Green(s string) string
- func (p *Printer) Red(s string) string
- func (p *Printer) Section(title string)
- func (p *Printer) Spin(label string) func()
- func (p *Printer) Spinner(label string) *Spinner
- func (p *Printer) Yellow(s string) string
- type Spinner
- type WarnWriter
Constants ¶
const ( GlyphOK = "✓" // success / synced / clean GlyphPartial = "◐" // partial coverage (mirrors the capability matrix) GlyphErr = "✗" // failure / drift / missing GlyphWarn = "⚠" // warning / needs attention GlyphInfo = "•" // neutral bullet GlyphArrow = "→" // transition / "see" )
Curated glyph vocabulary. Always Unicode; each is one display column wide, so callers can align around them with plain rune/space counting (no runewidth).
const GlyphWarnEmoji = "⚠️"
GlyphWarnEmoji is the colourful warning sign (with VS16) used as the warning label prefix. Wider than one column in some terminals, which is fine — the warning lines are not part of any padded layout.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type ColorMode ¶
type ColorMode int
ColorMode is the resolved value of the global --color flag.
func ParseColorMode ¶
ParseColorMode maps the --color flag string to a ColorMode. An empty string defaults to auto so callers can pass the raw flag value.
type Printer ¶
Printer renders styled output to a pair of writers. Construct one per command invocation via New; the color decision is frozen at construction.
func New ¶
New builds a Printer bound to out/err, resolving whether to emit color from mode, the NO_COLOR environment variable, and whether out is a terminal.
func (*Printer) Bold ¶
Semantic style helpers. Each returns s unchanged when color is disabled, so callers can compose them freely without branching.
func (*Printer) Color ¶
Color reports whether this Printer emits ANSI. Commands that hand a writer to a third-party renderer (e.g. the diff library's own colorizer) consult this to gate that output through the same decision.
func (*Printer) Section ¶
Section prints a heading (bold when colored, plain text otherwise) to Out.
func (*Printer) Spin ¶
Spin is the one-call helper: it starts a Spinner and returns the stop function. Typical use:
stop := p.Spin("fetching marketplace github")
result, err := fetcher.Fetch(src, cacheDir)
stop()
if err != nil { ... }
type Spinner ¶
type Spinner struct {
// contains filtered or unexported fields
}
Spinner is a lightweight in-place progress indicator for slow network ops (marketplace fetch, plugin pull). It animates only when its writer is a terminal; off a terminal (CI logs, piped stderr, captured-output tests) it is a complete no-op — no animation, no static fallback line — so byte-stable fixtures stay byte-stable and grep'd output stays clean. The success line a caller already prints carries the result.
type WarnWriter ¶
type WarnWriter struct {
// contains filtered or unexported fields
}
WarnWriter wraps a destination writer and styles "warning: " line prefixes as a bold-yellow "⚠️ warning:" so every warning — whether emitted by the CLI itself, by an adapter's Ingest, or by capture's re-reference path — reads consistently. Lines that do not start with the literal "warning: " prefix (e.g. pre-styled ANSI lines, indented continuation lines, or "agentsync:" notes) pass through verbatim. The writer is line-buffered so a callers' partial Write is held until a newline arrives — fmt.Fprintf in practice always finishes a line per call, but buffering keeps a chunked writer correct.
Not safe for concurrent use: the line-assembly buffer is unsynchronized. One *WarnWriter per command invocation is the intended pattern.
func NewWarnWriter ¶
func NewWarnWriter(w io.Writer, p *Printer) *WarnWriter
NewWarnWriter returns a *WarnWriter that flushes styled lines to w using p. p's color decision is honored: with color off, the prefix becomes a plain "⚠️ warning:" (the glyph is content, not decoration — same rule as the curated glyph vocabulary above).
func (*WarnWriter) Flush ¶
func (s *WarnWriter) Flush()
Flush emits any buffered partial line (no trailing \n) as-is. Call at end of command if you've routed a writer that may not always end in \n; the import path does always terminate, so this is defensive.
func (*WarnWriter) RouteTo ¶
func (s *WarnWriter) RouteTo(a any) func()
RouteTo wires this writer into anything that exposes a SetStderr(io.Writer) setter (matching adapter.WarnEmitter) and returns a restore function that detaches the writer when invoked. Idiomatic use pairs with defer:
defer warnW.RouteTo(a)()
The inner RouteTo(a) call evaluates immediately (wires the writer); the outer () is the deferred restore. The returned function is always safe to call — it's a no-op when the target doesn't implement the setter, when the target is a typed-nil pointer, or when the target was an untyped nil — so callers never need to type-assert or nil-check.
Non-implementor cases that resolve to a silent no-op:
- untyped nil (`any(nil)`): the type-assert misses because the interface value carries no concrete type.
- typed nil (`var a *T = nil; RouteTo(a)`): the type-assert SUCCEEDS because the interface value holds the method set of *T, but calling SetStderr would dereference the nil pointer. RouteTo guards against this via reflect.
- any value whose dynamic type doesn't implement SetStderr.