// Package tui holds small, reusable terminal-UI primitives shared across the // tools in benvin-utils: direction markers, a common style palette, and // width-aware padding/truncation helpers that respect ANSI styling. package tui import ( "strings" "github.com/charmbracelet/lipgloss" ) // Shared style palette. Tools should prefer these so every benvin-utils TUI // looks consistent. var ( Header = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("15")).Background(lipgloss.Color("240")) Title = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("87")) Dim = lipgloss.NewStyle().Foreground(lipgloss.Color("244")) Up = lipgloss.NewStyle().Foreground(lipgloss.Color("203")) // a rising value = red-ish Down = lipgloss.NewStyle().Foreground(lipgloss.Color("84")) // a falling value = green Hot = lipgloss.NewStyle().Foreground(lipgloss.Color("203")) // over threshold Warn = lipgloss.NewStyle().Foreground(lipgloss.Color("214")) // approaching threshold Err = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("203")) ) // Marker glyphs for direction-of-change indicators. const ( GlyphUp = "▲" GlyphDown = "▼" GlyphFlat = "·" ) // Arrow renders a direction marker: 1 = up (▲), -1 = down (▼), 0 = flat (·). func Arrow(dir int) string { switch { case dir > 0: return Up.Render(GlyphUp) case dir < 0: return Down.Render(GlyphDown) default: return Dim.Render(GlyphFlat) } } // Sign reports whether d is positive (1), negative (-1), or zero (0). It is the // usual companion to Arrow when comparing a new sample against a previous one. func Sign[T int | int64 | float64](d T) int { switch { case d > 0: return 1 case d < 0: return -1 default: return 0 } } // Pad right-pads plain (unstyled) text to width w, truncating if longer. func Pad(s string, w int) string { if len(s) > w { s = s[:w] } return s + strings.Repeat(" ", w-len(s)) } // PadVisible right-pads a possibly ANSI-styled cell to width w using its visible // (rendered) width, so styled cells still align in a table. func PadVisible(s string, w int) string { if vis := lipgloss.Width(s); vis < w { return s + strings.Repeat(" ", w-vis) } return s } // Truncate shortens s to at most n runes, adding an ellipsis when it cuts. func Truncate(s string, n int) string { if n <= 0 { return "" } if len(s) <= n { return s } if n == 1 { return "…" } return s[:n-1] + "…" }