Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

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)
}