1package fang
  2
  3import (
  4	"image/color"
  5	"os"
  6	"strings"
  7
  8	"github.com/charmbracelet/lipgloss/v2"
  9	"github.com/charmbracelet/x/exp/charmtone"
 10	"github.com/charmbracelet/x/term"
 11	"golang.org/x/text/cases"
 12	"golang.org/x/text/language"
 13)
 14
 15// ColorScheme describes a colorscheme.
 16type ColorScheme struct {
 17	Base           color.Color
 18	Title          color.Color
 19	Description    color.Color
 20	Codeblock      color.Color
 21	Program        color.Color
 22	DimmedArgument color.Color
 23	Comment        color.Color
 24	Flag           color.Color
 25	FlagDefault    color.Color
 26	Command        color.Color
 27	QuotedString   color.Color
 28	Argument       color.Color
 29	Help           color.Color
 30	Dash           color.Color
 31	ErrorHeader    [2]color.Color // 0=fg 1=bg
 32	ErrorDetails   color.Color
 33}
 34
 35// DefaultTheme is the default colorscheme.
 36//
 37// Deprecated: use [DefaultColorScheme] instead.
 38func DefaultTheme(isDark bool) ColorScheme {
 39	return DefaultColorScheme(lipgloss.LightDark(isDark))
 40}
 41
 42// DefaultColorScheme is the default colorscheme.
 43func DefaultColorScheme(c lipgloss.LightDarkFunc) ColorScheme {
 44	return ColorScheme{
 45		Base:           c(charmtone.Charcoal, charmtone.Ash),
 46		Title:          charmtone.Charple,
 47		Codeblock:      c(charmtone.Salt, lipgloss.Color("#2F2E36")),
 48		Program:        c(charmtone.Malibu, charmtone.Guppy),
 49		Command:        c(charmtone.Pony, charmtone.Cheeky),
 50		DimmedArgument: c(charmtone.Squid, charmtone.Oyster),
 51		Comment:        c(charmtone.Squid, lipgloss.Color("#747282")),
 52		Flag:           c(lipgloss.Color("#0CB37F"), charmtone.Guac),
 53		Argument:       c(charmtone.Charcoal, charmtone.Ash),
 54		Description:    c(charmtone.Charcoal, charmtone.Ash), // flag and command descriptions
 55		FlagDefault:    c(charmtone.Smoke, charmtone.Squid),  // flag default values in descriptions
 56		QuotedString:   c(charmtone.Coral, charmtone.Salmon),
 57		ErrorHeader: [2]color.Color{
 58			charmtone.Butter,
 59			charmtone.Cherry,
 60		},
 61	}
 62}
 63
 64// AnsiColorScheme is a ANSI colorscheme.
 65func AnsiColorScheme(c lipgloss.LightDarkFunc) ColorScheme {
 66	base := c(lipgloss.Black, lipgloss.White)
 67	return ColorScheme{
 68		Base:         base,
 69		Title:        lipgloss.Blue,
 70		Description:  base,
 71		Comment:      c(lipgloss.BrightWhite, lipgloss.BrightBlack),
 72		Flag:         lipgloss.Magenta,
 73		FlagDefault:  lipgloss.BrightMagenta,
 74		Command:      lipgloss.Cyan,
 75		QuotedString: lipgloss.Green,
 76		Argument:     base,
 77		Help:         base,
 78		Dash:         base,
 79		ErrorHeader:  [2]color.Color{lipgloss.Black, lipgloss.Red},
 80		ErrorDetails: lipgloss.Red,
 81	}
 82}
 83
 84// Styles represents all the styles used.
 85type Styles struct {
 86	Text            lipgloss.Style
 87	Title           lipgloss.Style
 88	Span            lipgloss.Style
 89	ErrorHeader     lipgloss.Style
 90	ErrorText       lipgloss.Style
 91	FlagDescription lipgloss.Style
 92	FlagDefault     lipgloss.Style
 93	Codeblock       Codeblock
 94	Program         Program
 95}
 96
 97// Codeblock styles.
 98type Codeblock struct {
 99	Base    lipgloss.Style
100	Program Program
101	Text    lipgloss.Style
102	Comment lipgloss.Style
103}
104
105// Program name, args, flags, styling.
106type Program struct {
107	Name           lipgloss.Style
108	Command        lipgloss.Style
109	Flag           lipgloss.Style
110	Argument       lipgloss.Style
111	DimmedArgument lipgloss.Style
112	QuotedString   lipgloss.Style
113}
114
115func mustColorscheme(cs func(lipgloss.LightDarkFunc) ColorScheme) ColorScheme {
116	var isDark bool
117	if term.IsTerminal(os.Stdout.Fd()) {
118		isDark = lipgloss.HasDarkBackground(os.Stdin, os.Stdout)
119	}
120	return cs(lipgloss.LightDark(isDark))
121}
122
123func makeStyles(cs ColorScheme) Styles {
124	//nolint:mnd
125	return Styles{
126		Text: lipgloss.NewStyle().Foreground(cs.Base),
127		Title: lipgloss.NewStyle().
128			Bold(true).
129			Foreground(cs.Title).
130			Transform(strings.ToUpper).
131			Padding(1, 0).
132			Margin(0, 2),
133		FlagDescription: lipgloss.NewStyle().
134			Foreground(cs.Description).
135			Transform(titleFirstWord),
136		FlagDefault: lipgloss.NewStyle().
137			Foreground(cs.FlagDefault),
138		Codeblock: Codeblock{
139			Base: lipgloss.NewStyle().
140				Background(cs.Codeblock).
141				Foreground(cs.Base).
142				MarginLeft(2).
143				Padding(1, 2),
144			Text: lipgloss.NewStyle().
145				Background(cs.Codeblock),
146			Comment: lipgloss.NewStyle().
147				Background(cs.Codeblock).
148				Foreground(cs.Comment),
149			Program: Program{
150				Name: lipgloss.NewStyle().
151					Background(cs.Codeblock).
152					Foreground(cs.Program),
153				Flag: lipgloss.NewStyle().
154					Background(cs.Codeblock).
155					Foreground(cs.Flag),
156				Argument: lipgloss.NewStyle().
157					Background(cs.Codeblock).
158					Foreground(cs.Argument),
159				DimmedArgument: lipgloss.NewStyle().
160					Background(cs.Codeblock).
161					Foreground(cs.DimmedArgument),
162				Command: lipgloss.NewStyle().
163					Background(cs.Codeblock).
164					Foreground(cs.Command),
165				QuotedString: lipgloss.NewStyle().
166					Background(cs.Codeblock).
167					Foreground(cs.QuotedString),
168			},
169		},
170		Program: Program{
171			Name: lipgloss.NewStyle().
172				Foreground(cs.Program),
173			Argument: lipgloss.NewStyle().
174				Foreground(cs.Argument),
175			DimmedArgument: lipgloss.NewStyle().
176				Foreground(cs.DimmedArgument),
177			Flag: lipgloss.NewStyle().
178				Foreground(cs.Flag),
179			Command: lipgloss.NewStyle().
180				Foreground(cs.Command),
181			QuotedString: lipgloss.NewStyle().
182				Foreground(cs.QuotedString),
183		},
184		Span: lipgloss.NewStyle().
185			Background(cs.Codeblock),
186		ErrorText: lipgloss.NewStyle().
187			MarginLeft(2).
188			Width(width() - 4).
189			Transform(titleFirstWord),
190		ErrorHeader: lipgloss.NewStyle().
191			Foreground(cs.ErrorHeader[0]).
192			Background(cs.ErrorHeader[1]).
193			Bold(true).
194			Padding(0, 1).
195			Margin(1).
196			MarginLeft(2).
197			SetString("ERROR"),
198	}
199}
200
201func titleFirstWord(s string) string {
202	words := strings.Fields(s)
203	if len(words) == 0 {
204		return s
205	}
206	words[0] = cases.Title(language.AmericanEnglish).String(words[0])
207	return strings.Join(words, " ")
208}