a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
160 lines
4.4 KiB
Go
160 lines
4.4 KiB
Go
package progress
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// Extended palette — used by the post-summary helpers.
|
|
var (
|
|
colWarn = lipgloss.Color("#E8C66B")
|
|
colInfoSoft = lipgloss.Color("#6EAAD4")
|
|
colMuted = lipgloss.Color("#5A5A60")
|
|
colBorder = lipgloss.Color("#3A3A40")
|
|
)
|
|
|
|
// Composed styles.
|
|
var (
|
|
styleHeading = lipgloss.NewStyle().Foreground(colFgDim).Bold(true)
|
|
styleCount = lipgloss.NewStyle().Foreground(colMuted)
|
|
styleKey = lipgloss.NewStyle().Foreground(colFg).Bold(true)
|
|
styleVal = lipgloss.NewStyle().Foreground(colFgDim)
|
|
styleHint = lipgloss.NewStyle().Foreground(colMuted).Italic(true)
|
|
styleStep = lipgloss.NewStyle().Foreground(colInfoSoft)
|
|
styleStrong = lipgloss.NewStyle().Foreground(colFg).Bold(true)
|
|
// styleBox carries everything but the border charset; Card applies the
|
|
// active (rounded or ASCII) border at render time so an OEM-codepage
|
|
// terminal gets a box it can actually draw.
|
|
styleBox = lipgloss.NewStyle().
|
|
BorderForeground(colBorder).
|
|
Padding(0, 1)
|
|
)
|
|
|
|
// Heading returns a section header on its own line: lowercase title,
|
|
// optional dim count chip. No leading indent — caller controls layout.
|
|
func Heading(title string, count ...string) string {
|
|
t := styleHeading.Render(strings.ToLower(title))
|
|
if len(count) > 0 && count[0] != "" {
|
|
t += " " + styleCount.Render(activeGlyphs().Sep+" "+count[0])
|
|
}
|
|
return t
|
|
}
|
|
|
|
// Caption renders a one-line dim hint.
|
|
func Caption(text string) string {
|
|
return styleHint.Render(text)
|
|
}
|
|
|
|
// Row renders one row of a 2-column listing: bold key (padded to keyWidth)
|
|
// followed by dim details. No leading indent.
|
|
func Row(key, details string, keyWidth int) string {
|
|
return styleKey.Render(padRight(key, keyWidth)) + " " + styleVal.Render(details)
|
|
}
|
|
|
|
// NumberedStep renders a numbered step (" 1. text") with the index in soft
|
|
// blue. Used by post-run summary cards for "next steps" hints.
|
|
func NumberedStep(n int, text string) string {
|
|
return styleStep.Render(fmt.Sprintf("%d.", n)) + " " + styleVal.Render(text)
|
|
}
|
|
|
|
// Chips renders items as " · " separated dim text, fitted to width if width
|
|
// is positive (otherwise a single-line list).
|
|
func Chips(items []string, width int) string {
|
|
if len(items) == 0 {
|
|
return ""
|
|
}
|
|
sep := " " + activeGlyphs().Sep + " "
|
|
if width <= 0 {
|
|
return styleVal.Render(strings.Join(items, sep))
|
|
}
|
|
// Width-aware multi-line wrap.
|
|
var lines []string
|
|
var cur strings.Builder
|
|
for i, it := range items {
|
|
piece := it
|
|
if i > 0 {
|
|
piece = sep + piece
|
|
}
|
|
if cur.Len()+lipgloss.Width(piece) > width && cur.Len() > 0 {
|
|
lines = append(lines, cur.String())
|
|
cur.Reset()
|
|
cur.WriteString(it)
|
|
} else {
|
|
cur.WriteString(piece)
|
|
}
|
|
}
|
|
if cur.Len() > 0 {
|
|
lines = append(lines, cur.String())
|
|
}
|
|
for i := range lines {
|
|
lines[i] = styleVal.Render(lines[i])
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|
|
|
|
// Stat renders "value unit" with an emphasised value (bold, given color)
|
|
// and dim unit. Pass an empty unit to skip the suffix.
|
|
type StatSeverity int
|
|
|
|
const (
|
|
StatNeutral StatSeverity = iota
|
|
StatGood
|
|
StatWarn
|
|
StatBad
|
|
)
|
|
|
|
func Stat(value, unit string, sev StatSeverity) string {
|
|
color := colFg
|
|
switch sev {
|
|
case StatGood:
|
|
color = colAccent
|
|
case StatWarn:
|
|
color = colWarn
|
|
case StatBad:
|
|
color = colErr
|
|
}
|
|
v := lipgloss.NewStyle().Foreground(color).Bold(true).Render(value)
|
|
if unit == "" {
|
|
return v
|
|
}
|
|
return v + " " + styleVal.Render(unit)
|
|
}
|
|
|
|
// StatStrip renders multiple Stats joined with a mid-dot separator.
|
|
func StatStrip(stats ...string) string {
|
|
return strings.Join(stats, styleVal.Render(" "+activeGlyphs().Sep+" "))
|
|
}
|
|
|
|
// Card wraps body in a dim rounded box with an optional title row above. Pad
|
|
// is the inner padding rows. Returns the box plus a trailing newline.
|
|
func Card(title, body string) string {
|
|
if title != "" {
|
|
body = styleStrong.Render(title) + "\n" + body
|
|
}
|
|
return styleBox.Border(activeGlyphs().Border).Render(body) + "\n"
|
|
}
|
|
|
|
// Indent prefixes every line of s with the given number of spaces.
|
|
func Indent(s string, n int) string {
|
|
pad := strings.Repeat(" ", n)
|
|
return pad + strings.ReplaceAll(s, "\n", "\n"+pad)
|
|
}
|
|
|
|
// SortStrings is a small convenience used by callers preparing chip lists.
|
|
func SortStrings(s []string) []string {
|
|
out := append([]string(nil), s...)
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
func padRight(s string, n int) string {
|
|
w := lipgloss.Width(s)
|
|
if w >= n {
|
|
return s
|
|
}
|
|
return s + strings.Repeat(" ", n-w)
|
|
}
|