all repos — llm_aggregator @ 658b2cfd3cd0eca5835b921100e08569a426e199

A CLI tool to aggregate RSS feeds and summarise them with LLMs

internal/style/style.go (view raw)

  1// Package style provides consistent text styling using lipgloss.
  2// It respects the NO_COLOR environment variable (https://no-color.org/).
  3package style
  4
  5import (
  6	"fmt"
  7	"os"
  8	"strings"
  9
 10	"github.com/charmbracelet/lipgloss"
 11)
 12
 13// Bold returns a bold style.
 14func Bold(text string) string {
 15	return lipgloss.NewStyle().Bold(true).Render(text)
 16}
 17
 18// Italic returns an italic style.
 19func Italic(text string) string {
 20	return lipgloss.NewStyle().Italic(true).Render(text)
 21}
 22
 23// Colour represents a lipgloss colour that can be applied.
 24type Colour lipgloss.Color
 25
 26// ANSI colour codes (standard 8-colour palette).
 27// Using ANSI escape codes for portability across terminals.
 28var (
 29	// Red for errors and warnings
 30	Red = Colour("1")
 31
 32	// Green for success messages
 33	Green = Colour("2")
 34
 35	// Yellow for emphasis
 36	Yellow = Colour("3")
 37
 38	// Blue for URLs
 39	Blue = Colour("12")
 40
 41	// Magenta for labels and info
 42	Magenta = Colour("5")
 43
 44	// Cyan for filepaths and values
 45	Cyan = Colour("6")
 46
 47	// White for headings
 48	White = Colour("15")
 49
 50	// Bright black for muted/debug text
 51	BrightBlack = Colour("8")
 52)
 53
 54// NoColor returns true if the NO_COLOR environment variable is set.
 55// This respects https://no-color.org/ standard.
 56func NoColor() bool {
 57	return os.Getenv("NO_COLOR") != ""
 58}
 59
 60// Warning returns an orange and bold warning message.
 61// Note: ANSI doesn't have orange, so we use red with bold styling.
 62func Warning(text string) string {
 63	if NoColor() {
 64		return lipgloss.NewStyle().Bold(true).Render("WARNING: " + text)
 65	}
 66	return lipgloss.NewStyle().
 67		Foreground(lipgloss.Color(Red)).
 68		Bold(true).
 69		Render("WARNING: " + text)
 70}
 71
 72// Warningf returns a formatted warning message.
 73func Warningf(format string, args ...any) string {
 74	return Warning(fmt.Sprintf(format, args...))
 75}
 76
 77// Error returns a red and bold error message.
 78func Error(text string) string {
 79	if NoColor() {
 80		return lipgloss.NewStyle().Bold(true).Render("ERROR: " + text)
 81	}
 82	return lipgloss.NewStyle().
 83		Foreground(lipgloss.Color(Red)).
 84		Bold(true).
 85		Render("ERROR: " + text)
 86}
 87
 88// Errorf returns a formatted error message.
 89func Errorf(format string, args ...any) string {
 90	return Error(fmt.Sprintf(format, args...))
 91}
 92
 93// Success returns a green success message.
 94func Success(text string) string {
 95	if NoColor() {
 96		return lipgloss.NewStyle().Bold(true).Render("✓ " + text)
 97	}
 98	return lipgloss.NewStyle().
 99		Foreground(lipgloss.Color(Green)).
100		Bold(true).
101		Render("✓ " + text)
102}
103
104// Successf returns a formatted success message.
105func Successf(format string, args ...any) string {
106	return Success(fmt.Sprintf(format, args...))
107}
108
109// Filepath returns a styled filepath in cyan.
110func Filepath(filepath string) string {
111	if NoColor() {
112		return filepath
113	}
114	return lipgloss.NewStyle().Foreground(lipgloss.Color(Cyan)).Render(filepath)
115}
116
117// FilepathStyled returns a lipgloss style for filepaths that can be applied to custom text.
118var FilepathStyled = lipgloss.NewStyle().Foreground(lipgloss.Color(Cyan))
119
120// URL returns a styled URL in blue with underline.
121func URL(url string) string {
122	if NoColor() {
123		return lipgloss.NewStyle().Underline(true).Render(url)
124	}
125	return lipgloss.NewStyle().
126		Foreground(lipgloss.Color(Blue)).
127		Underline(true).
128		Render(url)
129}
130
131// URLStyled returns a lipgloss style for URLs that can be applied to custom text.
132var URLStyled = lipgloss.NewStyle().Foreground(lipgloss.Color(Blue)).Underline(true)
133
134// Label returns a styled label/key in magenta and bold.
135func Label(text string) string {
136	if NoColor() {
137		return lipgloss.NewStyle().Bold(true).Render(text)
138	}
139	return lipgloss.NewStyle().
140		Foreground(lipgloss.Color(Magenta)).
141		Bold(true).
142		Render(text)
143}
144
145// LabelStyled returns a lipgloss style for labels.
146var LabelStyled = lipgloss.NewStyle().Foreground(lipgloss.Color(Magenta)).Bold(true)
147
148// Heading returns a styled heading.
149func Heading(text string) string {
150	return lipgloss.NewStyle().Bold(true).Render(text)
151}
152
153// Section returns a styled section header.
154func Section(text string) string {
155	return lipgloss.NewStyle().
156		Bold(true).
157		Underline(true).
158		Render(text)
159}
160
161// Value returns styled value text.
162func Value(text string) string {
163	if NoColor() {
164		return text
165	}
166	return lipgloss.NewStyle().Foreground(lipgloss.Color(Cyan)).Render(text)
167}
168
169// ValueStyled returns a lipgloss style for values.
170var ValueStyled = lipgloss.NewStyle().Foreground(lipgloss.Color(Cyan))
171
172// Info returns a styled info message in cyan.
173func Info(text string) string {
174	if NoColor() {
175		return "ℹ " + text
176	}
177	return lipgloss.NewStyle().Foreground(lipgloss.Color(Cyan)).Render("ℹ " + text)
178}
179
180// Infof returns a formatted info message.
181func Infof(format string, args ...any) string {
182	return Info(fmt.Sprintf(format, args...))
183}
184
185// Debug returns a styled debug message in grey.
186func Debug(text string) string {
187	if NoColor() {
188		return lipgloss.NewStyle().Italic(true).Render("Debug: " + text)
189	}
190	return lipgloss.NewStyle().
191		Foreground(lipgloss.Color(BrightBlack)).
192		Italic(true).
193		Render("Debug: " + text)
194}
195
196// Debugf returns a formatted debug message.
197func Debugf(format string, args ...any) string {
198	return Debug(fmt.Sprintf(format, args...))
199}
200
201// Highlight returns a highlighted text (yellow bold).
202func Highlight(text string) string {
203	if NoColor() {
204		return lipgloss.NewStyle().Bold(true).Render(text)
205	}
206	return lipgloss.NewStyle().
207		Foreground(lipgloss.Color(Yellow)).
208		Bold(true).
209		Render(text)
210}
211
212// Highlightf returns a formatted highlighted message.
213func Highlightf(format string, args ...any) string {
214	return Highlight(fmt.Sprintf(format, args...))
215}
216
217// ParseAndStyleURLs takes a string and styles any URLs found within it.
218// This is useful for processing text that may contain URLs mixed with other content.
219func ParseAndStyleURLs(text string) string {
220	// Simple URL pattern matching - matches http:// and https:// URLs
221	// This is a basic implementation; for production use, consider a regex library
222	parts := strings.Split(text, " ")
223
224	for i, part := range parts {
225		if strings.HasPrefix(part, "http://") || strings.HasPrefix(part, "https://") {
226			parts[i] = URL(part)
227		}
228	}
229
230	return strings.Join(parts, " ")
231}
232
233// ParseAndStyleFilepaths takes a string and styles any filepaths found within it.
234// This looks for paths that start with / or are relative paths containing /.
235func ParseAndStyleFilepaths(text string) string {
236	// Simple detection for filepaths - words containing / but not just URLs
237	parts := strings.Split(text, " ")
238
239	for i, part := range parts {
240		// Skip if it's already a URL (starts with http)
241		if strings.HasPrefix(part, "http://") || strings.HasPrefix(part, "https://") {
242			continue
243		}
244
245		// Check if it looks like a filepath
246		if strings.Contains(part, "/") && !strings.Contains(part, "://") {
247			parts[i] = Filepath(part)
248		}
249	}
250
251	return strings.Join(parts, " ")
252}
253