- New diary module: For, Bootstrap, Setup, SetVerbosity, Reset, Close, ParseConfig - Proxy component handler safe in package vars; styled console handler in bonfire's palette - Daily writer: one file per day, 0640, lazy open, retention, disables itself on open failure - Every channel leaf wrapped by sunscreen.Wrap; stack via slog.NewMultiHandler - bonfire: ColorMode, Palette, DefaultPalette, DetectProfile (lipgloss v2, colorprofile) - Generated main runs diary.Bootstrap first and diary.Setup after compass.Load - Ignore /storage/ and /examples/hello/storage/
101 lines
3.2 KiB
Go
101 lines
3.2 KiB
Go
package bonfire
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"charm.land/lipgloss/v2"
|
|
"github.com/charmbracelet/colorprofile"
|
|
)
|
|
|
|
// ColorMode selects how console output decides on color: from the terminal
|
|
// and the environment, always, or never.
|
|
type ColorMode int
|
|
|
|
const (
|
|
// ColorAuto colors output when the writer is a terminal and the
|
|
// environment allows it (NO_COLOR and TERM=dumb disable color,
|
|
// FORCE_COLOR enables it on any writer).
|
|
ColorAuto ColorMode = iota
|
|
// ColorAlways colors output whatever the writer is (--ansi).
|
|
ColorAlways
|
|
// ColorNever writes no escape codes at all (--no-ansi).
|
|
ColorNever
|
|
)
|
|
|
|
// Palette is the shared console look: the status styles of list, help,
|
|
// widgets and log lines. Render through a writer that applies the color
|
|
// profile (DetectProfile), because lipgloss always emits ANSI codes.
|
|
type Palette struct {
|
|
// Success marks completed work (green).
|
|
Success lipgloss.Style
|
|
// Current marks the item in progress and informational lines (cyan).
|
|
Current lipgloss.Style
|
|
// Warning marks warnings (yellow).
|
|
Warning lipgloss.Style
|
|
// Error marks failures (red).
|
|
Error lipgloss.Style
|
|
// ErrorBold marks fatal failures and error levels (bold red).
|
|
ErrorBold lipgloss.Style
|
|
// Muted marks secondary detail such as times and keys (gray).
|
|
Muted lipgloss.Style
|
|
// Header marks headings and names (bold).
|
|
Header lipgloss.Style
|
|
}
|
|
|
|
// DefaultPalette returns the console palette, ported from SSU's status
|
|
// styles: green success, cyan current, yellow warning, red and bold red
|
|
// errors, gray (240) muted text and bold headers.
|
|
func DefaultPalette() Palette {
|
|
return Palette{
|
|
Success: lipgloss.NewStyle().Foreground(lipgloss.Color("2")),
|
|
Current: lipgloss.NewStyle().Foreground(lipgloss.Color("6")),
|
|
Warning: lipgloss.NewStyle().Foreground(lipgloss.Color("3")),
|
|
Error: lipgloss.NewStyle().Foreground(lipgloss.Color("1")),
|
|
ErrorBold: lipgloss.NewStyle().Foreground(lipgloss.Color("1")).Bold(true),
|
|
Muted: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
|
|
Header: lipgloss.NewStyle().Bold(true),
|
|
}
|
|
}
|
|
|
|
// DetectProfile returns the color profile for console output written to w.
|
|
// ColorNever gives colorprofile.NoTTY (no escape codes at all). ColorAlways
|
|
// gives the detected profile raised to at least 256 colors. ColorAuto treats
|
|
// a non-empty FORCE_COLOR as ColorAlways unless NO_COLOR is set or TERM is
|
|
// dumb, and otherwise detects the profile from w and environ, which honours
|
|
// NO_COLOR and TERM=dumb. A nil environ means os.Environ().
|
|
func DetectProfile(w io.Writer, mode ColorMode, environ []string) colorprofile.Profile {
|
|
if mode == ColorNever {
|
|
return colorprofile.NoTTY
|
|
}
|
|
if environ == nil {
|
|
environ = os.Environ()
|
|
}
|
|
if mode == ColorAuto && forceColor(environ) {
|
|
mode = ColorAlways
|
|
}
|
|
p := colorprofile.Detect(w, environ)
|
|
if mode == ColorAlways && p < colorprofile.ANSI256 {
|
|
p = colorprofile.ANSI256
|
|
}
|
|
return p
|
|
}
|
|
|
|
func forceColor(environ []string) bool {
|
|
if envValue(environ, "NO_COLOR") != "" || envValue(environ, "TERM") == "dumb" {
|
|
return false
|
|
}
|
|
return envValue(environ, "FORCE_COLOR") != ""
|
|
}
|
|
|
|
func envValue(environ []string, key string) string {
|
|
for i := len(environ) - 1; i >= 0; i-- {
|
|
k, v, ok := strings.Cut(environ[i], "=")
|
|
if ok && k == key {
|
|
return v
|
|
}
|
|
}
|
|
return ""
|
|
}
|