ajhahn.de
← eeco
Go 74 lines
package tui

import (
	"os"

	"github.com/charmbracelet/lipgloss"
)

// styles holds the few rendered styles the control center uses. They are
// intentionally minimal and neutral (Constraint 4: precise, no emoji, no
// first person). When colour is disabled the styles degrade to plain
// text rather than changing layout.
type styles struct {
	brand       lipgloss.Style // logo accent (matches the "eco" half of the README mark)
	logoMuted   lipgloss.Style // logo accent for the leading "e"; adapts to terminal background (near-black on light, near-white on dark) to mirror the two PNG mark variants
	prompt      lipgloss.Style // » input prompt
	dim         lipgloss.Style // hint line, secondary text
	dimmer      lipgloss.Style // status footer — sits below placeholder weight
	key         lipgloss.Style // command names, key labels
	value       lipgloss.Style // primary readable body text — table values, free-text body
	tableHeader lipgloss.Style // column header cells in a section table
	warn        lipgloss.Style // cautionary inline notes
	ok          lipgloss.Style // success inline notes
}

// colorEnabled reports whether coloured output is wanted. The NO_COLOR
// convention (any non-empty value disables colour) is honoured; a value
// the user cannot override (a non-terminal sink) is handled separately
// by the non-interactive path.
func colorEnabled() bool {
	return os.Getenv("NO_COLOR") == ""
}

// newStyles builds the style set. With colour off every style is the
// identity style, so the same View code path renders readable plain
// text under NO_COLOR or a dumb terminal.
func newStyles(color bool) styles {
	if !color {
		plain := lipgloss.NewStyle()
		return styles{
			brand: plain, logoMuted: plain, prompt: plain,
			dim: plain, dimmer: plain, key: plain,
			value: plain, tableHeader: plain,
			warn: plain, ok: plain,
		}
	}
	// Atom One Dark palette. Blue (#61afef, the palette's blue slot) is
	// the eeco brand accent — logo "eco", prompt, command names, primary
	// affordances. Green stays a semantic success signal only (never an
	// accent). Greys form a three-step hierarchy: body > hint > footer.
	const (
		blue     = "#61afef" // brand accent — logo "eco", prompt, command names
		cyan     = "#66b2ff" // table headers
		yellow   = "#e5c07b" // cautions
		green    = "#98c379" // success only
		fgWhite  = "#ffffff" // primary body text
		fgGrey   = "#abb2bf" // hints, secondary text
		fgFooter = "#5c6370" // status footer, faintest
		nearDark = "#282c34" // light-background foreground
	)
	return styles{
		brand:       lipgloss.NewStyle().Foreground(lipgloss.Color(blue)).Bold(true),
		logoMuted:   lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: nearDark, Dark: fgGrey}).Bold(true),
		prompt:      lipgloss.NewStyle().Foreground(lipgloss.Color(blue)).Bold(true),
		dim:         lipgloss.NewStyle().Foreground(lipgloss.Color(fgGrey)),
		dimmer:      lipgloss.NewStyle().Foreground(lipgloss.Color(fgFooter)),
		key:         lipgloss.NewStyle().Foreground(lipgloss.Color(blue)).Bold(true),
		value:       lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: nearDark, Dark: fgWhite}),
		tableHeader: lipgloss.NewStyle().Foreground(lipgloss.Color(cyan)).Bold(true),
		warn:        lipgloss.NewStyle().Foreground(lipgloss.Color(yellow)).Bold(true),
		ok:          lipgloss.NewStyle().Foreground(lipgloss.Color(green)).Bold(true),
	}
}