├── logo.png
├── .github
└── FUNDING.yml
├── go.mod
├── go.sum
├── scripts
├── README.md
├── gen.tmpl
└── regenerate.go
├── options.go
├── LICENSE
├── README.md
├── ansi.go
├── ansi_test.go
└── cols.go
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leaanthony/go-ansi-parser/HEAD/logo.png
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: leaanthony
4 |
5 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/leaanthony/go-ansi-parser
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/matryer/is v1.4.0
7 | github.com/rivo/uniseg v0.2.0
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
2 | github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
3 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
4 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
5 |
--------------------------------------------------------------------------------
/scripts/README.md:
--------------------------------------------------------------------------------
1 | This script is used to regenerate the colour definitions used by go-ansi-parser.
2 |
3 | This script makes use of the colours defined [here](https://jonasjacek.github.io/colors/) which are licensed using the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
--------------------------------------------------------------------------------
/options.go:
--------------------------------------------------------------------------------
1 | package ansi
2 |
3 | // ParseOption specifies parse option.
4 | type ParseOption struct {
5 | ignoreUnexpectedCode bool
6 | ansiForegroundColor string
7 | ansiBackgroundColor string
8 | }
9 |
10 | // WithIgnoreInvalidCodes disables returning an error on invalid ANSI code.
11 | func WithIgnoreInvalidCodes() ParseOption {
12 | return ParseOption{ignoreUnexpectedCode: true}
13 | }
14 |
15 | // WithDefaultForegroundColor specifies default foreground code (ANSI 39).
16 | // See ColourMap variable and foreground color codes 30-37.
17 | func WithDefaultForegroundColor(ansiColor string) ParseOption {
18 | return ParseOption{ansiForegroundColor: ansiColor}
19 | }
20 |
21 | // WithDefaultBackgroundColor specifies default foreground code (ANSI 49).
22 | // See ColourMap variable and foreground color codes 30-37.
23 | func WithDefaultBackgroundColor(ansiColor string) ParseOption {
24 | return ParseOption{ansiBackgroundColor: ansiColor}
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/gen.tmpl:
--------------------------------------------------------------------------------
1 | package ansi
2 |
3 | // Rgb represents an RGB colour value
4 | // with 8bits per channel
5 | type Rgb struct {
6 | R uint8 `json:"r"`
7 | G uint8 `json:"g"`
8 | B uint8 `json:"b"`
9 | }
10 |
11 | // Hsl represents the HSL value of the colour
12 | type Hsl struct {
13 | H float64 `json:"h"`
14 | S float64 `json:"s"`
15 | L float64 `json:"l"`
16 | }
17 |
18 | // Col represents a colour value.
19 | // The Id is the ANSI colour ID.
20 | type Col struct {
21 | Id int `json:"id"`
22 | Hex string `json:"hex"`
23 | Rgb Rgb `json:"rgb"`
24 | Hsl Hsl `json:"hsl"`
25 | Name string `json:"name"`
26 | }
27 |
28 | // Cols represents the default colour definitions
29 | // used by the library. This may be overridden.
30 | var Cols = []*Col{ {{range $col := .}}
31 | {
32 | Id: {{.Id}},
33 | Hex: "{{.Hex}}",
34 | Rgb: Rgb{ {{.Rgb.R}}, {{.Rgb.G}}, {{.Rgb.B}} },
35 | Hsl: Hsl{ {{.Hsl.H}}, {{.Hsl.S}}, {{.Hsl.L}} },
36 | Name: "{{.Name}}",
37 | },{{end}}
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-Present Lea Anthony
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/scripts/regenerate.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | _ "embed"
6 | "encoding/json"
7 | "io/ioutil"
8 | "log"
9 | "net/http"
10 | "os"
11 | "path/filepath"
12 | "text/template"
13 | )
14 |
15 | // Rgb represents an RGB colour value
16 | // with 8bits per channel
17 | type Rgb struct {
18 | R uint8 `json:"r"`
19 | G uint8 `json:"g"`
20 | B uint8 `json:"b"`
21 | }
22 |
23 | // Hsl represents the HSL value of the colour
24 | type Hsl struct {
25 | H float64 `json:"h"`
26 | S float64 `json:"s"`
27 | L float64 `json:"l"`
28 | }
29 |
30 | // InputCol represents a colour value.
31 | type InputCol struct {
32 | Id uint8 `json:"colorId"`
33 | Hex string `json:"hexString"`
34 | Rgb Rgb `json:"rgb"`
35 | Hsl Hsl `json:"hsl"`
36 | Name string `json:"name"`
37 | }
38 |
39 | // Template is our cols.go template
40 | //go:embed gen.tmpl
41 | var Template string
42 |
43 | func main() {
44 |
45 | var Cols []InputCol
46 |
47 | resp, err := http.Get("https://jonasjacek.github.io/colors/data.json")
48 | if err != nil {
49 | log.Fatal(err)
50 | }
51 | defer func() {
52 | err := resp.Body.Close()
53 | if err != nil {
54 | log.Fatal(err)
55 | }
56 | }()
57 | data, err := ioutil.ReadAll(resp.Body)
58 | if err != nil {
59 | log.Fatal(err)
60 | }
61 | err = json.Unmarshal(data, &Cols)
62 | if err != nil {
63 | log.Fatal(err)
64 | }
65 |
66 | t, err := template.New("cols").Parse(Template)
67 | if err != nil {
68 | log.Fatal(err)
69 | }
70 | var buffer bytes.Buffer
71 | err = t.Execute(&buffer, Cols)
72 | if err != nil {
73 | log.Fatal(err)
74 | }
75 | err = os.WriteFile(filepath.Join("..", "cols.go"), buffer.Bytes(), 0755)
76 | if err != nil {
77 | log.Fatal(err)
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 | A library for parsing ANSI encoded strings
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Go ANSI Parser converts strings with [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code)
15 | into a slice of structs that represent styled text. Features:
16 |
17 | * Can parse ANSI 16, 256 and TrueColor
18 | * Supports all styles: Regular, Bold, Faint, Italic, Blinking, Inversed, Invisible, Underlined, Strikethrough
19 | * Provides RGB, Hex, HSL, ANSI ID and Name for parsed colours
20 | * Truncation - works with emojis and grapheme clusters
21 | * Length - works with emojis and grapheme clusters
22 | * Cleanse - removes the ansi escape codes
23 | * Configurable colour map for customisation
24 | * 100% Test Coverage
25 |
26 | # Installation
27 | ```shell
28 | go get github.com/leaanthony/go-ansi-parser
29 | ```
30 |
31 | ## Usage
32 |
33 | ### Parse
34 | ```go
35 | text, err := ansi.Parse("\u001b[1;31;40mHello World\033[0m")
36 |
37 | // is the equivalent of...
38 |
39 | text := []*ansi.StyledText{
40 | {
41 | Label: "Hello World",
42 | FgCol: &ansi.Col{
43 | Id: 9,
44 | Hex: "#ff0000",
45 | Rgb: &ansi.Rgb{ R: 255, G: 0, B: 0 },
46 | Hsl: &ansi.Hsl{ H: 0, S: 100, L: 50 },
47 | Name: "Red",
48 | },
49 | BgCol: &ansi.Col{
50 | Id: 0,
51 | Hex: "#000000",
52 | Rgb: &ansi.Rgb{0, 0, 0},
53 | Hsl: &ansi.Hsl{0, 0, 0},
54 | Name: "Black",
55 | },
56 | Style: 1,
57 | },
58 | }
59 | ```
60 | ### Truncating
61 | ```go
62 | shorter, err := ansi.Truncate("\u001b[1;31;40mHello\033[0m \u001b[0;30mWorld!\033[0m", 8)
63 |
64 | // is the equivalent of...
65 |
66 | shorter := "\u001b[1;31;40mHello\033[0m \u001b[0;30mWo\033[0m"
67 | ```
68 | ### Cleanse
69 | ```go
70 | cleaner, err := ansi.Cleanse("\u001b[1;31;40mHello\033[0m \u001b[0;30mWorld!\033[0m")
71 |
72 | // is the equivalent of...
73 |
74 | cleaner := "Hello World!"
75 | ```
76 | ### Length
77 | ```go
78 | length, err := ansi.Length("\u001b[1;31;40mHello\033[0m \u001b[0;30mWorld!\033[0m")
79 |
80 | // is the equivalent of...
81 |
82 | length := 12
83 |
84 | // Works with grapheme clusters and emoji
85 | length, err := ansi.Length("\u001b[1;31;40m👩🏽🔧😎\033[0m") // 2
86 | ```
87 |
--------------------------------------------------------------------------------
/ansi.go:
--------------------------------------------------------------------------------
1 | package ansi
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 |
8 | "github.com/rivo/uniseg"
9 | )
10 |
11 | // TextStyle is a type representing the
12 | // ansi text styles
13 | type TextStyle int
14 |
15 | const (
16 | // Bold Style
17 | Bold TextStyle = 1 << 0
18 | // Faint Style
19 | Faint TextStyle = 1 << 1
20 | // Italic Style
21 | Italic TextStyle = 1 << 2
22 | // Blinking Style
23 | Blinking TextStyle = 1 << 3
24 | // Inversed Style
25 | Inversed TextStyle = 1 << 4
26 | // Invisible Style
27 | Invisible TextStyle = 1 << 5
28 | // Underlined Style
29 | Underlined TextStyle = 1 << 6
30 | // Strikethrough Style
31 | Strikethrough TextStyle = 1 << 7
32 | // Bright Style
33 | Bright TextStyle = 1 << 8
34 | )
35 |
36 | type ColourMode int
37 |
38 | const (
39 | Default ColourMode = 0
40 | TwoFiveSix ColourMode = 1
41 | TrueColour ColourMode = 2
42 | )
43 |
44 | var invalid = fmt.Errorf("invalid ansi string")
45 | var missingTerminator = fmt.Errorf("missing escape terminator 'm'")
46 | var invalidTrueColorSequence = fmt.Errorf("invalid TrueColor sequence")
47 | var invalid256ColSequence = fmt.Errorf("invalid 256 colour sequence")
48 |
49 | const (
50 | // Default colors uses foreground color codes [30-37].
51 | // See ColourMap and case for background colors.
52 | defaultForegroundColor = "37"
53 | defaultBackgroundColor = "30"
54 | )
55 |
56 | // StyledText represents a single formatted string
57 | type StyledText struct {
58 | Label string
59 | FgCol *Col
60 | BgCol *Col
61 | Style TextStyle
62 | ColourMode ColourMode
63 | // Offset is the offset into the input string where the StyledText begins
64 | Offset int
65 | // Len is the length in bytes of the substring of the input text that
66 | // contains the styled text
67 | Len int
68 | }
69 |
70 | func (s *StyledText) styleToParams() []string {
71 | var params []string
72 | if s.Bold() {
73 | params = append(params, "1")
74 | }
75 | if s.Faint() {
76 | params = append(params, "2")
77 | }
78 | if s.Italic() {
79 | params = append(params, "3")
80 | }
81 | if s.Underlined() {
82 | params = append(params, "4")
83 | }
84 | if s.Blinking() {
85 | params = append(params, "5")
86 | }
87 | if s.Inversed() {
88 | params = append(params, "7")
89 | }
90 | if s.Invisible() {
91 | params = append(params, "8")
92 | }
93 | if s.Strikethrough() {
94 | params = append(params, "9")
95 | }
96 | if s.FgCol != nil {
97 | // Do we have an ID?
98 | switch s.ColourMode {
99 | case Default:
100 | offset := 30
101 | id := s.FgCol.Id
102 | // Adjust when bold has been applied to the id
103 | if (s.Bold() || s.Bright()) && id > 7 && id < 16 {
104 | id -= 8
105 | }
106 | if s.Bright() {
107 | offset = 90
108 | }
109 | params = append(params, fmt.Sprintf("%d", id+offset))
110 | case TwoFiveSix:
111 | params = append(params, []string{"38", "5", fmt.Sprintf("%d", s.FgCol.Id)}...)
112 | case TrueColour:
113 | r := fmt.Sprintf("%d", s.FgCol.Rgb.R)
114 | g := fmt.Sprintf("%d", s.FgCol.Rgb.G)
115 | b := fmt.Sprintf("%d", s.FgCol.Rgb.B)
116 | params = append(params, []string{"38", "2", r, g, b}...)
117 | }
118 | }
119 | if s.BgCol != nil {
120 | // Do we have an ID?
121 | switch s.ColourMode {
122 | case Default:
123 | id := s.BgCol.Id
124 | offset := 40
125 | if s.Bright() {
126 | offset = 100
127 | }
128 | // Adjust when bold has been applied to the id
129 | if (s.Bold() || s.Bright()) && id > 7 && id < 16 {
130 | id -= 8
131 | }
132 | params = append(params, fmt.Sprintf("%d", id+offset))
133 | case TwoFiveSix:
134 | params = append(params, []string{"48", "5", fmt.Sprintf("%d", s.BgCol.Id)}...)
135 | case TrueColour:
136 | r := fmt.Sprintf("%d", s.BgCol.Rgb.R)
137 | g := fmt.Sprintf("%d", s.BgCol.Rgb.G)
138 | b := fmt.Sprintf("%d", s.BgCol.Rgb.B)
139 | params = append(params, []string{"48", "2", r, g, b}...)
140 | }
141 | }
142 | return params
143 | }
144 |
145 | func (s *StyledText) String() string {
146 | params := strings.Join(s.styleToParams(), ";")
147 | return "\033[0;" + params + "m" + s.Label + "\033[0m"
148 | }
149 |
150 | // Bold will return true if the text has a Bold style
151 | func (s *StyledText) Bold() bool {
152 | return s.Style&Bold == Bold
153 | }
154 |
155 | // Faint will return true if the text has a Faint style
156 | func (s *StyledText) Faint() bool {
157 | return s.Style&Faint == Faint
158 | }
159 |
160 | // Italic will return true if the text has an Italic style
161 | func (s *StyledText) Italic() bool {
162 | return s.Style&Italic == Italic
163 | }
164 |
165 | // Blinking will return true if the text has a Blinking style
166 | func (s *StyledText) Blinking() bool {
167 | return s.Style&Blinking == Blinking
168 | }
169 |
170 | // Inversed will return true if the text has an Inversed style
171 | func (s *StyledText) Inversed() bool {
172 | return s.Style&Inversed == Inversed
173 | }
174 |
175 | // Invisible will return true if the text has an Invisible style
176 | func (s *StyledText) Invisible() bool {
177 | return s.Style&Invisible == Invisible
178 | }
179 |
180 | // Underlined will return true if the text has an Underlined style
181 | func (s *StyledText) Underlined() bool {
182 | return s.Style&Underlined == Underlined
183 | }
184 |
185 | // Strikethrough will return true if the text has a Strikethrough style
186 | func (s *StyledText) Strikethrough() bool {
187 | return s.Style&Strikethrough == Strikethrough
188 | }
189 |
190 | // Bright will return true if the text has a Bright style
191 | func (s *StyledText) Bright() bool {
192 | return s.Style&Bright == Bright
193 | }
194 |
195 | // ColourMap maps ansi identifiers to a colour
196 | var ColourMap = map[string]map[string]*Col{
197 | "Regular": {
198 | "30": Cols[0],
199 | "31": Cols[1],
200 | "32": Cols[2],
201 | "33": Cols[3],
202 | "34": Cols[4],
203 | "35": Cols[5],
204 | "36": Cols[6],
205 | "37": Cols[7],
206 | "90": Cols[8],
207 | "91": Cols[9],
208 | "92": Cols[10],
209 | "93": Cols[11],
210 | "94": Cols[12],
211 | "95": Cols[13],
212 | "96": Cols[14],
213 | "97": Cols[15],
214 | "100": Cols[8],
215 | "101": Cols[9],
216 | "102": Cols[10],
217 | "103": Cols[11],
218 | "104": Cols[12],
219 | "105": Cols[13],
220 | "106": Cols[14],
221 | "107": Cols[15],
222 | },
223 | "Bold": {
224 | "30": Cols[8],
225 | "31": Cols[9],
226 | "32": Cols[10],
227 | "33": Cols[11],
228 | "34": Cols[12],
229 | "35": Cols[13],
230 | "36": Cols[14],
231 | "37": Cols[15],
232 | "90": Cols[8],
233 | "91": Cols[9],
234 | "92": Cols[10],
235 | "93": Cols[11],
236 | "94": Cols[12],
237 | "95": Cols[13],
238 | "96": Cols[14],
239 | "97": Cols[15],
240 | "100": Cols[8],
241 | "101": Cols[9],
242 | "102": Cols[10],
243 | "103": Cols[11],
244 | "104": Cols[12],
245 | "105": Cols[13],
246 | "106": Cols[14],
247 | "107": Cols[15],
248 | },
249 | "Faint": {
250 | "30": Cols[0],
251 | "31": Cols[1],
252 | "32": Cols[2],
253 | "33": Cols[3],
254 | "34": Cols[4],
255 | "35": Cols[5],
256 | "36": Cols[6],
257 | "37": Cols[7],
258 | },
259 | }
260 |
261 | // Parse will convert an ansi encoded string and return
262 | // a slice of StyledText structs that represent the text.
263 | // If parsing is unsuccessful, an error is returned.
264 | func Parse(input string, options ...ParseOption) ([]*StyledText, error) {
265 | var result []*StyledText
266 | index := 0
267 | offset := 0
268 | escapeCodeLen := 0
269 | var currentStyledText = &StyledText{}
270 |
271 | if len(input) == 0 {
272 | return []*StyledText{currentStyledText}, nil
273 | }
274 |
275 | for {
276 | // Read all chars to next escape code
277 | esc := strings.Index(input, "\033[")
278 |
279 | // If no more esc chars, save what's left and return
280 | if esc == -1 {
281 | text := input[index:]
282 | if len(text) > 0 {
283 | currentStyledText.Label = text
284 | currentStyledText.Offset = offset
285 | currentStyledText.Len = len(text) + escapeCodeLen
286 | result = append(result, currentStyledText)
287 | }
288 | return result, nil
289 | }
290 | label := input[:esc]
291 | if len(label) > 0 {
292 | currentStyledText.Label = label
293 | currentStyledText.Offset = offset
294 | currentStyledText.Len = len(label) + escapeCodeLen
295 | offset += currentStyledText.Len
296 | result = append(result, currentStyledText)
297 | currentStyledText = &StyledText{
298 | Label: "",
299 | FgCol: currentStyledText.FgCol,
300 | BgCol: currentStyledText.BgCol,
301 | Style: currentStyledText.Style,
302 | }
303 | escapeCodeLen = 0
304 | }
305 | input = input[esc:]
306 | // skip
307 | input = input[2:]
308 |
309 | // Read in params
310 | endesc := strings.Index(input, "m")
311 | if endesc == -1 {
312 | return nil, missingTerminator
313 | }
314 | paramText := input[:endesc]
315 | input = input[endesc+1:]
316 | escapeCodeLen += 2 + endesc + 1
317 | params := strings.Split(paramText, ";")
318 | colourMap := ColourMap["Regular"]
319 | skip := 0
320 | for index, param := range params {
321 | if skip > 0 {
322 | skip--
323 | continue
324 | }
325 | param = stripLeadingZeros(param)
326 | switch param {
327 | case "0", "":
328 | colourMap = ColourMap["Regular"]
329 | currentStyledText.Style = 0
330 | currentStyledText.FgCol = nil
331 | currentStyledText.BgCol = nil
332 | case "1":
333 | // Bold
334 | colourMap = ColourMap["Bold"]
335 | currentStyledText.Style |= Bold
336 | case "2":
337 | // Dim/Feint
338 | colourMap = ColourMap["Faint"]
339 | currentStyledText.Style |= Faint
340 | case "3":
341 | // Italic
342 | currentStyledText.Style |= Italic
343 | case "4":
344 | // Underlined
345 | currentStyledText.Style |= Underlined
346 | case "5":
347 | // Blinking
348 | currentStyledText.Style |= Blinking
349 | case "7":
350 | // Inversed
351 | currentStyledText.Style |= Inversed
352 | case "8":
353 | // Invisible
354 | currentStyledText.Style |= Invisible
355 | case "9":
356 | // Strikethrough
357 | currentStyledText.Style |= Strikethrough
358 | case "30", "31", "32", "33", "34", "35", "36", "37":
359 | currentStyledText.FgCol = colourMap[param]
360 | case "90", "91", "92", "93", "94", "95", "96", "97":
361 | currentStyledText.FgCol = colourMap[param]
362 | currentStyledText.Style |= Bright
363 | case "100", "101", "102", "103", "104", "105", "106", "107":
364 | currentStyledText.BgCol = colourMap[param]
365 | currentStyledText.Style |= Bright
366 | case "40", "41", "42", "43", "44", "45", "46", "47":
367 | bgcol := "3" + param[1:] // Equivalent of -10
368 | currentStyledText.BgCol = colourMap[bgcol]
369 | case "38", "48":
370 | if len(params)-index < 3 {
371 | return nil, invalid
372 | }
373 | // 256 colours
374 | param1 := stripLeadingZeros(params[index+1])
375 | if param1 == "5" {
376 | skip = 2
377 | colIndexText := stripLeadingZeros(params[index+2])
378 | colIndex, err := strconv.Atoi(colIndexText)
379 | if err != nil {
380 | return nil, invalid256ColSequence
381 | }
382 | if colIndex < 0 || colIndex > 255 {
383 | return nil, invalid256ColSequence
384 | }
385 | currentStyledText.ColourMode = TwoFiveSix
386 | if param == "38" {
387 | currentStyledText.FgCol = Cols[colIndex]
388 | continue
389 | }
390 | currentStyledText.BgCol = Cols[colIndex]
391 | continue
392 | }
393 | // we must have 4 params left
394 | if len(params)-index < 5 {
395 | return nil, invalidTrueColorSequence
396 | }
397 | if param1 != "2" {
398 | return nil, invalidTrueColorSequence
399 | }
400 | var r, g, b uint8
401 | ri, err := strconv.Atoi(params[index+2])
402 | if err != nil {
403 | return nil, invalidTrueColorSequence
404 | }
405 | gi, err := strconv.Atoi(params[index+3])
406 | if err != nil {
407 | return nil, invalidTrueColorSequence
408 | }
409 | bi, err := strconv.Atoi(params[index+4])
410 | if err != nil {
411 | return nil, invalidTrueColorSequence
412 | }
413 | if bi > 255 || gi > 255 || ri > 255 {
414 | return nil, invalidTrueColorSequence
415 | }
416 | if bi < 0 || gi < 0 || ri < 0 {
417 | return nil, invalidTrueColorSequence
418 | }
419 | r = uint8(ri)
420 | g = uint8(gi)
421 | b = uint8(bi)
422 | skip = 4
423 | colvalue := fmt.Sprintf("#%02x%02x%02x", r, g, b)
424 | currentStyledText.ColourMode = TrueColour
425 | if param == "38" {
426 | currentStyledText.FgCol = &Col{Id: 256, Hex: colvalue, Rgb: Rgb{r, g, b}}
427 | continue
428 | }
429 | currentStyledText.BgCol = &Col{Id: 256, Hex: colvalue, Rgb: Rgb{r, g, b}}
430 | case "39":
431 | // Lookup for default foreground color.
432 | foregroundColor := colourMap[defaultForegroundColor]
433 | for _, option := range options {
434 | if option.ansiForegroundColor != "" {
435 | foregroundColor = colourMap[option.ansiForegroundColor]
436 | break
437 | }
438 | }
439 |
440 | // Set selected foreground color.
441 | currentStyledText.FgCol = foregroundColor
442 | case "49":
443 | // Lookup for default background color.
444 | backgroundColor := colourMap[defaultBackgroundColor]
445 | for _, option := range options {
446 | if option.ansiBackgroundColor != "" {
447 | backgroundColor = colourMap[option.ansiBackgroundColor]
448 | break
449 | }
450 | }
451 |
452 | // Set selected background color.
453 | currentStyledText.BgCol = backgroundColor
454 | default:
455 | // Unexpected codes may be ignored.
456 | unexpectedCodeIgnored := false
457 | for _, option := range options {
458 | if option.ignoreUnexpectedCode {
459 | unexpectedCodeIgnored = true
460 | break
461 | }
462 | }
463 | if !unexpectedCodeIgnored {
464 | return nil, invalid
465 | }
466 | }
467 | }
468 | }
469 | }
470 |
471 | func stripLeadingZeros(s string) string {
472 | if len(s) < 2 {
473 | return s
474 | }
475 | return strings.TrimLeft(s, "0")
476 | }
477 |
478 | // HasEscapeCodes tests that input has escape codes.
479 | func HasEscapeCodes(input string) bool {
480 | return strings.IndexAny(input, "\033[") != -1
481 | }
482 |
483 | // String builds an ANSI string for specified StyledText slice.
484 | func String(input []*StyledText) string {
485 | var result strings.Builder
486 | for _, text := range input {
487 | params := text.styleToParams()
488 | if len(params) == 0 {
489 | result.WriteString(text.Label)
490 | continue
491 | }
492 | result.WriteString(text.String())
493 | }
494 | return result.String()
495 | }
496 |
497 | // Truncate truncates text to length but preserves control symbols in ANSI string.
498 | func Truncate(input string, maxChars int, options ...ParseOption) (string, error) {
499 | parsed, err := Parse(input, options...)
500 | if err != nil {
501 | return "", err
502 | }
503 | charsLeft := maxChars
504 | var result []*StyledText
505 | for _, element := range parsed {
506 | userPerceivedChars := uniseg.GraphemeClusterCount(element.Label)
507 | if userPerceivedChars >= charsLeft {
508 | var newLabel []rune
509 | graphemes := uniseg.NewGraphemes(element.Label)
510 | for graphemes.Next() {
511 | newLabel = append(newLabel, graphemes.Runes()...)
512 | charsLeft--
513 | if charsLeft == 0 {
514 | element.Label = string(newLabel)
515 | result = append(result, element)
516 | return String(result), nil
517 | }
518 | }
519 | }
520 | result = append(result, element)
521 | charsLeft -= userPerceivedChars
522 | }
523 | return String(result), nil
524 | }
525 |
526 | // Cleanse removes ANSI control symbols from the string.
527 | func Cleanse(input string, options ...ParseOption) (string, error) {
528 | if input == "" {
529 | return "", nil
530 | }
531 | parsed, err := Parse(input, options...)
532 | if err != nil {
533 | return "", err
534 | }
535 | var result strings.Builder
536 | for _, element := range parsed {
537 | result.WriteString(element.Label)
538 | }
539 | return result.String(), nil
540 | }
541 |
542 | // Length calculates count of user-perceived characters in ANSI string.
543 | func Length(input string, options ...ParseOption) (int, error) {
544 | if input == "" {
545 | return 0, nil
546 | }
547 | parsed, err := Parse(input, options...)
548 | if err != nil {
549 | return -1, err
550 | }
551 | var result int
552 | for _, element := range parsed {
553 | userPerceivedChars := uniseg.GraphemeClusterCount(element.Label)
554 | result += userPerceivedChars
555 | }
556 | return result, nil
557 | }
558 |
--------------------------------------------------------------------------------
/ansi_test.go:
--------------------------------------------------------------------------------
1 | package ansi
2 |
3 | import (
4 | "testing"
5 |
6 | is "github.com/matryer/is"
7 | )
8 |
9 | func TestParseAnsi16Styles(t *testing.T) {
10 | is2 := is.New(t)
11 | var got []*StyledText
12 | var err error
13 |
14 | // Bold
15 | got, err = Parse("\u001b[1;30mHello World\033[0m")
16 | is2.NoErr(err)
17 | is2.Equal(len(got), 1)
18 | is2.True(got[0].Bold())
19 | is2.Equal(got[0].Len, 18)
20 | is2.Equal(got[0].Offset, 0)
21 | // Faint
22 | got, err = Parse("\u001b[2;30mHello World\033[0m")
23 | is2.NoErr(err)
24 | is2.Equal(len(got), 1)
25 | is2.True(got[0].Faint())
26 | // Italic
27 | got, err = Parse("\u001b[3;30mHello World\033[0m")
28 | is2.NoErr(err)
29 | is2.Equal(len(got), 1)
30 | is2.True(got[0].Italic())
31 | // Underlined
32 | got, err = Parse("\u001b[4;30mHello World\033[0m")
33 | is2.NoErr(err)
34 | is2.Equal(len(got), 1)
35 | is2.True(got[0].Underlined())
36 | // Blinking
37 | got, err = Parse("\u001b[5;30mHello World\033[0m")
38 | is2.NoErr(err)
39 | is2.Equal(len(got), 1)
40 | is2.True(got[0].Blinking())
41 | // Inversed
42 | got, err = Parse("\u001b[7;30mHello World\033[0m")
43 | is2.NoErr(err)
44 | is2.Equal(len(got), 1)
45 | is2.True(got[0].Inversed())
46 | // Invisible
47 | got, err = Parse("\u001b[8;30mHello World\033[0m")
48 | is2.NoErr(err)
49 | is2.Equal(len(got), 1)
50 | is2.True(got[0].Invisible())
51 | // Strikethrough
52 | got, err = Parse("\u001b[9;30mHello World\033[0m")
53 | is2.NoErr(err)
54 | is2.Equal(len(got), 1)
55 | is2.True(got[0].Strikethrough())
56 | }
57 |
58 | func TestParseAnsi16Swap(t *testing.T) {
59 | is2 := is.New(t)
60 | var got []*StyledText
61 | var err error
62 |
63 | // Swap single
64 | c0ffee := &Col{
65 | Id: 0,
66 | Hex: "c0ffee",
67 | Name: "Coffee",
68 | }
69 | original := ColourMap["Regular"]["30"]
70 | ColourMap["Regular"]["30"] = c0ffee
71 | got, err = Parse("\u001b[0;30mHello World\033[0m")
72 | is2.NoErr(err)
73 | is2.Equal(len(got), 1)
74 | is2.Equal(got[0].FgCol.Name, "Coffee")
75 |
76 | // Restore
77 | ColourMap["Regular"]["30"] = original
78 | got, err = Parse("\u001b[0;30mHello World\033[0m")
79 | is2.NoErr(err)
80 | is2.Equal(len(got), 1)
81 | is2.Equal(got[0].FgCol.Name, "Black")
82 | }
83 |
84 | func TestParseAnsi16SingleColour(t *testing.T) {
85 | is2 := is.New(t)
86 | tests := []struct {
87 | name string
88 | input string
89 | wantText string
90 | wantColor string
91 | wantErr bool
92 | }{
93 | {"No formatting", "Hello World", "Hello World", "", false},
94 | {"Black", "\u001b[0;30mHello World\033[0m", "Hello World", "Black", false},
95 | {"Red", "\u001b[0;31mHello World\033[0m", "Hello World", "Maroon", false},
96 | {"Green", "\u001b[0;32mGreen\033[0m", "Green", "Green", false},
97 | {"Yellow", "\u001b[0;33m😀\033[0m", "😀", "Olive", false},
98 | {"Blue", "\u001b[0;34m123\033[0m", "123", "Navy", false},
99 | {"Purple", "\u001b[0;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Purple", false},
100 | {"Cyan", "\033[0;36m😀\033[0m", "😀", "Teal", false},
101 | {"White", "\u001b[0;37m[0;37m\033[0m", "[0;37m", "Silver", false},
102 | {"Black Bold", "\u001b[1;30mHello World\033[0m", "Hello World", "Grey", false},
103 | {"Red Bold", "\u001b[1;31mHello World\033[0m", "Hello World", "Red", false},
104 | {"Green Bold", "\u001b[1;32mGreen\033[0m", "Green", "Lime", false},
105 | {"Yellow Bold", "\u001b[1;33m😀\033[0m", "😀", "Yellow", false},
106 | {"Blue Bold", "\u001b[1;34m123\033[0m", "123", "Blue", false},
107 | {"Purple Bold", "\u001b[1;35m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
108 | {"Cyan Bold", "\033[1;36m😀\033[0m", "😀", "Aqua", false},
109 | {"White Bold", "\u001b[1;37m[0;37m\033[0m", "[0;37m", "White", false},
110 | {"Black Bold & Bright", "\u001b[1;90mHello World\033[0m", "Hello World", "Grey", false},
111 | {"Red Bold & Bright", "\u001b[1;91mHello World\033[0m", "Hello World", "Red", false},
112 | {"Green Bold & Bright", "\u001b[1;92mGreen\033[0m", "Green", "Lime", false},
113 | {"Yellow Bold & Bright", "\u001b[1;93m😀\033[0m", "😀", "Yellow", false},
114 | {"Blue Bold & Bright", "\u001b[1;94m123\033[0m", "123", "Blue", false},
115 | {"Purple Bold & Bright", "\u001b[1;95m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
116 | {"Cyan Bold & Bright", "\033[1;96m😀\033[0m", "😀", "Aqua", false},
117 | {"White Bold & Bright", "\u001b[1;97m[0;37m\033[0m", "[0;37m", "White", false},
118 | {"Black Bright", "\u001b[90mHello World\033[0m", "Hello World", "Grey", false},
119 | {"Red Bright", "\u001b[91mHello World\033[0m", "Hello World", "Red", false},
120 | {"Green Bright", "\u001b[92mGreen\033[0m", "Green", "Lime", false},
121 | {"Yellow Bright", "\u001b[93m😀\033[0m", "😀", "Yellow", false},
122 | {"Blue Bright", "\u001b[94m123\033[0m", "123", "Blue", false},
123 | {"Purple Bright", "\u001b[95m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
124 | {"Cyan Bright", "\033[96m😀\033[0m", "😀", "Aqua", false},
125 | {"White Bright", "\u001b[97m[0;37m\033[0m", "[0;37m", "White", false},
126 | {"Black Bold & Bright Background", "\u001b[1;100mHello World\033[0m", "Hello World", "Grey", false},
127 | {"Red Bold & Bright Background", "\u001b[1;101mHello World\033[0m", "Hello World", "Red", false},
128 | {"Green Bold & Bright Background", "\u001b[1;102mGreen\033[0m", "Green", "Lime", false},
129 | {"Yellow Bold & Bright Background", "\u001b[1;103m😀\033[0m", "😀", "Yellow", false},
130 | {"Blue Bold & Bright Background", "\u001b[1;104m123\033[0m", "123", "Blue", false},
131 | {"Purple Bold & Bright Background", "\u001b[1;105m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
132 | {"Cyan Bold & Bright Background", "\033[1;106m😀\033[0m", "😀", "Aqua", false},
133 | {"White Bold & Bright Background", "\u001b[1;107m[0;37m\033[0m", "[0;37m", "White", false},
134 | {"Black Bright Background", "\u001b[100mHello World\033[0m", "Hello World", "Grey", false},
135 | {"Red Bright Background", "\u001b[101mHello World\033[0m", "Hello World", "Red", false},
136 | {"Green Bright Background", "\u001b[102mGreen\033[0m", "Green", "Lime", false},
137 | {"Yellow Bright Background", "\u001b[103m😀\033[0m", "😀", "Yellow", false},
138 | {"Blue Bright Background", "\u001b[104m123\033[0m", "123", "Blue", false},
139 | {"Purple Bright Background", "\u001b[105m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
140 | {"Cyan Bright Background", "\033[106m😀\033[0m", "😀", "Aqua", false},
141 | {"White Bright Background", "\u001b[107m[0;37m\033[0m", "[0;37m", "White", false},
142 |
143 | {"Blank", "", "", "", false},
144 | {"Emoji", "😀👩🏽🔧", "😀👩🏽🔧", "", false},
145 | {"Spaces", " ", " ", "", false},
146 | {"Bad code", "\u001b[1 ", "", "", true},
147 | }
148 | for _, tt := range tests {
149 | t.Run(tt.name, func(t *testing.T) {
150 | got, err := Parse(tt.input)
151 | is2.Equal(err != nil, tt.wantErr)
152 | expectedLength := 1
153 | if tt.wantErr {
154 | expectedLength = 0
155 | }
156 | is2.Equal(len(got), expectedLength)
157 | if expectedLength == 1 {
158 | if len(tt.wantColor) > 0 {
159 | if got[0].FgCol != nil {
160 | is2.Equal(got[0].FgCol.Name, tt.wantColor)
161 | } else {
162 | is2.Equal(got[0].BgCol.Name, tt.wantColor)
163 | }
164 | }
165 | }
166 | })
167 | }
168 | }
169 |
170 | func TestParseAnsi16SingleBGColour(t *testing.T) {
171 | is2 := is.New(t)
172 | tests := []struct {
173 | name string
174 | input string
175 | wantText string
176 | wantColor string
177 | wantErr bool
178 | }{
179 | {"No formatting", "Hello World", "Hello World", "", false},
180 | {"Black", "\u001b[0;40mHello World\033[0m", "Hello World", "Black", false},
181 | {"Red", "\u001b[0;41mHello World\033[0m", "Hello World", "Maroon", false},
182 | {"Green", "\u001b[0;42mGreen\033[0m", "Green", "Green", false},
183 | {"Yellow", "\u001b[0;43m😀\033[0m", "😀", "Olive", false},
184 | {"Blue", "\u001b[0;44m123\033[0m", "123", "Navy", false},
185 | {"Purple", "\u001b[0;45m👩🏽🔧\u001B[0m", "👩🏽🔧", "Purple", false},
186 | {"Cyan", "\033[0;46m😀\033[0m", "😀", "Teal", false},
187 | {"White", "\u001b[0;47m[0;47m\033[0m", "[0;47m", "Silver", false},
188 | {"Black Bold", "\u001b[1;40mHello World\033[0m", "Hello World", "Grey", false},
189 | {"Red Bold", "\u001b[1;41mHello World\033[0m", "Hello World", "Red", false},
190 | {"Green Bold", "\u001b[1;42mGreen\033[0m", "Green", "Lime", false},
191 | {"Yellow Bold", "\u001b[1;43m😀\033[0m", "😀", "Yellow", false},
192 | {"Blue Bold", "\u001b[1;44m123\033[0m", "123", "Blue", false},
193 | {"Purple Bold", "\u001b[1;45m👩🏽🔧\u001B[0m", "👩🏽🔧", "Fuchsia", false},
194 | {"Cyan Bold", "\033[1;46m😀\033[0m", "😀", "Aqua", false},
195 | {"White Bold", "\u001b[1;47m[0;47m\033[0m", "[0;47m", "White", false},
196 | {"Pre text", "Hello\u001b[0m", "Hello", "", false},
197 | {"Blank", "", "", "", false},
198 | {"Emoji", "😀👩🏽🔧", "😀👩🏽🔧", "", false},
199 | {"Spaces", " ", " ", "", false},
200 | {"Bad code", "\u001b[1 ", "", "", true},
201 | {"No colour", "\033[m\033[40m \033[0m", " ", "", false},
202 | }
203 | for _, tt := range tests {
204 | t.Run(tt.name, func(t *testing.T) {
205 | got, err := Parse(tt.input)
206 | is2.Equal(err != nil, tt.wantErr)
207 | expectedLength := 1
208 | if tt.wantErr {
209 | expectedLength = 0
210 | }
211 | is2.Equal(len(got), expectedLength)
212 | if expectedLength == 1 {
213 | if len(tt.wantColor) > 0 {
214 | is2.True(got[0].BgCol != nil)
215 | is2.Equal(got[0].BgCol.Name, tt.wantColor)
216 | }
217 | }
218 | })
219 | }
220 | }
221 |
222 | func TestParseAnsi16MultiColour(t *testing.T) {
223 | is2 := is.New(t)
224 | tests := []struct {
225 | name string
226 | input string
227 | want []*StyledText
228 | wantErr bool
229 | }{
230 | {"Black & Red", "\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
231 | {Label: "Hello World", FgCol: &Col{Name: "Black"}, Offset: 0, Len: 18},
232 | {Label: "Hello World", FgCol: &Col{Name: "Maroon"}, Offset: 18, Len: 22},
233 | }, false},
234 | {"Text then Black & Red", "This is great!\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
235 | {Label: "This is great!", Offset: 0, Len: 14},
236 | {Label: "Hello World", FgCol: &Col{Name: "Black"}, Offset: 14, Len: 18},
237 | {Label: "Hello World", FgCol: &Col{Name: "Maroon"}, Offset: 32, Len: 22},
238 | }, false},
239 | {"Text Reset then Black & Red", "This is great!\u001B[0m\u001B[0;30mHello World\u001B[0m\u001B[0;31mHello World\u001B[0m", []*StyledText{
240 | {Label: "This is great!", Offset: 0, Len: 14},
241 | {Label: "Hello World", FgCol: &Col{Name: "Black"}, Offset: 14, Len: 22},
242 | {Label: "Hello World", FgCol: &Col{Name: "Maroon"}, Offset: 36, Len: 22},
243 | }, false},
244 | {"Text Reset then Black & Red", "This is great!\u001B[0m", []*StyledText{
245 | {Label: "This is great!", Offset: 0, Len: 14},
246 | }, false},
247 | {"Black & Red no reset", "\u001B[0;30mHello World\u001B[0;31mHello World", []*StyledText{
248 | {Label: "Hello World", FgCol: &Col{Name: "Black"}, Offset: 0, Len: 18},
249 | {Label: "Hello World", FgCol: &Col{Name: "Maroon"}, Offset: 18, Len: 18},
250 | }, false},
251 | {"Black,space,Red", "\u001B[0;30mHello World\u001B[0m \u001B[0;31mHello World\u001B[0m", []*StyledText{
252 | {Label: "Hello World", FgCol: &Col{Name: "Black"}, Offset: 0, Len: 18},
253 | {Label: " ", Offset: 18, Len: 5},
254 | {Label: "Hello World", FgCol: &Col{Name: "Maroon"}, Offset: 23, Len: 18},
255 | }, false},
256 | {"Black,Red,Blue,Green underlined", "\033[4;30mBlack\u001B[0m\u001B[4;31mRed\u001B[0m\u001B[4;34mBlue\u001B[0m\u001B[4;32mGreen\u001B[0m", []*StyledText{
257 | {Label: "Black", FgCol: &Col{Name: "Black"}, Style: Underlined, Offset: 0, Len: 12},
258 | {Label: "Red", FgCol: &Col{Name: "Maroon"}, Style: Underlined, Offset: 12, Len: 14},
259 | {Label: "Blue", FgCol: &Col{Name: "Navy"}, Style: Underlined, Offset: 26, Len: 15},
260 | {Label: "Green", FgCol: &Col{Name: "Green"}, Style: Underlined, Offset: 41, Len: 16},
261 | }, false},
262 | {"Black,Red,Blue,Green bold", "\033[1;30mBlack\u001B[0m\u001B[1;31mRed\u001B[0m\u001B[1;34mBlue\u001B[0m\u001B[1;32mGreen\u001B[0m", []*StyledText{
263 | {Label: "Black", FgCol: &Col{Name: "Grey"}, Style: Bold, Offset: 0, Len: 12},
264 | {Label: "Red", FgCol: &Col{Name: "Red"}, Style: Bold, Offset: 12, Len: 14},
265 | {Label: "Blue", FgCol: &Col{Name: "Blue"}, Style: Bold, Offset: 26, Len: 15},
266 | {Label: "Green", FgCol: &Col{Name: "Lime"}, Style: Bold, Offset: 41, Len: 16},
267 | }, false},
268 | {"Green Feint & Yellow Italic", "\u001B[2;32m👩🏽🔧\u001B[0m\u001B[0;3;33m👩🏽🔧\u001B[0m", []*StyledText{
269 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Faint, Offset: 0, Len: len("👩🏽🔧") + 7},
270 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Italic, Offset: len("👩🏽🔧") + 7, Len: len("👩🏽🔧") + 13},
271 | }, false},
272 | {"Green Blinking & Yellow Inversed", "\u001B[5;32m👩🏽🔧\u001B[0m\u001B[0;7;33m👩🏽🔧\u001B[0m", []*StyledText{
273 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Blinking, Offset: 0, Len: len("👩🏽🔧") + 7},
274 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Inversed, Offset: len("👩🏽🔧") + 7, Len: len("👩🏽🔧") + 13},
275 | }, false},
276 | {"Green Invisible & Yellow Invisible & Strikethrough", "\u001B[8;32m👩🏽🔧\u001B[9;33m👩🏽🔧\u001B[0m", []*StyledText{
277 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Green"}, Style: Invisible, Offset: 0, Len: len("👩🏽🔧") + 7},
278 | {Label: "👩🏽🔧", FgCol: &Col{Name: "Olive"}, Style: Invisible | Strikethrough, Offset: len("👩🏽🔧") + 7, Len: len("👩🏽🔧") + 7},
279 | }, false},
280 | {"Red Foregraound & Black Background", "\u001b[1;31;40mHello World\033[0m", []*StyledText{
281 | {Label: "Hello World", FgCol: &Col{Name: "Red"}, BgCol: &Col{Name: "Black"}, Style: Bold, Offset: 0, Len: 21},
282 | }, false},
283 | }
284 | for _, tt := range tests {
285 | t.Run(tt.name, func(t *testing.T) {
286 | got, err := Parse(tt.input)
287 | is2.Equal(err != nil, tt.wantErr)
288 | for index, w := range tt.want {
289 | is2.Equal(got[index].Label, w.Label)
290 | if w.FgCol != nil {
291 | is2.Equal(got[index].FgCol.Name, w.FgCol.Name)
292 | }
293 | is2.Equal(got[index].Style, w.Style)
294 | is2.Equal(got[index].Offset, w.Offset)
295 | is2.Equal(got[index].Len, w.Len)
296 | }
297 | })
298 | }
299 | }
300 |
301 | func TestParseAnsi256(t *testing.T) {
302 | is2 := is.New(t)
303 | tests := []struct {
304 | name string
305 | input string
306 | want []*StyledText
307 | wantErr bool
308 | }{
309 | {"Grey93 & DarkViolet", "\u001B[38;5;255mGrey93\u001B[0m\u001B[38;5;128mDarkViolet\u001B[0m", []*StyledText{
310 | {Label: "Grey93", FgCol: &Col{Name: "Grey93"}},
311 | {Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}},
312 | }, false},
313 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;255mGrey93\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m", []*StyledText{
314 | {Label: "Grey93", FgCol: &Col{Name: "Grey93"}, Style: Bold},
315 | {Label: "DarkViolet", FgCol: &Col{Name: "DarkViolet"}, Style: Italic},
316 | }, false},
317 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;256mGrey93\u001B[0m", nil, true},
318 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;-1mGrey93\u001B[0m", nil, true},
319 | {"Bad No of Params", "\u001B[0;1;38;5mGrey93\u001B[0m", nil, true},
320 | {"Bad Params", "\u001B[0;1;38;fivemGrey93\u001B[0m", nil, true},
321 | {"Bad Params 2", "\u001B[0;1;38;3mGrey93\u001B[0m", nil, true},
322 | {"Bad Params 3", "\u001B[0;1;38;5;fivemGrey93\u001B[0m", nil, true},
323 | }
324 | for _, tt := range tests {
325 | t.Run(tt.name, func(t *testing.T) {
326 | got, err := Parse(tt.input)
327 | is2.Equal(err != nil, tt.wantErr)
328 | for index, w := range tt.want {
329 | is2.Equal(got[index].Label, w.Label)
330 | if w.FgCol != nil {
331 | is2.Equal(got[index].FgCol.Name, w.FgCol.Name)
332 | }
333 | is2.Equal(got[index].Style, w.Style)
334 | }
335 | })
336 | }
337 | }
338 |
339 | func TestRoundtripAnsi16(t *testing.T) {
340 | is2 := is.New(t)
341 | tests := []struct {
342 | name string
343 | input string
344 | }{
345 | {"No formatting", "Hello World"},
346 | {"Black", "\u001b[0;30mHello World\033[0m"},
347 | {"Red", "\u001b[0;31mHello World\033[0m"},
348 | {"Green", "\u001b[0;32mGreen\033[0m"},
349 | {"Yellow", "\u001b[0;33m😀\033[0m"},
350 | {"Blue", "\u001b[0;34m123\033[0m"},
351 | {"Purple", "\u001b[0;35m👩🏽🔧\u001B[0m"},
352 | {"Cyan", "\033[0;36m😀\033[0m"},
353 | {"White", "\u001b[0;37m[0;37m\033[0m"},
354 | {"Black Bold", "\u001b[0;1;30mHello World\033[0m"},
355 | {"Red Bold", "\u001b[0;1;31mHello World\033[0m"},
356 | {"Green Bold", "\u001b[0;1;32mGreen\033[0m"},
357 | {"Yellow Bold", "\u001b[0;1;33m😀\033[0m"},
358 | {"Blue Bold", "\u001b[0;1;34m123\033[0m"},
359 | {"Purple Bold", "\u001b[0;1;35m👩🏽🔧\u001B[0m"},
360 | {"Cyan Bold", "\033[0;1;36m😀\033[0m"},
361 | {"White Bold", "\u001b[0;1;37m[0;37m\033[0m"},
362 | {"Black Bright", "\u001b[0;90mHello World\033[0m"},
363 | {"Red Bright", "\u001b[0;91mHello World\033[0m"},
364 | {"Green Bright", "\u001b[0;92mGreen\033[0m"},
365 | {"Yellow Bright", "\u001b[0;93m😀\033[0m"},
366 | {"Blue Bright", "\u001b[0;94m123\033[0m"},
367 | {"Purple Bright", "\u001b[0;95m👩🏽🔧\u001B[0m"},
368 | {"Cyan Bright", "\033[0;96m😀\033[0m"},
369 | {"White Bright", "\u001b[0;97m[0;37m\033[0m"},
370 | {"Black Bright Background", "\u001b[0;100mHello World\033[0m"},
371 | {"Red Bright Background", "\u001b[0;101mHello World\033[0m"},
372 | {"Green Bright Background", "\u001b[0;102mGreen\033[0m"},
373 | {"Yellow Bright Background", "\u001b[0;103m😀\033[0m"},
374 | {"Blue Bright Background", "\u001b[0;104m123\033[0m"},
375 | {"Purple Bright Background", "\u001b[0;105m👩🏽🔧\u001B[0m"},
376 | {"Cyan Bright Background", "\033[0;106m😀\033[0m"},
377 | {"White Bright Background", "\u001b[0;107m[0;37m\033[0m"},
378 | {"Blank", ""},
379 | {"Emoji", "😀👩🏽🔧"},
380 | {"Spaces", " "},
381 | }
382 | for _, tt := range tests {
383 | t.Run(tt.name, func(t *testing.T) {
384 | got, err := Parse(tt.input)
385 | is2.NoErr(err)
386 | output := String(got)
387 | is2.Equal(output, tt.input)
388 | })
389 | }
390 | }
391 |
392 | func TestRoundtripAnsi256(t *testing.T) {
393 | is2 := is.New(t)
394 | tests := []struct {
395 | name string
396 | input string
397 | }{
398 | {"Grey93 & DarkViolet", "\u001B[0;38;5;255mGrey93\u001B[0m\u001B[0;38;5;128mDarkViolet\u001B[0m"},
399 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;38;5;255mGrey93\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m"},
400 | {"White", "\u001B[0;38;5;15mWhite\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m"},
401 | {"White Bold", "\u001B[0;1;38;5;15mWhite\u001B[0m\u001B[0;3;38;5;128mDarkViolet\u001B[0m"},
402 | }
403 | for _, tt := range tests {
404 | t.Run(tt.name, func(t *testing.T) {
405 | got, err := Parse(tt.input)
406 | is2.NoErr(err)
407 | output := String(got)
408 | is2.Equal(output, tt.input)
409 | })
410 | }
411 | }
412 |
413 | func TestParseAnsiBG256(t *testing.T) {
414 | is2 := is.New(t)
415 | tests := []struct {
416 | name string
417 | input string
418 | want []*StyledText
419 | wantErr bool
420 | }{
421 | {"Grey93 & DarkViolet", "\u001B[48;5;255mGrey93\u001B[0m\u001B[48;5;128mDarkViolet\u001B[0m", []*StyledText{
422 | {Label: "Grey93", BgCol: &Col{Name: "Grey93"}},
423 | {Label: "DarkViolet", BgCol: &Col{Name: "DarkViolet"}},
424 | }, false},
425 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;48;5;255mGrey93\u001B[0m\u001B[0;3;48;5;128mDarkViolet\u001B[0m", []*StyledText{
426 | {Label: "Grey93", BgCol: &Col{Name: "Grey93"}, Style: Bold},
427 | {Label: "DarkViolet", BgCol: &Col{Name: "DarkViolet"}, Style: Italic},
428 | }, false},
429 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;48;5;256mGrey93\u001B[0m", nil, true},
430 | {"Grey93 Bold & DarkViolet Italic", "\u001B[0;1;48;5;-1mGrey93\u001B[0m", nil, true},
431 | {"Bad No of Params", "\u001B[0;1;48;5mGrey93\u001B[0m", nil, true},
432 | {"Bad Params", "\u001B[0;1;48;fivemGrey93\u001B[0m", nil, true},
433 | {"Bad Params 2", "\u001B[0;1;48;3mGrey93\u001B[0m", nil, true},
434 | {"Bad Params 2", "\u001B[0;1;50;3mGrey93\u001B[0m", nil, true},
435 | }
436 | for _, tt := range tests {
437 | t.Run(tt.name, func(t *testing.T) {
438 | got, err := Parse(tt.input)
439 | is2.Equal(err != nil, tt.wantErr)
440 | for index, w := range tt.want {
441 | is2.Equal(got[index].Label, w.Label)
442 | if w.FgCol != nil {
443 | is2.Equal(got[index].BgCol.Name, w.BgCol.Name)
444 | }
445 | is2.Equal(got[index].Style, w.Style)
446 | }
447 | })
448 | }
449 | }
450 |
451 | func TestParseAnsiTrueColor(t *testing.T) {
452 | is2 := is.New(t)
453 | tests := []struct {
454 | name string
455 | input string
456 | want []*StyledText
457 | wantErr bool
458 | }{
459 | {"Red", "\u001B[38;2;255;0;0mRed\u001B[0m", []*StyledText{
460 | {Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
461 | }, false},
462 | {"Red BG", "\u001B[48;2;255;0;0mRed\u001B[0m", []*StyledText{
463 | {Label: "Red", BgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
464 | }, false},
465 | {"Red, text, Green", "\u001B[38;2;255;0;0mRed\u001B[0mI am plain text\u001B[38;2;0;255;0mGreen\u001B[0m", []*StyledText{
466 | {Label: "Red", FgCol: &Col{Rgb: Rgb{255, 0, 0}, Hex: "#ff0000"}},
467 | {Label: "I am plain text"},
468 | {Label: "Green", FgCol: &Col{Rgb: Rgb{0, 255, 0}, Hex: "#00ff00"}},
469 | }, false},
470 | {"Bad 1", "\u001B[38;2;256;0;0mRed\u001B[0m", nil, true},
471 | {"Bad 2", "\u001B[38;2;-1;0;0mRed\u001B[0m", nil, true},
472 | {"Bad no of params", "\u001B[38;2;0;0mRed\u001B[0m", nil, true},
473 | {"Bad params", "\u001B[38;2;0;onemRed\u001B[0m", nil, true},
474 | {"Bad params 2", "\u001B[38;2;red;0;0mRed\u001B[0m", nil, true},
475 | {"Bad params 3", "\u001B[38;2;0;red;0mRed\u001B[0m", nil, true},
476 | {"Bad params 4", "\u001B[38;2;0;0;redmRed\u001B[0m", nil, true},
477 | {"Bad params 4", "\u001B[38;3;0;0;redmRed\u001B[0m", nil, true},
478 | }
479 | for _, tt := range tests {
480 | t.Run(tt.name, func(t *testing.T) {
481 | got, err := Parse(tt.input)
482 | is2.Equal(err != nil, tt.wantErr)
483 | for index, w := range tt.want {
484 | is2.Equal(got[index].Label, w.Label)
485 | if w.FgCol != nil {
486 | is2.Equal(got[index].FgCol.Hex, w.FgCol.Hex)
487 | is2.Equal(got[index].FgCol.Rgb, w.FgCol.Rgb)
488 | }
489 | if w.BgCol != nil {
490 | is2.Equal(got[index].BgCol.Hex, w.BgCol.Hex)
491 | is2.Equal(got[index].BgCol.Rgb, w.BgCol.Rgb)
492 | }
493 | is2.Equal(got[index].Style, w.Style)
494 | }
495 | })
496 | }
497 | }
498 |
499 | func TestParseAnsiWithOptions(t *testing.T) {
500 | is2 := is.New(t)
501 | tests := []struct {
502 | name string
503 | input string
504 | options []ParseOption
505 | want []*StyledText
506 | wantErr bool
507 | }{
508 | {
509 | "Unexpected code errored", "\u001b[0;99mHello World\033[0m",
510 | nil, nil, true,
511 | },
512 | {
513 | "Unexpected code ignored", "\u001b[0;99mHello World\033[0m",
514 | []ParseOption{WithIgnoreInvalidCodes()},
515 | []*StyledText{{Label: "Hello World"}}, false,
516 | },
517 | {
518 | "Foreground code default", "\u001b[0;39mHello World\033[0m", nil,
519 | []*StyledText{{Label: "Hello World", FgCol: Cols[7]}}, false,
520 | },
521 | {
522 | "Foreground code specified", "\u001b[0;39mHello World\033[0m",
523 | []ParseOption{WithDefaultForegroundColor("35")},
524 | []*StyledText{{Label: "Hello World", FgCol: Cols[5]}}, false,
525 | },
526 | {
527 | "Background code default", "\u001b[0;49mHello World\033[0m", nil,
528 | []*StyledText{{Label: "Hello World", BgCol: Cols[0]}}, false,
529 | },
530 | {
531 | "Background code specified", "\u001b[0;49mHello World\033[0m",
532 | []ParseOption{WithDefaultBackgroundColor("36")},
533 | []*StyledText{{Label: "Hello World", BgCol: Cols[6]}}, false,
534 | },
535 | }
536 | for _, tt := range tests {
537 | t.Run(tt.name, func(t *testing.T) {
538 | got, err := Parse(tt.input, tt.options...)
539 | is2.Equal(err != nil, tt.wantErr)
540 | for index, w := range tt.want {
541 | is2.Equal(got[index].Label, w.Label)
542 | if w.FgCol != nil {
543 | is2.Equal(got[index].FgCol.Name, w.FgCol.Name)
544 | }
545 | if w.BgCol != nil {
546 | is2.Equal(got[index].BgCol.Name, w.BgCol.Name)
547 | }
548 | is2.Equal(got[index].Style, w.Style)
549 | }
550 | })
551 | }
552 | }
553 |
554 | func TestHasEscapeCodes(t *testing.T) {
555 | is2 := is.New(t)
556 | tests := []struct {
557 | name string
558 | input string
559 | want bool
560 | }{
561 | {"yes", "\u001B[0;30mHello World\033[0m\u001B[0;31mHello World\u001B[0m", true},
562 | {"no", "This is great!", false},
563 | }
564 | for _, tt := range tests {
565 | t.Run(tt.name, func(t *testing.T) {
566 | got := HasEscapeCodes(tt.input)
567 | is2.Equal(got, tt.want)
568 | })
569 | }
570 | }
571 |
572 | func TestString(t *testing.T) {
573 | is2 := is.New(t)
574 | tests := []struct {
575 | name string
576 | input []*StyledText
577 | want string
578 | }{
579 | {"Blank", []*StyledText{}, ""},
580 | {"ANSI16 Fg", []*StyledText{{Label: "Red", FgCol: Cols[1]}}, "\033[0;31mRed\033[0m"},
581 | {"ANSI16 Fg Bold", []*StyledText{{Label: "Red", FgCol: Cols[1], Style: Bold}}, "\033[0;1;31mRed\033[0m"},
582 | {"ANSI16 Fg Strikethrough", []*StyledText{{Label: "Red", FgCol: Cols[1], Style: Strikethrough}}, "\033[0;9;31mRed\033[0m"},
583 | {"ANSI16 Fg Bold & Italic", []*StyledText{{Label: "Red", FgCol: Cols[1], Style: Bold | Italic}}, "\033[0;1;3;31mRed\033[0m"},
584 | {"ANSI16 Bg", []*StyledText{{Label: "Black", BgCol: Cols[0]}}, "\033[0;40mBlack\033[0m"},
585 | {"ANSI16 Mixed", []*StyledText{{Label: "Mixed", FgCol: Cols[1], BgCol: Cols[0]}}, "\033[0;31;40mMixed\033[0m"},
586 | {"ANSI256 Fg", []*StyledText{{ColourMode: TwoFiveSix, Label: "Dark Blue", FgCol: Cols[18]}}, "\033[0;38;5;18mDark Blue\033[0m"},
587 | {"ANSI256 Fg Bold", []*StyledText{{ColourMode: TwoFiveSix, Label: "Dark Blue", FgCol: Cols[18], Style: Bold}}, "\033[0;1;38;5;18mDark Blue\033[0m"},
588 | {"ANSI256 Bg", []*StyledText{{ColourMode: TwoFiveSix, Label: "Dark Blue", BgCol: Cols[18]}}, "\033[0;48;5;18mDark Blue\033[0m"},
589 | {"ANSI256 Bg Bold", []*StyledText{{ColourMode: TwoFiveSix, Label: "Dark Blue", BgCol: Cols[18], Style: Bold}}, "\033[0;1;48;5;18mDark Blue\033[0m"},
590 | {"Truecolor Fg", []*StyledText{{ColourMode: TrueColour, Label: "TrueColor!", FgCol: &Col{Id: 256, Rgb: Rgb{R: 128, G: 127, B: 126}}}}, "\033[0;38;2;128;127;126mTrueColor!\033[0m"},
591 | {"Truecolor Fg Bold", []*StyledText{{ColourMode: TrueColour, Label: "TrueColor!", FgCol: &Col{Id: 256, Rgb: Rgb{R: 128, G: 127, B: 126}}, Style: Bold}}, "\033[0;1;38;2;128;127;126mTrueColor!\033[0m"},
592 | {"Truecolor Bg", []*StyledText{{ColourMode: TrueColour, Label: "TrueColor!", BgCol: &Col{Id: 256, Rgb: Rgb{R: 128, G: 127, B: 126}}}}, "\033[0;48;2;128;127;126mTrueColor!\033[0m"},
593 | {"Truecolor Bg Bold", []*StyledText{{ColourMode: TrueColour, Label: "TrueColor!", BgCol: &Col{Id: 256, Rgb: Rgb{R: 128, G: 127, B: 126}}, Style: Bold}}, "\033[0;1;48;2;128;127;126mTrueColor!\033[0m"},
594 | {"Truecolor Mixed", []*StyledText{{ColourMode: TrueColour, Label: "TrueColor!", FgCol: &Col{Id: 256, Rgb: Rgb{R: 90, G: 91, B: 92}}, BgCol: &Col{Id: 256, Rgb: Rgb{R: 128, G: 127, B: 126}}, Style: Bold | Faint | Underlined | Strikethrough | Italic | Invisible | Blinking | Inversed}}, "\033[0;1;2;3;4;5;7;8;9;38;2;90;91;92;48;2;128;127;126mTrueColor!\033[0m"},
595 | }
596 | for _, tt := range tests {
597 | t.Run(tt.name, func(t *testing.T) {
598 | got := String(tt.input)
599 | is2.Equal(got, tt.want)
600 | })
601 | }
602 | }
603 |
604 | func TestTruncate(t *testing.T) {
605 | is2 := is.New(t)
606 | tests := []struct {
607 | name string
608 | input string
609 | maxChars int
610 | want string
611 | wantErr bool
612 | }{
613 | {"No formatting", "Hello World", 11, "Hello World", false},
614 | {"No formatting truncated", "Hello World", 5, "Hello", false},
615 | {"No formatting many chars", "Hello World", 50, "Hello World", false},
616 | {"Black", "\u001b[0;30mHello World\033[0m", 5, "\u001B[0;30mHello\u001B[0m", false},
617 | {"Red Bold", "\u001b[0;1;31mHello World\033[0m", 5, "\033[0;1;31mHello\033[0m", false},
618 | {"Red Bold & Black", "\u001b[0;1;31mI am Red\033[0m\u001B[0;30mI am Black\u001B[0m", 12, "\u001B[0;1;31mI am Red\u001B[0m\u001B[0;30mI am\u001B[0m", false},
619 | {"Red Bold & text & Black", "\u001b[0;1;31mI am Red\033[0m and \u001B[0;30mI am Black\u001B[0m", 17, "\u001B[0;1;31mI am Red\u001B[0m and \u001B[0;30mI am\u001B[0m", false},
620 | {"Emoji", "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", 1, "\u001B[0;1;31m😀\u001B[0m", false},
621 | {"Emoji 2", "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", 2, "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", false},
622 | {"Emoji 3", "\u001B[0;1;31m😀👩🏽🔧😀\u001B[0m", 2, "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", false},
623 | {"Bad", "\033[44;32;12", 10, "", true},
624 |
625 | //{"Spaces", " ", " ", "", false},
626 | //{"Bad code", "\u001b[1 ", "", "", true},
627 | }
628 | for _, tt := range tests {
629 | t.Run(tt.name, func(t *testing.T) {
630 | got, err := Truncate(tt.input, tt.maxChars)
631 | is2.Equal(err != nil, tt.wantErr)
632 | is2.Equal(got, tt.want)
633 | })
634 | }
635 | }
636 |
637 | func TestCleanse(t *testing.T) {
638 | is2 := is.New(t)
639 | tests := []struct {
640 | name string
641 | input string
642 | want string
643 | wantErr bool
644 | }{
645 | {"Blank", "", "", false},
646 | {"No formatting", "Hello World", "Hello World", false},
647 | {"ANSI16 Fg", "\033[0;31mRed\033[0m", "Red", false},
648 | {"Black", "\u001b[0;30mHello World\033[0m", "Hello World", false},
649 | {"Red Bold & Black", "\u001b[0;1;31mI am Red\033[0m & \u001B[0;30mI am Black\u001B[0m", "I am Red & I am Black", false},
650 | {"Red All the styles", "\u001b[0;1;2;3;4;5;7;8;9;31mI am Red\033[0m", "I am Red", false},
651 | {"Emoji", "\u001B[0;1;31m😀\u001B[0m", "😀", false},
652 | {"Emoji 2", "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", "😀👩🏽🔧", false},
653 | {"Bad", "\033[44;32;12", "", true},
654 | }
655 | for _, tt := range tests {
656 | t.Run(tt.name, func(t *testing.T) {
657 | got, err := Cleanse(tt.input)
658 | is2.Equal(err != nil, tt.wantErr)
659 | is2.Equal(got, tt.want)
660 | })
661 | }
662 | }
663 |
664 | func TestLength(t *testing.T) {
665 | is2 := is.New(t)
666 | tests := []struct {
667 | name string
668 | input string
669 | want int
670 | wantErr bool
671 | }{
672 | {"Blank", "", 0, false},
673 | {"No formatting", "Hello World", 11, false},
674 | {"ANSI16 Fg", "\033[0;31mRed\033[0m", 3, false},
675 | {"Zero chats", "\u001b[0;30m\033[0m", 0, false},
676 | {"Red Bold & Black", "\u001b[0;1;31mI am Red\033[0m & \u001B[0;30mI am Black\u001B[0m", 21, false},
677 | {"Red All the styles", "\u001b[0;1;2;3;4;5;7;8;9;31mI am Red\033[0m", 8, false},
678 | {"Emoji", "\u001B[0;1;31m😀\u001B[0m", 1, false},
679 | {"Emoji 2", "\u001B[0;1;31m😀👩🏽🔧\u001B[0m", 2, false},
680 | {"Bad", "\033[44;32;12", -1, true},
681 | }
682 | for _, tt := range tests {
683 | t.Run(tt.name, func(t *testing.T) {
684 | got, err := Length(tt.input)
685 | is2.Equal(err != nil, tt.wantErr)
686 | is2.Equal(got, tt.want)
687 | })
688 | }
689 | }
690 |
691 | func TestStripLeadingZeros(t *testing.T) {
692 | is2 := is.New(t)
693 | tests := []struct {
694 | name string
695 | input string
696 | want string
697 | }{
698 | {"Blank", "", ""},
699 | {"One rune not 0", "4", "4"},
700 | {"Only 0", "0", "0"},
701 | {"Multi-digit non-0", "35", "35"},
702 | {"Two-digit leading 0", "05", "5"},
703 | {"Three-digit leading 0", "045", "45"},
704 | }
705 | for _, tt := range tests {
706 | t.Run(tt.name, func(t *testing.T) {
707 | got := stripLeadingZeros(tt.input)
708 | is2.Equal(got, tt.want)
709 | })
710 | }
711 | }
712 |
--------------------------------------------------------------------------------
/cols.go:
--------------------------------------------------------------------------------
1 | package ansi
2 |
3 | // Rgb represents an RGB colour value
4 | // with 8bits per channel
5 | type Rgb struct {
6 | R uint8 `json:"r"`
7 | G uint8 `json:"g"`
8 | B uint8 `json:"b"`
9 | }
10 |
11 | // Hsl represents the HSL value of the colour
12 | type Hsl struct {
13 | H float64 `json:"h"`
14 | S float64 `json:"s"`
15 | L float64 `json:"l"`
16 | }
17 |
18 | // Col represents a colour value.
19 | // The Id is the ANSI colour ID.
20 | type Col struct {
21 | Id int `json:"id"`
22 | Hex string `json:"hex"`
23 | Rgb Rgb `json:"rgb"`
24 | Hsl Hsl `json:"hsl"`
25 | Name string `json:"name"`
26 | }
27 |
28 | // Cols represents the default colour definitions
29 | // used by the library. This may be overridden.
30 | var Cols = []*Col{
31 | {
32 | Id: 0,
33 | Hex: "#000000",
34 | Rgb: Rgb{0, 0, 0},
35 | Hsl: Hsl{0, 0, 0},
36 | Name: "Black",
37 | },
38 | {
39 | Id: 1,
40 | Hex: "#800000",
41 | Rgb: Rgb{128, 0, 0},
42 | Hsl: Hsl{0, 100, 25},
43 | Name: "Maroon",
44 | },
45 | {
46 | Id: 2,
47 | Hex: "#008000",
48 | Rgb: Rgb{0, 128, 0},
49 | Hsl: Hsl{120, 100, 25},
50 | Name: "Green",
51 | },
52 | {
53 | Id: 3,
54 | Hex: "#808000",
55 | Rgb: Rgb{128, 128, 0},
56 | Hsl: Hsl{60, 100, 25},
57 | Name: "Olive",
58 | },
59 | {
60 | Id: 4,
61 | Hex: "#000080",
62 | Rgb: Rgb{0, 0, 128},
63 | Hsl: Hsl{240, 100, 25},
64 | Name: "Navy",
65 | },
66 | {
67 | Id: 5,
68 | Hex: "#800080",
69 | Rgb: Rgb{128, 0, 128},
70 | Hsl: Hsl{300, 100, 25},
71 | Name: "Purple",
72 | },
73 | {
74 | Id: 6,
75 | Hex: "#008080",
76 | Rgb: Rgb{0, 128, 128},
77 | Hsl: Hsl{180, 100, 25},
78 | Name: "Teal",
79 | },
80 | {
81 | Id: 7,
82 | Hex: "#c0c0c0",
83 | Rgb: Rgb{192, 192, 192},
84 | Hsl: Hsl{0, 0, 75},
85 | Name: "Silver",
86 | },
87 | {
88 | Id: 8,
89 | Hex: "#808080",
90 | Rgb: Rgb{128, 128, 128},
91 | Hsl: Hsl{0, 0, 50},
92 | Name: "Grey",
93 | },
94 | {
95 | Id: 9,
96 | Hex: "#ff0000",
97 | Rgb: Rgb{255, 0, 0},
98 | Hsl: Hsl{0, 100, 50},
99 | Name: "Red",
100 | },
101 | {
102 | Id: 10,
103 | Hex: "#00ff00",
104 | Rgb: Rgb{0, 255, 0},
105 | Hsl: Hsl{120, 100, 50},
106 | Name: "Lime",
107 | },
108 | {
109 | Id: 11,
110 | Hex: "#ffff00",
111 | Rgb: Rgb{255, 255, 0},
112 | Hsl: Hsl{60, 100, 50},
113 | Name: "Yellow",
114 | },
115 | {
116 | Id: 12,
117 | Hex: "#0000ff",
118 | Rgb: Rgb{0, 0, 255},
119 | Hsl: Hsl{240, 100, 50},
120 | Name: "Blue",
121 | },
122 | {
123 | Id: 13,
124 | Hex: "#ff00ff",
125 | Rgb: Rgb{255, 0, 255},
126 | Hsl: Hsl{300, 100, 50},
127 | Name: "Fuchsia",
128 | },
129 | {
130 | Id: 14,
131 | Hex: "#00ffff",
132 | Rgb: Rgb{0, 255, 255},
133 | Hsl: Hsl{180, 100, 50},
134 | Name: "Aqua",
135 | },
136 | {
137 | Id: 15,
138 | Hex: "#ffffff",
139 | Rgb: Rgb{255, 255, 255},
140 | Hsl: Hsl{0, 0, 100},
141 | Name: "White",
142 | },
143 | {
144 | Id: 16,
145 | Hex: "#000000",
146 | Rgb: Rgb{0, 0, 0},
147 | Hsl: Hsl{0, 0, 0},
148 | Name: "Grey0",
149 | },
150 | {
151 | Id: 17,
152 | Hex: "#00005f",
153 | Rgb: Rgb{0, 0, 95},
154 | Hsl: Hsl{240, 100, 18},
155 | Name: "NavyBlue",
156 | },
157 | {
158 | Id: 18,
159 | Hex: "#000087",
160 | Rgb: Rgb{0, 0, 135},
161 | Hsl: Hsl{240, 100, 26},
162 | Name: "DarkBlue",
163 | },
164 | {
165 | Id: 19,
166 | Hex: "#0000af",
167 | Rgb: Rgb{0, 0, 175},
168 | Hsl: Hsl{240, 100, 34},
169 | Name: "Blue3",
170 | },
171 | {
172 | Id: 20,
173 | Hex: "#0000d7",
174 | Rgb: Rgb{0, 0, 215},
175 | Hsl: Hsl{240, 100, 42},
176 | Name: "Blue3",
177 | },
178 | {
179 | Id: 21,
180 | Hex: "#0000ff",
181 | Rgb: Rgb{0, 0, 255},
182 | Hsl: Hsl{240, 100, 50},
183 | Name: "Blue1",
184 | },
185 | {
186 | Id: 22,
187 | Hex: "#005f00",
188 | Rgb: Rgb{0, 95, 0},
189 | Hsl: Hsl{120, 100, 18},
190 | Name: "DarkGreen",
191 | },
192 | {
193 | Id: 23,
194 | Hex: "#005f5f",
195 | Rgb: Rgb{0, 95, 95},
196 | Hsl: Hsl{180, 100, 18},
197 | Name: "DeepSkyBlue4",
198 | },
199 | {
200 | Id: 24,
201 | Hex: "#005f87",
202 | Rgb: Rgb{0, 95, 135},
203 | Hsl: Hsl{197.777777777778, 100, 26},
204 | Name: "DeepSkyBlue4",
205 | },
206 | {
207 | Id: 25,
208 | Hex: "#005faf",
209 | Rgb: Rgb{0, 95, 175},
210 | Hsl: Hsl{207.428571428571, 100, 34},
211 | Name: "DeepSkyBlue4",
212 | },
213 | {
214 | Id: 26,
215 | Hex: "#005fd7",
216 | Rgb: Rgb{0, 95, 215},
217 | Hsl: Hsl{213.488372093023, 100, 42},
218 | Name: "DodgerBlue3",
219 | },
220 | {
221 | Id: 27,
222 | Hex: "#005fff",
223 | Rgb: Rgb{0, 95, 255},
224 | Hsl: Hsl{217.647058823529, 100, 50},
225 | Name: "DodgerBlue2",
226 | },
227 | {
228 | Id: 28,
229 | Hex: "#008700",
230 | Rgb: Rgb{0, 135, 0},
231 | Hsl: Hsl{120, 100, 26},
232 | Name: "Green4",
233 | },
234 | {
235 | Id: 29,
236 | Hex: "#00875f",
237 | Rgb: Rgb{0, 135, 95},
238 | Hsl: Hsl{162.222222222222, 100, 26},
239 | Name: "SpringGreen4",
240 | },
241 | {
242 | Id: 30,
243 | Hex: "#008787",
244 | Rgb: Rgb{0, 135, 135},
245 | Hsl: Hsl{180, 100, 26},
246 | Name: "Turquoise4",
247 | },
248 | {
249 | Id: 31,
250 | Hex: "#0087af",
251 | Rgb: Rgb{0, 135, 175},
252 | Hsl: Hsl{193.714285714286, 100, 34},
253 | Name: "DeepSkyBlue3",
254 | },
255 | {
256 | Id: 32,
257 | Hex: "#0087d7",
258 | Rgb: Rgb{0, 135, 215},
259 | Hsl: Hsl{202.325581395349, 100, 42},
260 | Name: "DeepSkyBlue3",
261 | },
262 | {
263 | Id: 33,
264 | Hex: "#0087ff",
265 | Rgb: Rgb{0, 135, 255},
266 | Hsl: Hsl{208.235294117647, 100, 50},
267 | Name: "DodgerBlue1",
268 | },
269 | {
270 | Id: 34,
271 | Hex: "#00af00",
272 | Rgb: Rgb{0, 175, 0},
273 | Hsl: Hsl{120, 100, 34},
274 | Name: "Green3",
275 | },
276 | {
277 | Id: 35,
278 | Hex: "#00af5f",
279 | Rgb: Rgb{0, 175, 95},
280 | Hsl: Hsl{152.571428571429, 100, 34},
281 | Name: "SpringGreen3",
282 | },
283 | {
284 | Id: 36,
285 | Hex: "#00af87",
286 | Rgb: Rgb{0, 175, 135},
287 | Hsl: Hsl{166.285714285714, 100, 34},
288 | Name: "DarkCyan",
289 | },
290 | {
291 | Id: 37,
292 | Hex: "#00afaf",
293 | Rgb: Rgb{0, 175, 175},
294 | Hsl: Hsl{180, 100, 34},
295 | Name: "LightSeaGreen",
296 | },
297 | {
298 | Id: 38,
299 | Hex: "#00afd7",
300 | Rgb: Rgb{0, 175, 215},
301 | Hsl: Hsl{191.162790697674, 100, 42},
302 | Name: "DeepSkyBlue2",
303 | },
304 | {
305 | Id: 39,
306 | Hex: "#00afff",
307 | Rgb: Rgb{0, 175, 255},
308 | Hsl: Hsl{198.823529411765, 100, 50},
309 | Name: "DeepSkyBlue1",
310 | },
311 | {
312 | Id: 40,
313 | Hex: "#00d700",
314 | Rgb: Rgb{0, 215, 0},
315 | Hsl: Hsl{120, 100, 42},
316 | Name: "Green3",
317 | },
318 | {
319 | Id: 41,
320 | Hex: "#00d75f",
321 | Rgb: Rgb{0, 215, 95},
322 | Hsl: Hsl{146.511627906977, 100, 42},
323 | Name: "SpringGreen3",
324 | },
325 | {
326 | Id: 42,
327 | Hex: "#00d787",
328 | Rgb: Rgb{0, 215, 135},
329 | Hsl: Hsl{157.674418604651, 100, 42},
330 | Name: "SpringGreen2",
331 | },
332 | {
333 | Id: 43,
334 | Hex: "#00d7af",
335 | Rgb: Rgb{0, 215, 175},
336 | Hsl: Hsl{168.837209302326, 100, 42},
337 | Name: "Cyan3",
338 | },
339 | {
340 | Id: 44,
341 | Hex: "#00d7d7",
342 | Rgb: Rgb{0, 215, 215},
343 | Hsl: Hsl{180, 100, 42},
344 | Name: "DarkTurquoise",
345 | },
346 | {
347 | Id: 45,
348 | Hex: "#00d7ff",
349 | Rgb: Rgb{0, 215, 255},
350 | Hsl: Hsl{189.411764705882, 100, 50},
351 | Name: "Turquoise2",
352 | },
353 | {
354 | Id: 46,
355 | Hex: "#00ff00",
356 | Rgb: Rgb{0, 255, 0},
357 | Hsl: Hsl{120, 100, 50},
358 | Name: "Green1",
359 | },
360 | {
361 | Id: 47,
362 | Hex: "#00ff5f",
363 | Rgb: Rgb{0, 255, 95},
364 | Hsl: Hsl{142.352941176471, 100, 50},
365 | Name: "SpringGreen2",
366 | },
367 | {
368 | Id: 48,
369 | Hex: "#00ff87",
370 | Rgb: Rgb{0, 255, 135},
371 | Hsl: Hsl{151.764705882353, 100, 50},
372 | Name: "SpringGreen1",
373 | },
374 | {
375 | Id: 49,
376 | Hex: "#00ffaf",
377 | Rgb: Rgb{0, 255, 175},
378 | Hsl: Hsl{161.176470588235, 100, 50},
379 | Name: "MediumSpringGreen",
380 | },
381 | {
382 | Id: 50,
383 | Hex: "#00ffd7",
384 | Rgb: Rgb{0, 255, 215},
385 | Hsl: Hsl{170.588235294118, 100, 50},
386 | Name: "Cyan2",
387 | },
388 | {
389 | Id: 51,
390 | Hex: "#00ffff",
391 | Rgb: Rgb{0, 255, 255},
392 | Hsl: Hsl{180, 100, 50},
393 | Name: "Cyan1",
394 | },
395 | {
396 | Id: 52,
397 | Hex: "#5f0000",
398 | Rgb: Rgb{95, 0, 0},
399 | Hsl: Hsl{0, 100, 18},
400 | Name: "DarkRed",
401 | },
402 | {
403 | Id: 53,
404 | Hex: "#5f005f",
405 | Rgb: Rgb{95, 0, 95},
406 | Hsl: Hsl{300, 100, 18},
407 | Name: "DeepPink4",
408 | },
409 | {
410 | Id: 54,
411 | Hex: "#5f0087",
412 | Rgb: Rgb{95, 0, 135},
413 | Hsl: Hsl{282.222222222222, 100, 26},
414 | Name: "Purple4",
415 | },
416 | {
417 | Id: 55,
418 | Hex: "#5f00af",
419 | Rgb: Rgb{95, 0, 175},
420 | Hsl: Hsl{272.571428571429, 100, 34},
421 | Name: "Purple4",
422 | },
423 | {
424 | Id: 56,
425 | Hex: "#5f00d7",
426 | Rgb: Rgb{95, 0, 215},
427 | Hsl: Hsl{266.511627906977, 100, 42},
428 | Name: "Purple3",
429 | },
430 | {
431 | Id: 57,
432 | Hex: "#5f00ff",
433 | Rgb: Rgb{95, 0, 255},
434 | Hsl: Hsl{262.352941176471, 100, 50},
435 | Name: "BlueViolet",
436 | },
437 | {
438 | Id: 58,
439 | Hex: "#5f5f00",
440 | Rgb: Rgb{95, 95, 0},
441 | Hsl: Hsl{60, 100, 18},
442 | Name: "Orange4",
443 | },
444 | {
445 | Id: 59,
446 | Hex: "#5f5f5f",
447 | Rgb: Rgb{95, 95, 95},
448 | Hsl: Hsl{0, 0, 37},
449 | Name: "Grey37",
450 | },
451 | {
452 | Id: 60,
453 | Hex: "#5f5f87",
454 | Rgb: Rgb{95, 95, 135},
455 | Hsl: Hsl{240, 17, 45},
456 | Name: "MediumPurple4",
457 | },
458 | {
459 | Id: 61,
460 | Hex: "#5f5faf",
461 | Rgb: Rgb{95, 95, 175},
462 | Hsl: Hsl{240, 33, 52},
463 | Name: "SlateBlue3",
464 | },
465 | {
466 | Id: 62,
467 | Hex: "#5f5fd7",
468 | Rgb: Rgb{95, 95, 215},
469 | Hsl: Hsl{240, 60, 60},
470 | Name: "SlateBlue3",
471 | },
472 | {
473 | Id: 63,
474 | Hex: "#5f5fff",
475 | Rgb: Rgb{95, 95, 255},
476 | Hsl: Hsl{240, 100, 68},
477 | Name: "RoyalBlue1",
478 | },
479 | {
480 | Id: 64,
481 | Hex: "#5f8700",
482 | Rgb: Rgb{95, 135, 0},
483 | Hsl: Hsl{77.7777777777778, 100, 26},
484 | Name: "Chartreuse4",
485 | },
486 | {
487 | Id: 65,
488 | Hex: "#5f875f",
489 | Rgb: Rgb{95, 135, 95},
490 | Hsl: Hsl{120, 17, 45},
491 | Name: "DarkSeaGreen4",
492 | },
493 | {
494 | Id: 66,
495 | Hex: "#5f8787",
496 | Rgb: Rgb{95, 135, 135},
497 | Hsl: Hsl{180, 17, 45},
498 | Name: "PaleTurquoise4",
499 | },
500 | {
501 | Id: 67,
502 | Hex: "#5f87af",
503 | Rgb: Rgb{95, 135, 175},
504 | Hsl: Hsl{210, 33, 52},
505 | Name: "SteelBlue",
506 | },
507 | {
508 | Id: 68,
509 | Hex: "#5f87d7",
510 | Rgb: Rgb{95, 135, 215},
511 | Hsl: Hsl{220, 60, 60},
512 | Name: "SteelBlue3",
513 | },
514 | {
515 | Id: 69,
516 | Hex: "#5f87ff",
517 | Rgb: Rgb{95, 135, 255},
518 | Hsl: Hsl{225, 100, 68},
519 | Name: "CornflowerBlue",
520 | },
521 | {
522 | Id: 70,
523 | Hex: "#5faf00",
524 | Rgb: Rgb{95, 175, 0},
525 | Hsl: Hsl{87.4285714285714, 100, 34},
526 | Name: "Chartreuse3",
527 | },
528 | {
529 | Id: 71,
530 | Hex: "#5faf5f",
531 | Rgb: Rgb{95, 175, 95},
532 | Hsl: Hsl{120, 33, 52},
533 | Name: "DarkSeaGreen4",
534 | },
535 | {
536 | Id: 72,
537 | Hex: "#5faf87",
538 | Rgb: Rgb{95, 175, 135},
539 | Hsl: Hsl{150, 33, 52},
540 | Name: "CadetBlue",
541 | },
542 | {
543 | Id: 73,
544 | Hex: "#5fafaf",
545 | Rgb: Rgb{95, 175, 175},
546 | Hsl: Hsl{180, 33, 52},
547 | Name: "CadetBlue",
548 | },
549 | {
550 | Id: 74,
551 | Hex: "#5fafd7",
552 | Rgb: Rgb{95, 175, 215},
553 | Hsl: Hsl{200, 60, 60},
554 | Name: "SkyBlue3",
555 | },
556 | {
557 | Id: 75,
558 | Hex: "#5fafff",
559 | Rgb: Rgb{95, 175, 255},
560 | Hsl: Hsl{210, 100, 68},
561 | Name: "SteelBlue1",
562 | },
563 | {
564 | Id: 76,
565 | Hex: "#5fd700",
566 | Rgb: Rgb{95, 215, 0},
567 | Hsl: Hsl{93.4883720930233, 100, 42},
568 | Name: "Chartreuse3",
569 | },
570 | {
571 | Id: 77,
572 | Hex: "#5fd75f",
573 | Rgb: Rgb{95, 215, 95},
574 | Hsl: Hsl{120, 60, 60},
575 | Name: "PaleGreen3",
576 | },
577 | {
578 | Id: 78,
579 | Hex: "#5fd787",
580 | Rgb: Rgb{95, 215, 135},
581 | Hsl: Hsl{140, 60, 60},
582 | Name: "SeaGreen3",
583 | },
584 | {
585 | Id: 79,
586 | Hex: "#5fd7af",
587 | Rgb: Rgb{95, 215, 175},
588 | Hsl: Hsl{160, 60, 60},
589 | Name: "Aquamarine3",
590 | },
591 | {
592 | Id: 80,
593 | Hex: "#5fd7d7",
594 | Rgb: Rgb{95, 215, 215},
595 | Hsl: Hsl{180, 60, 60},
596 | Name: "MediumTurquoise",
597 | },
598 | {
599 | Id: 81,
600 | Hex: "#5fd7ff",
601 | Rgb: Rgb{95, 215, 255},
602 | Hsl: Hsl{195, 100, 68},
603 | Name: "SteelBlue1",
604 | },
605 | {
606 | Id: 82,
607 | Hex: "#5fff00",
608 | Rgb: Rgb{95, 255, 0},
609 | Hsl: Hsl{97.6470588235294, 100, 50},
610 | Name: "Chartreuse2",
611 | },
612 | {
613 | Id: 83,
614 | Hex: "#5fff5f",
615 | Rgb: Rgb{95, 255, 95},
616 | Hsl: Hsl{120, 100, 68},
617 | Name: "SeaGreen2",
618 | },
619 | {
620 | Id: 84,
621 | Hex: "#5fff87",
622 | Rgb: Rgb{95, 255, 135},
623 | Hsl: Hsl{135, 100, 68},
624 | Name: "SeaGreen1",
625 | },
626 | {
627 | Id: 85,
628 | Hex: "#5fffaf",
629 | Rgb: Rgb{95, 255, 175},
630 | Hsl: Hsl{150, 100, 68},
631 | Name: "SeaGreen1",
632 | },
633 | {
634 | Id: 86,
635 | Hex: "#5fffd7",
636 | Rgb: Rgb{95, 255, 215},
637 | Hsl: Hsl{165, 100, 68},
638 | Name: "Aquamarine1",
639 | },
640 | {
641 | Id: 87,
642 | Hex: "#5fffff",
643 | Rgb: Rgb{95, 255, 255},
644 | Hsl: Hsl{180, 100, 68},
645 | Name: "DarkSlateGray2",
646 | },
647 | {
648 | Id: 88,
649 | Hex: "#870000",
650 | Rgb: Rgb{135, 0, 0},
651 | Hsl: Hsl{0, 100, 26},
652 | Name: "DarkRed",
653 | },
654 | {
655 | Id: 89,
656 | Hex: "#87005f",
657 | Rgb: Rgb{135, 0, 95},
658 | Hsl: Hsl{317.777777777778, 100, 26},
659 | Name: "DeepPink4",
660 | },
661 | {
662 | Id: 90,
663 | Hex: "#870087",
664 | Rgb: Rgb{135, 0, 135},
665 | Hsl: Hsl{300, 100, 26},
666 | Name: "DarkMagenta",
667 | },
668 | {
669 | Id: 91,
670 | Hex: "#8700af",
671 | Rgb: Rgb{135, 0, 175},
672 | Hsl: Hsl{286.285714285714, 100, 34},
673 | Name: "DarkMagenta",
674 | },
675 | {
676 | Id: 92,
677 | Hex: "#8700d7",
678 | Rgb: Rgb{135, 0, 215},
679 | Hsl: Hsl{277.674418604651, 100, 42},
680 | Name: "DarkViolet",
681 | },
682 | {
683 | Id: 93,
684 | Hex: "#8700ff",
685 | Rgb: Rgb{135, 0, 255},
686 | Hsl: Hsl{271.764705882353, 100, 50},
687 | Name: "Purple",
688 | },
689 | {
690 | Id: 94,
691 | Hex: "#875f00",
692 | Rgb: Rgb{135, 95, 0},
693 | Hsl: Hsl{42.2222222222222, 100, 26},
694 | Name: "Orange4",
695 | },
696 | {
697 | Id: 95,
698 | Hex: "#875f5f",
699 | Rgb: Rgb{135, 95, 95},
700 | Hsl: Hsl{0, 17, 45},
701 | Name: "LightPink4",
702 | },
703 | {
704 | Id: 96,
705 | Hex: "#875f87",
706 | Rgb: Rgb{135, 95, 135},
707 | Hsl: Hsl{300, 17, 45},
708 | Name: "Plum4",
709 | },
710 | {
711 | Id: 97,
712 | Hex: "#875faf",
713 | Rgb: Rgb{135, 95, 175},
714 | Hsl: Hsl{270, 33, 52},
715 | Name: "MediumPurple3",
716 | },
717 | {
718 | Id: 98,
719 | Hex: "#875fd7",
720 | Rgb: Rgb{135, 95, 215},
721 | Hsl: Hsl{260, 60, 60},
722 | Name: "MediumPurple3",
723 | },
724 | {
725 | Id: 99,
726 | Hex: "#875fff",
727 | Rgb: Rgb{135, 95, 255},
728 | Hsl: Hsl{255, 100, 68},
729 | Name: "SlateBlue1",
730 | },
731 | {
732 | Id: 100,
733 | Hex: "#878700",
734 | Rgb: Rgb{135, 135, 0},
735 | Hsl: Hsl{60, 100, 26},
736 | Name: "Yellow4",
737 | },
738 | {
739 | Id: 101,
740 | Hex: "#87875f",
741 | Rgb: Rgb{135, 135, 95},
742 | Hsl: Hsl{60, 17, 45},
743 | Name: "Wheat4",
744 | },
745 | {
746 | Id: 102,
747 | Hex: "#878787",
748 | Rgb: Rgb{135, 135, 135},
749 | Hsl: Hsl{0, 0, 52},
750 | Name: "Grey53",
751 | },
752 | {
753 | Id: 103,
754 | Hex: "#8787af",
755 | Rgb: Rgb{135, 135, 175},
756 | Hsl: Hsl{240, 20, 60},
757 | Name: "LightSlateGrey",
758 | },
759 | {
760 | Id: 104,
761 | Hex: "#8787d7",
762 | Rgb: Rgb{135, 135, 215},
763 | Hsl: Hsl{240, 50, 68},
764 | Name: "MediumPurple",
765 | },
766 | {
767 | Id: 105,
768 | Hex: "#8787ff",
769 | Rgb: Rgb{135, 135, 255},
770 | Hsl: Hsl{240, 100, 76},
771 | Name: "LightSlateBlue",
772 | },
773 | {
774 | Id: 106,
775 | Hex: "#87af00",
776 | Rgb: Rgb{135, 175, 0},
777 | Hsl: Hsl{73.7142857142857, 100, 34},
778 | Name: "Yellow4",
779 | },
780 | {
781 | Id: 107,
782 | Hex: "#87af5f",
783 | Rgb: Rgb{135, 175, 95},
784 | Hsl: Hsl{90, 33, 52},
785 | Name: "DarkOliveGreen3",
786 | },
787 | {
788 | Id: 108,
789 | Hex: "#87af87",
790 | Rgb: Rgb{135, 175, 135},
791 | Hsl: Hsl{120, 20, 60},
792 | Name: "DarkSeaGreen",
793 | },
794 | {
795 | Id: 109,
796 | Hex: "#87afaf",
797 | Rgb: Rgb{135, 175, 175},
798 | Hsl: Hsl{180, 20, 60},
799 | Name: "LightSkyBlue3",
800 | },
801 | {
802 | Id: 110,
803 | Hex: "#87afd7",
804 | Rgb: Rgb{135, 175, 215},
805 | Hsl: Hsl{210, 50, 68},
806 | Name: "LightSkyBlue3",
807 | },
808 | {
809 | Id: 111,
810 | Hex: "#87afff",
811 | Rgb: Rgb{135, 175, 255},
812 | Hsl: Hsl{220, 100, 76},
813 | Name: "SkyBlue2",
814 | },
815 | {
816 | Id: 112,
817 | Hex: "#87d700",
818 | Rgb: Rgb{135, 215, 0},
819 | Hsl: Hsl{82.3255813953488, 100, 42},
820 | Name: "Chartreuse2",
821 | },
822 | {
823 | Id: 113,
824 | Hex: "#87d75f",
825 | Rgb: Rgb{135, 215, 95},
826 | Hsl: Hsl{100, 60, 60},
827 | Name: "DarkOliveGreen3",
828 | },
829 | {
830 | Id: 114,
831 | Hex: "#87d787",
832 | Rgb: Rgb{135, 215, 135},
833 | Hsl: Hsl{120, 50, 68},
834 | Name: "PaleGreen3",
835 | },
836 | {
837 | Id: 115,
838 | Hex: "#87d7af",
839 | Rgb: Rgb{135, 215, 175},
840 | Hsl: Hsl{150, 50, 68},
841 | Name: "DarkSeaGreen3",
842 | },
843 | {
844 | Id: 116,
845 | Hex: "#87d7d7",
846 | Rgb: Rgb{135, 215, 215},
847 | Hsl: Hsl{180, 50, 68},
848 | Name: "DarkSlateGray3",
849 | },
850 | {
851 | Id: 117,
852 | Hex: "#87d7ff",
853 | Rgb: Rgb{135, 215, 255},
854 | Hsl: Hsl{200, 100, 76},
855 | Name: "SkyBlue1",
856 | },
857 | {
858 | Id: 118,
859 | Hex: "#87ff00",
860 | Rgb: Rgb{135, 255, 0},
861 | Hsl: Hsl{88.2352941176471, 100, 50},
862 | Name: "Chartreuse1",
863 | },
864 | {
865 | Id: 119,
866 | Hex: "#87ff5f",
867 | Rgb: Rgb{135, 255, 95},
868 | Hsl: Hsl{105, 100, 68},
869 | Name: "LightGreen",
870 | },
871 | {
872 | Id: 120,
873 | Hex: "#87ff87",
874 | Rgb: Rgb{135, 255, 135},
875 | Hsl: Hsl{120, 100, 76},
876 | Name: "LightGreen",
877 | },
878 | {
879 | Id: 121,
880 | Hex: "#87ffaf",
881 | Rgb: Rgb{135, 255, 175},
882 | Hsl: Hsl{140, 100, 76},
883 | Name: "PaleGreen1",
884 | },
885 | {
886 | Id: 122,
887 | Hex: "#87ffd7",
888 | Rgb: Rgb{135, 255, 215},
889 | Hsl: Hsl{160, 100, 76},
890 | Name: "Aquamarine1",
891 | },
892 | {
893 | Id: 123,
894 | Hex: "#87ffff",
895 | Rgb: Rgb{135, 255, 255},
896 | Hsl: Hsl{180, 100, 76},
897 | Name: "DarkSlateGray1",
898 | },
899 | {
900 | Id: 124,
901 | Hex: "#af0000",
902 | Rgb: Rgb{175, 0, 0},
903 | Hsl: Hsl{0, 100, 34},
904 | Name: "Red3",
905 | },
906 | {
907 | Id: 125,
908 | Hex: "#af005f",
909 | Rgb: Rgb{175, 0, 95},
910 | Hsl: Hsl{327.428571428571, 100, 34},
911 | Name: "DeepPink4",
912 | },
913 | {
914 | Id: 126,
915 | Hex: "#af0087",
916 | Rgb: Rgb{175, 0, 135},
917 | Hsl: Hsl{313.714285714286, 100, 34},
918 | Name: "MediumVioletRed",
919 | },
920 | {
921 | Id: 127,
922 | Hex: "#af00af",
923 | Rgb: Rgb{175, 0, 175},
924 | Hsl: Hsl{300, 100, 34},
925 | Name: "Magenta3",
926 | },
927 | {
928 | Id: 128,
929 | Hex: "#af00d7",
930 | Rgb: Rgb{175, 0, 215},
931 | Hsl: Hsl{288.837209302326, 100, 42},
932 | Name: "DarkViolet",
933 | },
934 | {
935 | Id: 129,
936 | Hex: "#af00ff",
937 | Rgb: Rgb{175, 0, 255},
938 | Hsl: Hsl{281.176470588235, 100, 50},
939 | Name: "Purple",
940 | },
941 | {
942 | Id: 130,
943 | Hex: "#af5f00",
944 | Rgb: Rgb{175, 95, 0},
945 | Hsl: Hsl{32.5714285714286, 100, 34},
946 | Name: "DarkOrange3",
947 | },
948 | {
949 | Id: 131,
950 | Hex: "#af5f5f",
951 | Rgb: Rgb{175, 95, 95},
952 | Hsl: Hsl{0, 33, 52},
953 | Name: "IndianRed",
954 | },
955 | {
956 | Id: 132,
957 | Hex: "#af5f87",
958 | Rgb: Rgb{175, 95, 135},
959 | Hsl: Hsl{330, 33, 52},
960 | Name: "HotPink3",
961 | },
962 | {
963 | Id: 133,
964 | Hex: "#af5faf",
965 | Rgb: Rgb{175, 95, 175},
966 | Hsl: Hsl{300, 33, 52},
967 | Name: "MediumOrchid3",
968 | },
969 | {
970 | Id: 134,
971 | Hex: "#af5fd7",
972 | Rgb: Rgb{175, 95, 215},
973 | Hsl: Hsl{280, 60, 60},
974 | Name: "MediumOrchid",
975 | },
976 | {
977 | Id: 135,
978 | Hex: "#af5fff",
979 | Rgb: Rgb{175, 95, 255},
980 | Hsl: Hsl{270, 100, 68},
981 | Name: "MediumPurple2",
982 | },
983 | {
984 | Id: 136,
985 | Hex: "#af8700",
986 | Rgb: Rgb{175, 135, 0},
987 | Hsl: Hsl{46.2857142857143, 100, 34},
988 | Name: "DarkGoldenrod",
989 | },
990 | {
991 | Id: 137,
992 | Hex: "#af875f",
993 | Rgb: Rgb{175, 135, 95},
994 | Hsl: Hsl{30, 33, 52},
995 | Name: "LightSalmon3",
996 | },
997 | {
998 | Id: 138,
999 | Hex: "#af8787",
1000 | Rgb: Rgb{175, 135, 135},
1001 | Hsl: Hsl{0, 20, 60},
1002 | Name: "RosyBrown",
1003 | },
1004 | {
1005 | Id: 139,
1006 | Hex: "#af87af",
1007 | Rgb: Rgb{175, 135, 175},
1008 | Hsl: Hsl{300, 20, 60},
1009 | Name: "Grey63",
1010 | },
1011 | {
1012 | Id: 140,
1013 | Hex: "#af87d7",
1014 | Rgb: Rgb{175, 135, 215},
1015 | Hsl: Hsl{270, 50, 68},
1016 | Name: "MediumPurple2",
1017 | },
1018 | {
1019 | Id: 141,
1020 | Hex: "#af87ff",
1021 | Rgb: Rgb{175, 135, 255},
1022 | Hsl: Hsl{260, 100, 76},
1023 | Name: "MediumPurple1",
1024 | },
1025 | {
1026 | Id: 142,
1027 | Hex: "#afaf00",
1028 | Rgb: Rgb{175, 175, 0},
1029 | Hsl: Hsl{60, 100, 34},
1030 | Name: "Gold3",
1031 | },
1032 | {
1033 | Id: 143,
1034 | Hex: "#afaf5f",
1035 | Rgb: Rgb{175, 175, 95},
1036 | Hsl: Hsl{60, 33, 52},
1037 | Name: "DarkKhaki",
1038 | },
1039 | {
1040 | Id: 144,
1041 | Hex: "#afaf87",
1042 | Rgb: Rgb{175, 175, 135},
1043 | Hsl: Hsl{60, 20, 60},
1044 | Name: "NavajoWhite3",
1045 | },
1046 | {
1047 | Id: 145,
1048 | Hex: "#afafaf",
1049 | Rgb: Rgb{175, 175, 175},
1050 | Hsl: Hsl{0, 0, 68},
1051 | Name: "Grey69",
1052 | },
1053 | {
1054 | Id: 146,
1055 | Hex: "#afafd7",
1056 | Rgb: Rgb{175, 175, 215},
1057 | Hsl: Hsl{240, 33, 76},
1058 | Name: "LightSteelBlue3",
1059 | },
1060 | {
1061 | Id: 147,
1062 | Hex: "#afafff",
1063 | Rgb: Rgb{175, 175, 255},
1064 | Hsl: Hsl{240, 100, 84},
1065 | Name: "LightSteelBlue",
1066 | },
1067 | {
1068 | Id: 148,
1069 | Hex: "#afd700",
1070 | Rgb: Rgb{175, 215, 0},
1071 | Hsl: Hsl{71.1627906976744, 100, 42},
1072 | Name: "Yellow3",
1073 | },
1074 | {
1075 | Id: 149,
1076 | Hex: "#afd75f",
1077 | Rgb: Rgb{175, 215, 95},
1078 | Hsl: Hsl{80, 60, 60},
1079 | Name: "DarkOliveGreen3",
1080 | },
1081 | {
1082 | Id: 150,
1083 | Hex: "#afd787",
1084 | Rgb: Rgb{175, 215, 135},
1085 | Hsl: Hsl{90, 50, 68},
1086 | Name: "DarkSeaGreen3",
1087 | },
1088 | {
1089 | Id: 151,
1090 | Hex: "#afd7af",
1091 | Rgb: Rgb{175, 215, 175},
1092 | Hsl: Hsl{120, 33, 76},
1093 | Name: "DarkSeaGreen2",
1094 | },
1095 | {
1096 | Id: 152,
1097 | Hex: "#afd7d7",
1098 | Rgb: Rgb{175, 215, 215},
1099 | Hsl: Hsl{180, 33, 76},
1100 | Name: "LightCyan3",
1101 | },
1102 | {
1103 | Id: 153,
1104 | Hex: "#afd7ff",
1105 | Rgb: Rgb{175, 215, 255},
1106 | Hsl: Hsl{210, 100, 84},
1107 | Name: "LightSkyBlue1",
1108 | },
1109 | {
1110 | Id: 154,
1111 | Hex: "#afff00",
1112 | Rgb: Rgb{175, 255, 0},
1113 | Hsl: Hsl{78.8235294117647, 100, 50},
1114 | Name: "GreenYellow",
1115 | },
1116 | {
1117 | Id: 155,
1118 | Hex: "#afff5f",
1119 | Rgb: Rgb{175, 255, 95},
1120 | Hsl: Hsl{90, 100, 68},
1121 | Name: "DarkOliveGreen2",
1122 | },
1123 | {
1124 | Id: 156,
1125 | Hex: "#afff87",
1126 | Rgb: Rgb{175, 255, 135},
1127 | Hsl: Hsl{100, 100, 76},
1128 | Name: "PaleGreen1",
1129 | },
1130 | {
1131 | Id: 157,
1132 | Hex: "#afffaf",
1133 | Rgb: Rgb{175, 255, 175},
1134 | Hsl: Hsl{120, 100, 84},
1135 | Name: "DarkSeaGreen2",
1136 | },
1137 | {
1138 | Id: 158,
1139 | Hex: "#afffd7",
1140 | Rgb: Rgb{175, 255, 215},
1141 | Hsl: Hsl{150, 100, 84},
1142 | Name: "DarkSeaGreen1",
1143 | },
1144 | {
1145 | Id: 159,
1146 | Hex: "#afffff",
1147 | Rgb: Rgb{175, 255, 255},
1148 | Hsl: Hsl{180, 100, 84},
1149 | Name: "PaleTurquoise1",
1150 | },
1151 | {
1152 | Id: 160,
1153 | Hex: "#d70000",
1154 | Rgb: Rgb{215, 0, 0},
1155 | Hsl: Hsl{0, 100, 42},
1156 | Name: "Red3",
1157 | },
1158 | {
1159 | Id: 161,
1160 | Hex: "#d7005f",
1161 | Rgb: Rgb{215, 0, 95},
1162 | Hsl: Hsl{333.488372093023, 100, 42},
1163 | Name: "DeepPink3",
1164 | },
1165 | {
1166 | Id: 162,
1167 | Hex: "#d70087",
1168 | Rgb: Rgb{215, 0, 135},
1169 | Hsl: Hsl{322.325581395349, 100, 42},
1170 | Name: "DeepPink3",
1171 | },
1172 | {
1173 | Id: 163,
1174 | Hex: "#d700af",
1175 | Rgb: Rgb{215, 0, 175},
1176 | Hsl: Hsl{311.162790697674, 100, 42},
1177 | Name: "Magenta3",
1178 | },
1179 | {
1180 | Id: 164,
1181 | Hex: "#d700d7",
1182 | Rgb: Rgb{215, 0, 215},
1183 | Hsl: Hsl{300, 100, 42},
1184 | Name: "Magenta3",
1185 | },
1186 | {
1187 | Id: 165,
1188 | Hex: "#d700ff",
1189 | Rgb: Rgb{215, 0, 255},
1190 | Hsl: Hsl{290.588235294118, 100, 50},
1191 | Name: "Magenta2",
1192 | },
1193 | {
1194 | Id: 166,
1195 | Hex: "#d75f00",
1196 | Rgb: Rgb{215, 95, 0},
1197 | Hsl: Hsl{26.5116279069767, 100, 42},
1198 | Name: "DarkOrange3",
1199 | },
1200 | {
1201 | Id: 167,
1202 | Hex: "#d75f5f",
1203 | Rgb: Rgb{215, 95, 95},
1204 | Hsl: Hsl{0, 60, 60},
1205 | Name: "IndianRed",
1206 | },
1207 | {
1208 | Id: 168,
1209 | Hex: "#d75f87",
1210 | Rgb: Rgb{215, 95, 135},
1211 | Hsl: Hsl{340, 60, 60},
1212 | Name: "HotPink3",
1213 | },
1214 | {
1215 | Id: 169,
1216 | Hex: "#d75faf",
1217 | Rgb: Rgb{215, 95, 175},
1218 | Hsl: Hsl{320, 60, 60},
1219 | Name: "HotPink2",
1220 | },
1221 | {
1222 | Id: 170,
1223 | Hex: "#d75fd7",
1224 | Rgb: Rgb{215, 95, 215},
1225 | Hsl: Hsl{300, 60, 60},
1226 | Name: "Orchid",
1227 | },
1228 | {
1229 | Id: 171,
1230 | Hex: "#d75fff",
1231 | Rgb: Rgb{215, 95, 255},
1232 | Hsl: Hsl{285, 100, 68},
1233 | Name: "MediumOrchid1",
1234 | },
1235 | {
1236 | Id: 172,
1237 | Hex: "#d78700",
1238 | Rgb: Rgb{215, 135, 0},
1239 | Hsl: Hsl{37.6744186046512, 100, 42},
1240 | Name: "Orange3",
1241 | },
1242 | {
1243 | Id: 173,
1244 | Hex: "#d7875f",
1245 | Rgb: Rgb{215, 135, 95},
1246 | Hsl: Hsl{20, 60, 60},
1247 | Name: "LightSalmon3",
1248 | },
1249 | {
1250 | Id: 174,
1251 | Hex: "#d78787",
1252 | Rgb: Rgb{215, 135, 135},
1253 | Hsl: Hsl{0, 50, 68},
1254 | Name: "LightPink3",
1255 | },
1256 | {
1257 | Id: 175,
1258 | Hex: "#d787af",
1259 | Rgb: Rgb{215, 135, 175},
1260 | Hsl: Hsl{330, 50, 68},
1261 | Name: "Pink3",
1262 | },
1263 | {
1264 | Id: 176,
1265 | Hex: "#d787d7",
1266 | Rgb: Rgb{215, 135, 215},
1267 | Hsl: Hsl{300, 50, 68},
1268 | Name: "Plum3",
1269 | },
1270 | {
1271 | Id: 177,
1272 | Hex: "#d787ff",
1273 | Rgb: Rgb{215, 135, 255},
1274 | Hsl: Hsl{280, 100, 76},
1275 | Name: "Violet",
1276 | },
1277 | {
1278 | Id: 178,
1279 | Hex: "#d7af00",
1280 | Rgb: Rgb{215, 175, 0},
1281 | Hsl: Hsl{48.8372093023256, 100, 42},
1282 | Name: "Gold3",
1283 | },
1284 | {
1285 | Id: 179,
1286 | Hex: "#d7af5f",
1287 | Rgb: Rgb{215, 175, 95},
1288 | Hsl: Hsl{40, 60, 60},
1289 | Name: "LightGoldenrod3",
1290 | },
1291 | {
1292 | Id: 180,
1293 | Hex: "#d7af87",
1294 | Rgb: Rgb{215, 175, 135},
1295 | Hsl: Hsl{30, 50, 68},
1296 | Name: "Tan",
1297 | },
1298 | {
1299 | Id: 181,
1300 | Hex: "#d7afaf",
1301 | Rgb: Rgb{215, 175, 175},
1302 | Hsl: Hsl{0, 33, 76},
1303 | Name: "MistyRose3",
1304 | },
1305 | {
1306 | Id: 182,
1307 | Hex: "#d7afd7",
1308 | Rgb: Rgb{215, 175, 215},
1309 | Hsl: Hsl{300, 33, 76},
1310 | Name: "Thistle3",
1311 | },
1312 | {
1313 | Id: 183,
1314 | Hex: "#d7afff",
1315 | Rgb: Rgb{215, 175, 255},
1316 | Hsl: Hsl{270, 100, 84},
1317 | Name: "Plum2",
1318 | },
1319 | {
1320 | Id: 184,
1321 | Hex: "#d7d700",
1322 | Rgb: Rgb{215, 215, 0},
1323 | Hsl: Hsl{60, 100, 42},
1324 | Name: "Yellow3",
1325 | },
1326 | {
1327 | Id: 185,
1328 | Hex: "#d7d75f",
1329 | Rgb: Rgb{215, 215, 95},
1330 | Hsl: Hsl{60, 60, 60},
1331 | Name: "Khaki3",
1332 | },
1333 | {
1334 | Id: 186,
1335 | Hex: "#d7d787",
1336 | Rgb: Rgb{215, 215, 135},
1337 | Hsl: Hsl{60, 50, 68},
1338 | Name: "LightGoldenrod2",
1339 | },
1340 | {
1341 | Id: 187,
1342 | Hex: "#d7d7af",
1343 | Rgb: Rgb{215, 215, 175},
1344 | Hsl: Hsl{60, 33, 76},
1345 | Name: "LightYellow3",
1346 | },
1347 | {
1348 | Id: 188,
1349 | Hex: "#d7d7d7",
1350 | Rgb: Rgb{215, 215, 215},
1351 | Hsl: Hsl{0, 0, 84},
1352 | Name: "Grey84",
1353 | },
1354 | {
1355 | Id: 189,
1356 | Hex: "#d7d7ff",
1357 | Rgb: Rgb{215, 215, 255},
1358 | Hsl: Hsl{240, 100, 92},
1359 | Name: "LightSteelBlue1",
1360 | },
1361 | {
1362 | Id: 190,
1363 | Hex: "#d7ff00",
1364 | Rgb: Rgb{215, 255, 0},
1365 | Hsl: Hsl{69.4117647058823, 100, 50},
1366 | Name: "Yellow2",
1367 | },
1368 | {
1369 | Id: 191,
1370 | Hex: "#d7ff5f",
1371 | Rgb: Rgb{215, 255, 95},
1372 | Hsl: Hsl{75, 100, 68},
1373 | Name: "DarkOliveGreen1",
1374 | },
1375 | {
1376 | Id: 192,
1377 | Hex: "#d7ff87",
1378 | Rgb: Rgb{215, 255, 135},
1379 | Hsl: Hsl{80, 100, 76},
1380 | Name: "DarkOliveGreen1",
1381 | },
1382 | {
1383 | Id: 193,
1384 | Hex: "#d7ffaf",
1385 | Rgb: Rgb{215, 255, 175},
1386 | Hsl: Hsl{90, 100, 84},
1387 | Name: "DarkSeaGreen1",
1388 | },
1389 | {
1390 | Id: 194,
1391 | Hex: "#d7ffd7",
1392 | Rgb: Rgb{215, 255, 215},
1393 | Hsl: Hsl{120, 100, 92},
1394 | Name: "Honeydew2",
1395 | },
1396 | {
1397 | Id: 195,
1398 | Hex: "#d7ffff",
1399 | Rgb: Rgb{215, 255, 255},
1400 | Hsl: Hsl{180, 100, 92},
1401 | Name: "LightCyan1",
1402 | },
1403 | {
1404 | Id: 196,
1405 | Hex: "#ff0000",
1406 | Rgb: Rgb{255, 0, 0},
1407 | Hsl: Hsl{0, 100, 50},
1408 | Name: "Red1",
1409 | },
1410 | {
1411 | Id: 197,
1412 | Hex: "#ff005f",
1413 | Rgb: Rgb{255, 0, 95},
1414 | Hsl: Hsl{337.647058823529, 100, 50},
1415 | Name: "DeepPink2",
1416 | },
1417 | {
1418 | Id: 198,
1419 | Hex: "#ff0087",
1420 | Rgb: Rgb{255, 0, 135},
1421 | Hsl: Hsl{328.235294117647, 100, 50},
1422 | Name: "DeepPink1",
1423 | },
1424 | {
1425 | Id: 199,
1426 | Hex: "#ff00af",
1427 | Rgb: Rgb{255, 0, 175},
1428 | Hsl: Hsl{318.823529411765, 100, 50},
1429 | Name: "DeepPink1",
1430 | },
1431 | {
1432 | Id: 200,
1433 | Hex: "#ff00d7",
1434 | Rgb: Rgb{255, 0, 215},
1435 | Hsl: Hsl{309.411764705882, 100, 50},
1436 | Name: "Magenta2",
1437 | },
1438 | {
1439 | Id: 201,
1440 | Hex: "#ff00ff",
1441 | Rgb: Rgb{255, 0, 255},
1442 | Hsl: Hsl{300, 100, 50},
1443 | Name: "Magenta1",
1444 | },
1445 | {
1446 | Id: 202,
1447 | Hex: "#ff5f00",
1448 | Rgb: Rgb{255, 95, 0},
1449 | Hsl: Hsl{22.3529411764706, 100, 50},
1450 | Name: "OrangeRed1",
1451 | },
1452 | {
1453 | Id: 203,
1454 | Hex: "#ff5f5f",
1455 | Rgb: Rgb{255, 95, 95},
1456 | Hsl: Hsl{0, 100, 68},
1457 | Name: "IndianRed1",
1458 | },
1459 | {
1460 | Id: 204,
1461 | Hex: "#ff5f87",
1462 | Rgb: Rgb{255, 95, 135},
1463 | Hsl: Hsl{345, 100, 68},
1464 | Name: "IndianRed1",
1465 | },
1466 | {
1467 | Id: 205,
1468 | Hex: "#ff5faf",
1469 | Rgb: Rgb{255, 95, 175},
1470 | Hsl: Hsl{330, 100, 68},
1471 | Name: "HotPink",
1472 | },
1473 | {
1474 | Id: 206,
1475 | Hex: "#ff5fd7",
1476 | Rgb: Rgb{255, 95, 215},
1477 | Hsl: Hsl{315, 100, 68},
1478 | Name: "HotPink",
1479 | },
1480 | {
1481 | Id: 207,
1482 | Hex: "#ff5fff",
1483 | Rgb: Rgb{255, 95, 255},
1484 | Hsl: Hsl{300, 100, 68},
1485 | Name: "MediumOrchid1",
1486 | },
1487 | {
1488 | Id: 208,
1489 | Hex: "#ff8700",
1490 | Rgb: Rgb{255, 135, 0},
1491 | Hsl: Hsl{31.7647058823529, 100, 50},
1492 | Name: "DarkOrange",
1493 | },
1494 | {
1495 | Id: 209,
1496 | Hex: "#ff875f",
1497 | Rgb: Rgb{255, 135, 95},
1498 | Hsl: Hsl{15, 100, 68},
1499 | Name: "Salmon1",
1500 | },
1501 | {
1502 | Id: 210,
1503 | Hex: "#ff8787",
1504 | Rgb: Rgb{255, 135, 135},
1505 | Hsl: Hsl{0, 100, 76},
1506 | Name: "LightCoral",
1507 | },
1508 | {
1509 | Id: 211,
1510 | Hex: "#ff87af",
1511 | Rgb: Rgb{255, 135, 175},
1512 | Hsl: Hsl{340, 100, 76},
1513 | Name: "PaleVioletRed1",
1514 | },
1515 | {
1516 | Id: 212,
1517 | Hex: "#ff87d7",
1518 | Rgb: Rgb{255, 135, 215},
1519 | Hsl: Hsl{320, 100, 76},
1520 | Name: "Orchid2",
1521 | },
1522 | {
1523 | Id: 213,
1524 | Hex: "#ff87ff",
1525 | Rgb: Rgb{255, 135, 255},
1526 | Hsl: Hsl{300, 100, 76},
1527 | Name: "Orchid1",
1528 | },
1529 | {
1530 | Id: 214,
1531 | Hex: "#ffaf00",
1532 | Rgb: Rgb{255, 175, 0},
1533 | Hsl: Hsl{41.1764705882353, 100, 50},
1534 | Name: "Orange1",
1535 | },
1536 | {
1537 | Id: 215,
1538 | Hex: "#ffaf5f",
1539 | Rgb: Rgb{255, 175, 95},
1540 | Hsl: Hsl{30, 100, 68},
1541 | Name: "SandyBrown",
1542 | },
1543 | {
1544 | Id: 216,
1545 | Hex: "#ffaf87",
1546 | Rgb: Rgb{255, 175, 135},
1547 | Hsl: Hsl{20, 100, 76},
1548 | Name: "LightSalmon1",
1549 | },
1550 | {
1551 | Id: 217,
1552 | Hex: "#ffafaf",
1553 | Rgb: Rgb{255, 175, 175},
1554 | Hsl: Hsl{0, 100, 84},
1555 | Name: "LightPink1",
1556 | },
1557 | {
1558 | Id: 218,
1559 | Hex: "#ffafd7",
1560 | Rgb: Rgb{255, 175, 215},
1561 | Hsl: Hsl{330, 100, 84},
1562 | Name: "Pink1",
1563 | },
1564 | {
1565 | Id: 219,
1566 | Hex: "#ffafff",
1567 | Rgb: Rgb{255, 175, 255},
1568 | Hsl: Hsl{300, 100, 84},
1569 | Name: "Plum1",
1570 | },
1571 | {
1572 | Id: 220,
1573 | Hex: "#ffd700",
1574 | Rgb: Rgb{255, 215, 0},
1575 | Hsl: Hsl{50.5882352941176, 100, 50},
1576 | Name: "Gold1",
1577 | },
1578 | {
1579 | Id: 221,
1580 | Hex: "#ffd75f",
1581 | Rgb: Rgb{255, 215, 95},
1582 | Hsl: Hsl{45, 100, 68},
1583 | Name: "LightGoldenrod2",
1584 | },
1585 | {
1586 | Id: 222,
1587 | Hex: "#ffd787",
1588 | Rgb: Rgb{255, 215, 135},
1589 | Hsl: Hsl{40, 100, 76},
1590 | Name: "LightGoldenrod2",
1591 | },
1592 | {
1593 | Id: 223,
1594 | Hex: "#ffd7af",
1595 | Rgb: Rgb{255, 215, 175},
1596 | Hsl: Hsl{30, 100, 84},
1597 | Name: "NavajoWhite1",
1598 | },
1599 | {
1600 | Id: 224,
1601 | Hex: "#ffd7d7",
1602 | Rgb: Rgb{255, 215, 215},
1603 | Hsl: Hsl{0, 100, 92},
1604 | Name: "MistyRose1",
1605 | },
1606 | {
1607 | Id: 225,
1608 | Hex: "#ffd7ff",
1609 | Rgb: Rgb{255, 215, 255},
1610 | Hsl: Hsl{300, 100, 92},
1611 | Name: "Thistle1",
1612 | },
1613 | {
1614 | Id: 226,
1615 | Hex: "#ffff00",
1616 | Rgb: Rgb{255, 255, 0},
1617 | Hsl: Hsl{60, 100, 50},
1618 | Name: "Yellow1",
1619 | },
1620 | {
1621 | Id: 227,
1622 | Hex: "#ffff5f",
1623 | Rgb: Rgb{255, 255, 95},
1624 | Hsl: Hsl{60, 100, 68},
1625 | Name: "LightGoldenrod1",
1626 | },
1627 | {
1628 | Id: 228,
1629 | Hex: "#ffff87",
1630 | Rgb: Rgb{255, 255, 135},
1631 | Hsl: Hsl{60, 100, 76},
1632 | Name: "Khaki1",
1633 | },
1634 | {
1635 | Id: 229,
1636 | Hex: "#ffffaf",
1637 | Rgb: Rgb{255, 255, 175},
1638 | Hsl: Hsl{60, 100, 84},
1639 | Name: "Wheat1",
1640 | },
1641 | {
1642 | Id: 230,
1643 | Hex: "#ffffd7",
1644 | Rgb: Rgb{255, 255, 215},
1645 | Hsl: Hsl{60, 100, 92},
1646 | Name: "Cornsilk1",
1647 | },
1648 | {
1649 | Id: 231,
1650 | Hex: "#ffffff",
1651 | Rgb: Rgb{255, 255, 255},
1652 | Hsl: Hsl{0, 0, 100},
1653 | Name: "Grey100",
1654 | },
1655 | {
1656 | Id: 232,
1657 | Hex: "#080808",
1658 | Rgb: Rgb{8, 8, 8},
1659 | Hsl: Hsl{0, 0, 3},
1660 | Name: "Grey3",
1661 | },
1662 | {
1663 | Id: 233,
1664 | Hex: "#121212",
1665 | Rgb: Rgb{18, 18, 18},
1666 | Hsl: Hsl{0, 0, 7},
1667 | Name: "Grey7",
1668 | },
1669 | {
1670 | Id: 234,
1671 | Hex: "#1c1c1c",
1672 | Rgb: Rgb{28, 28, 28},
1673 | Hsl: Hsl{0, 0, 10},
1674 | Name: "Grey11",
1675 | },
1676 | {
1677 | Id: 235,
1678 | Hex: "#262626",
1679 | Rgb: Rgb{38, 38, 38},
1680 | Hsl: Hsl{0, 0, 14},
1681 | Name: "Grey15",
1682 | },
1683 | {
1684 | Id: 236,
1685 | Hex: "#303030",
1686 | Rgb: Rgb{48, 48, 48},
1687 | Hsl: Hsl{0, 0, 18},
1688 | Name: "Grey19",
1689 | },
1690 | {
1691 | Id: 237,
1692 | Hex: "#3a3a3a",
1693 | Rgb: Rgb{58, 58, 58},
1694 | Hsl: Hsl{0, 0, 22},
1695 | Name: "Grey23",
1696 | },
1697 | {
1698 | Id: 238,
1699 | Hex: "#444444",
1700 | Rgb: Rgb{68, 68, 68},
1701 | Hsl: Hsl{0, 0, 26},
1702 | Name: "Grey27",
1703 | },
1704 | {
1705 | Id: 239,
1706 | Hex: "#4e4e4e",
1707 | Rgb: Rgb{78, 78, 78},
1708 | Hsl: Hsl{0, 0, 30},
1709 | Name: "Grey30",
1710 | },
1711 | {
1712 | Id: 240,
1713 | Hex: "#585858",
1714 | Rgb: Rgb{88, 88, 88},
1715 | Hsl: Hsl{0, 0, 34},
1716 | Name: "Grey35",
1717 | },
1718 | {
1719 | Id: 241,
1720 | Hex: "#626262",
1721 | Rgb: Rgb{98, 98, 98},
1722 | Hsl: Hsl{0, 0, 37},
1723 | Name: "Grey39",
1724 | },
1725 | {
1726 | Id: 242,
1727 | Hex: "#6c6c6c",
1728 | Rgb: Rgb{108, 108, 108},
1729 | Hsl: Hsl{0, 0, 40},
1730 | Name: "Grey42",
1731 | },
1732 | {
1733 | Id: 243,
1734 | Hex: "#767676",
1735 | Rgb: Rgb{118, 118, 118},
1736 | Hsl: Hsl{0, 0, 46},
1737 | Name: "Grey46",
1738 | },
1739 | {
1740 | Id: 244,
1741 | Hex: "#808080",
1742 | Rgb: Rgb{128, 128, 128},
1743 | Hsl: Hsl{0, 0, 50},
1744 | Name: "Grey50",
1745 | },
1746 | {
1747 | Id: 245,
1748 | Hex: "#8a8a8a",
1749 | Rgb: Rgb{138, 138, 138},
1750 | Hsl: Hsl{0, 0, 54},
1751 | Name: "Grey54",
1752 | },
1753 | {
1754 | Id: 246,
1755 | Hex: "#949494",
1756 | Rgb: Rgb{148, 148, 148},
1757 | Hsl: Hsl{0, 0, 58},
1758 | Name: "Grey58",
1759 | },
1760 | {
1761 | Id: 247,
1762 | Hex: "#9e9e9e",
1763 | Rgb: Rgb{158, 158, 158},
1764 | Hsl: Hsl{0, 0, 61},
1765 | Name: "Grey62",
1766 | },
1767 | {
1768 | Id: 248,
1769 | Hex: "#a8a8a8",
1770 | Rgb: Rgb{168, 168, 168},
1771 | Hsl: Hsl{0, 0, 65},
1772 | Name: "Grey66",
1773 | },
1774 | {
1775 | Id: 249,
1776 | Hex: "#b2b2b2",
1777 | Rgb: Rgb{178, 178, 178},
1778 | Hsl: Hsl{0, 0, 69},
1779 | Name: "Grey70",
1780 | },
1781 | {
1782 | Id: 250,
1783 | Hex: "#bcbcbc",
1784 | Rgb: Rgb{188, 188, 188},
1785 | Hsl: Hsl{0, 0, 73},
1786 | Name: "Grey74",
1787 | },
1788 | {
1789 | Id: 251,
1790 | Hex: "#c6c6c6",
1791 | Rgb: Rgb{198, 198, 198},
1792 | Hsl: Hsl{0, 0, 77},
1793 | Name: "Grey78",
1794 | },
1795 | {
1796 | Id: 252,
1797 | Hex: "#d0d0d0",
1798 | Rgb: Rgb{208, 208, 208},
1799 | Hsl: Hsl{0, 0, 81},
1800 | Name: "Grey82",
1801 | },
1802 | {
1803 | Id: 253,
1804 | Hex: "#dadada",
1805 | Rgb: Rgb{218, 218, 218},
1806 | Hsl: Hsl{0, 0, 85},
1807 | Name: "Grey85",
1808 | },
1809 | {
1810 | Id: 254,
1811 | Hex: "#e4e4e4",
1812 | Rgb: Rgb{228, 228, 228},
1813 | Hsl: Hsl{0, 0, 89},
1814 | Name: "Grey89",
1815 | },
1816 | {
1817 | Id: 255,
1818 | Hex: "#eeeeee",
1819 | Rgb: Rgb{238, 238, 238},
1820 | Hsl: Hsl{0, 0, 93},
1821 | Name: "Grey93",
1822 | },
1823 | }
1824 |
--------------------------------------------------------------------------------