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}