sai 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +38 -1
  4. data/README.md +39 -241
  5. data/docs/USAGE.md +351 -0
  6. data/lib/sai/ansi/color_parser.rb +109 -0
  7. data/lib/sai/ansi/sequence_processor.rb +269 -0
  8. data/lib/sai/ansi/sequenced_string.rb +475 -0
  9. data/lib/sai/ansi/style_parser.rb +66 -0
  10. data/lib/sai/ansi.rb +0 -27
  11. data/lib/sai/conversion/color_sequence.rb +4 -4
  12. data/lib/sai/conversion/rgb/color_classifier.rb +209 -0
  13. data/lib/sai/conversion/rgb/color_indexer.rb +48 -0
  14. data/lib/sai/conversion/rgb/color_space.rb +192 -0
  15. data/lib/sai/conversion/rgb/color_transformer.rb +140 -0
  16. data/lib/sai/conversion/rgb.rb +23 -269
  17. data/lib/sai/decorator/color_manipulations.rb +157 -0
  18. data/lib/sai/decorator/delegation.rb +84 -0
  19. data/lib/sai/decorator/gradients.rb +363 -0
  20. data/lib/sai/decorator/hex_colors.rb +56 -0
  21. data/lib/sai/decorator/named_colors.rb +780 -0
  22. data/lib/sai/decorator/named_styles.rb +276 -0
  23. data/lib/sai/decorator/rgb_colors.rb +64 -0
  24. data/lib/sai/decorator.rb +35 -795
  25. data/lib/sai/mode_selector.rb +19 -19
  26. data/lib/sai/named_colors.rb +437 -0
  27. data/lib/sai.rb +753 -23
  28. data/sig/manifest.yaml +3 -0
  29. data/sig/sai/ansi/color_parser.rbs +77 -0
  30. data/sig/sai/ansi/sequence_processor.rbs +178 -0
  31. data/sig/sai/ansi/sequenced_string.rbs +380 -0
  32. data/sig/sai/ansi/style_parser.rbs +59 -0
  33. data/sig/sai/ansi.rbs +0 -10
  34. data/sig/sai/conversion/rgb/color_classifier.rbs +165 -0
  35. data/sig/sai/conversion/rgb/color_indexer.rbs +41 -0
  36. data/sig/sai/conversion/rgb/color_space.rbs +129 -0
  37. data/sig/sai/conversion/rgb/color_transformer.rbs +99 -0
  38. data/sig/sai/conversion/rgb.rbs +15 -198
  39. data/sig/sai/decorator/color_manipulations.rbs +125 -0
  40. data/sig/sai/decorator/delegation.rbs +47 -0
  41. data/sig/sai/decorator/gradients.rbs +267 -0
  42. data/sig/sai/decorator/hex_colors.rbs +48 -0
  43. data/sig/sai/decorator/named_colors.rbs +1491 -0
  44. data/sig/sai/decorator/named_styles.rbs +72 -0
  45. data/sig/sai/decorator/rgb_colors.rbs +52 -0
  46. data/sig/sai/decorator.rbs +25 -202
  47. data/sig/sai/mode_selector.rbs +19 -19
  48. data/sig/sai/named_colors.rbs +65 -0
  49. data/sig/sai.rbs +1485 -44
  50. metadata +38 -4
@@ -0,0 +1,475 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'sai/ansi'
5
+ require 'sai/ansi/sequence_processor'
6
+
7
+ module Sai
8
+ module ANSI
9
+ # A representation of a ANSI encoded string and its individual {SequencedString::Segment segments}
10
+ #
11
+ # @author {https://aaronmallen.me Aaron Allen}
12
+ # @since 0.3.0
13
+ #
14
+ # @api public
15
+ class SequencedString
16
+ # @rbs skip
17
+ extend Forwardable
18
+ include Enumerable #[Segment]
19
+
20
+ # @!method each
21
+ # Iterate over each segment
22
+ #
23
+ # @author {https://aaronmallen.me Aaron Allen}
24
+ # @since 0.3.0
25
+ #
26
+ # @api public
27
+ # @return [Enumerator] the Enumerator
28
+ #
29
+ # @!method map
30
+ # Map over segments
31
+ #
32
+ # @author {https://aaronmallen.me Aaron Allen}
33
+ # @since 0.3.0
34
+ #
35
+ # @api public
36
+ # @return [Array] the segments to map over
37
+ #
38
+ # @!method size
39
+ # Number of segments
40
+ #
41
+ # @author {https://aaronmallen.me Aaron Allen}
42
+ # @since 0.3.0
43
+ #
44
+ # @api public
45
+ #
46
+ # @return [Integer] the number of segments
47
+ def_delegators :@segments, :each, :empty?, :map, :size # steep:ignore NoMethod
48
+
49
+ # @rbs!
50
+ # def each: () { (Segment) -> void } -> SequencedString
51
+ # def empty?: () -> bool
52
+ # def map: () { (Segment) -> untyped } -> Array[untyped]
53
+ # def size: () -> Integer
54
+
55
+ # Initialize a new instance of SequencedString
56
+ #
57
+ # @author {https://aaronmallen.me Aaron Allen}
58
+ # @since 0.3.0
59
+ #
60
+ # @api private
61
+ #
62
+ # @param string [String] the sequenced string to Segment
63
+ #
64
+ # @return [SequencedString] the new instance of SequencedString
65
+ # @rbs (String string) -> void
66
+ def initialize(string)
67
+ @segments = ANSI::SequenceProcessor.process(string).map do |segment_options|
68
+ Segment.new(**segment_options) # steep:ignore InsufficientKeywordArguments
69
+ end
70
+ end
71
+
72
+ # Fetch a segment by index
73
+ #
74
+ # @author {https://aaronmallen.me Aaron Allen}
75
+ # @since 0.3.0
76
+ #
77
+ # @api public
78
+ #
79
+ # @example
80
+ # string = SequencedString.new("\e[31mred\e[0m")
81
+ # string[0] #=> #<SequencedString::Segment:0x00007f9b3b8b3e10>
82
+ #
83
+ # @param index [Integer] the index of the segment to fetch
84
+ #
85
+ # @return [Segment, nil] the segment at the index
86
+ # @rbs (Integer index) -> Segment?
87
+ def [](index)
88
+ @segments[index]
89
+ end
90
+
91
+ # Compare the SequencedString to another object
92
+ #
93
+ # @author {https://aaronmallen.me Aaron Allen}
94
+ # @since 0.3.0
95
+ #
96
+ # @api public
97
+ #
98
+ # @example
99
+ # string = "\e[31mred\e[0m"
100
+ # SequencedString.new(string) == string #=> true
101
+ #
102
+ # @param other [Object] the object to compare to
103
+ #
104
+ # @return [Boolean] `true` if the SequencedString is equal to the other object, `false` otherwise
105
+ # @rbs (untyped other) -> bool
106
+ def ==(other)
107
+ (other.is_a?(self.class) && to_s == other.to_s) ||
108
+ (other.is_a?(String) && to_s == self.class.new(other).to_s)
109
+ end
110
+
111
+ # Combine a sequenced string with another object
112
+ #
113
+ # @author {https://aaronmallen.me Aaron Allen}
114
+ # @since 0.3.0
115
+ #
116
+ # @api public
117
+ #
118
+ # @example
119
+ # sequenced_string = SequencedString.new("\e[31mred\e[0m")
120
+ # sequenced_string + " is a color" #=> "\e[31mred\e[0m is a color"
121
+ #
122
+ # @param other [Object] the object to combine with
123
+ #
124
+ # @return [SequencedString] the combined string
125
+ # @rbs (untyped other) -> SequencedString
126
+ def +(other)
127
+ string = to_s + other.to_s
128
+ self.class.new(string)
129
+ end
130
+
131
+ # Return just the raw text content with **no ANSI sequences**
132
+ #
133
+ # @author {https://aaronmallen.me Aaron Allen}
134
+ # @since 0.3.0
135
+ #
136
+ # @api public
137
+ #
138
+ # @example
139
+ # string = SequencedString.new("Normal \e[31mred\e[0m")
140
+ # string.stripped #=> "Normal red"
141
+ #
142
+ # @return [String] the concatenation of all segment text without color or style
143
+ def stripped
144
+ map(&:text).join
145
+ end
146
+
147
+ # Return the fully reconstructed string with **all ANSI sequences** (foreground, background, style)
148
+ #
149
+ # @author {https://aaronmallen.me Aaron Allen}
150
+ # @since 0.3.0
151
+ #
152
+ # @api public
153
+ #
154
+ # @example
155
+ # string = SequencedString.new("\e[31mred\e[0m")
156
+ # string.to_s #=> "\e[31mred\e[0m"
157
+ #
158
+ # @return [String]
159
+ def to_s
160
+ build_string
161
+ end
162
+ alias to_str to_s
163
+
164
+ # Return a string with everything except **background** color sequences removed
165
+ #
166
+ # @author {https://aaronmallen.me Aaron Allen}
167
+ # @since 0.3.0
168
+ #
169
+ # @api public
170
+ #
171
+ # @example Remove all background colors
172
+ # string = SequencedString.new("\e[41mBack\e[0m \e[1mBold\e[0m")
173
+ # string.without_background #=> "\e[1mBold\e[0m"
174
+ #
175
+ # @return [SequencedString] new instance with background colors removed
176
+ # @rbs () -> SequencedString
177
+ def without_background
178
+ self.class.new(build_string(skip_background: true))
179
+ end
180
+
181
+ # Return a string containing *style* sequences but **no foreground or background colors**
182
+ #
183
+ # @author {https://aaronmallen.me Aaron Allen}
184
+ # @since 0.3.0
185
+ #
186
+ # @api public
187
+ #
188
+ # @example Remove all colors
189
+ # string = SequencedString.new("\e[31mred\e[0m \e[1mbold\e[0m")
190
+ # string.without_color #=> "\e[1mbold\e[0m"
191
+ #
192
+ # @return [SequencedString] new instance with all colors removed
193
+ # @rbs () -> SequencedString
194
+ def without_color
195
+ self.class.new(build_string(skip_background: true, skip_foreground: true))
196
+ end
197
+
198
+ # Return a string with everything except **foreground** color sequences removed
199
+ #
200
+ # @author {https://aaronmallen.me Aaron Allen}
201
+ # @since 0.3.0
202
+ #
203
+ # @api public
204
+ #
205
+ # @example Remove all foreground colors
206
+ # string = SequencedString.new("\e[41mBack\e[0m \e[1mBold\e[0m")
207
+ # string.without_foreground #=> "\e[41mBack\e[0m \e[1mBold\e[0m"
208
+ #
209
+ # @return [SequencedString] new instance with foreground colors removed
210
+ # @rbs () -> SequencedString
211
+ def without_foreground
212
+ self.class.new(build_string(skip_foreground: true))
213
+ end
214
+
215
+ # Return a string with specified styles removed
216
+ #
217
+ # @author {https://aaronmallen.me Aaron Allen}
218
+ # @since 0.3.0
219
+ #
220
+ # @api public
221
+ #
222
+ # @example Remove all styles
223
+ # string = SequencedString.new("\e[31mred\e[0m \e[1mbold\e[0m")
224
+ # string.without_style #=> "\e[31mred\e[0m"
225
+ #
226
+ # @example Remove specific style
227
+ # string = SequencedString.new("\e[1;4mBold and Underlined\e[0m")
228
+ # string.without_style(:bold) #=> "\e[4mUnderlined\e[0m"
229
+ #
230
+ # @param styles [Array<Symbol>] specific styles to remove (default: all)
231
+ #
232
+ # @return [SequencedString] new instance with specified styles removed
233
+ def without_style(*styles)
234
+ skipped_styles = styles.empty? ? ANSI::STYLES.keys : styles.map(&:to_sym)
235
+ self.class.new(build_string(skip_styles: skipped_styles))
236
+ end
237
+
238
+ private
239
+
240
+ # Build the color sequences for a segment
241
+ #
242
+ # @author {https://aaronmallen.me Aaron Allen}
243
+ # @since 0.3.0
244
+ #
245
+ # @api private
246
+ #
247
+ # @param segment [Segment] the segment to build color sequences for
248
+ # @param skip_background [Boolean] whether to skip background colors
249
+ # @param skip_foreground [Boolean] whether to skip foreground colors
250
+ #
251
+ # @return [Array<String>] the color sequences
252
+ # @rbs (Segment segment, ?skip_background: bool, ?skip_foreground: bool) -> Array[String]
253
+ def build_color_sequences(segment, skip_background: false, skip_foreground: false)
254
+ [
255
+ (skip_foreground ? nil : segment.foreground),
256
+ (skip_background ? nil : segment.background)
257
+ ].compact
258
+ end
259
+
260
+ # Build a string with specified parts skipped
261
+ #
262
+ # @author {https://aaronmallen.me Aaron Allen}
263
+ # @since 0.3.0
264
+ #
265
+ # @api private
266
+ #
267
+ # @param skip_background [Boolean] whether to skip background colors
268
+ # @param skip_foreground [Boolean] whether to skip foreground colors
269
+ # @param skip_styles [Array<Symbol>] styles to skip
270
+ #
271
+ # @return [String] the built string
272
+ # @rbs (?skip_background: bool, ?skip_foreground: bool, ?skip_styles: Array[Symbol]) -> String
273
+ def build_string(skip_background: false, skip_foreground: false, skip_styles: [])
274
+ map do |segment|
275
+ color_sequences = build_color_sequences(segment, skip_background:, skip_foreground:)
276
+ style_sequences = build_style_sequences(segment, skip_styles: skip_styles)
277
+ sequences = color_sequences + style_sequences
278
+
279
+ out = sequences.empty? ? +'' : "\e[#{sequences.compact.join(';')}m"
280
+ out << segment.text
281
+ out << ANSI::RESET unless sequences.empty?
282
+ out
283
+ end.join
284
+ end
285
+
286
+ # Build the style sequences for a segment
287
+ #
288
+ # @author {https://aaronmallen.me Aaron Allen}
289
+ # @since 0.3.0
290
+ #
291
+ # @api private
292
+ #
293
+ # @param segment [Segment] the segment to build style sequences for
294
+ # @param skip_styles [Array<Symbol>] styles to skip
295
+ #
296
+ # @return [Array<String>] the style sequences
297
+ # @rbs (Segment segment, ?skip_styles: Array[Symbol]) -> Array[String]
298
+ def build_style_sequences(segment, skip_styles: [])
299
+ return [] if skip_styles.include?(:all)
300
+
301
+ segment.styles.filter_map do |style_code|
302
+ style_name = ANSI::STYLES.key(style_code.to_i)
303
+ style_code unless skip_styles.include?(style_name)
304
+ end
305
+ end
306
+
307
+ # A segment of an ANSI encoded string
308
+ #
309
+ # @author {https://aaronmallen.me Aaron Allen}
310
+ # @since 0.3.0
311
+ #
312
+ # @api public
313
+ class Segment
314
+ # The background color sequences for the Segment
315
+ #
316
+ # @author {https://aaronmallen.me Aaron Allen}
317
+ # @since 0.3.0
318
+ #
319
+ # @api public
320
+ #
321
+ # @return [String, nil] the background color sequences
322
+ attr_reader :background #: String?
323
+
324
+ # The foreground color sequences for the Segment
325
+ #
326
+ # @author {https://aaronmallen.me Aaron Allen}
327
+ # @since 0.3.0
328
+ #
329
+ # @api public
330
+ #
331
+ # @return [String, nil] the foreground color sequences
332
+ attr_reader :foreground #: String?
333
+
334
+ # The {Location} of the encoded string within the {SequencedString}
335
+ #
336
+ # @author {https://aaronmallen.me Aaron Allen}
337
+ # @since 0.3.0
338
+ #
339
+ # @api public
340
+ #
341
+ # @return [Location] the {Location}
342
+ attr_reader :encoded_location #: Location
343
+ alias encoded_loc encoded_location
344
+
345
+ # The {Location} of the encoded string without it's encoding within the {SequencedString}
346
+ #
347
+ # @author {https://aaronmallen.me Aaron Allen}
348
+ # @since 0.3.0
349
+ #
350
+ # @api public
351
+ #
352
+ # @return [Location] the {Location}
353
+ attr_reader :stripped_location #: Location
354
+ alias stripped_loc stripped_location
355
+
356
+ # The style sequences (bold, underline, etc...) for the segment
357
+ #
358
+ # @author {https://aaronmallen.me Aaron Allen}
359
+ # @since 0.3.0
360
+ #
361
+ # @api public
362
+ #
363
+ # @return [Array<String>] the style sequences
364
+ attr_reader :styles #: Array[String]
365
+
366
+ # The raw text of the Segment without any of its ANSI sequences
367
+ #
368
+ # @author {https://aaronmallen.me Aaron Allen}
369
+ # @since 0.3.0
370
+ #
371
+ # @api public
372
+ #
373
+ # @return [String]
374
+ attr_reader :text #: String
375
+
376
+ # Initialize a new instance of Segment
377
+ #
378
+ # @author {https://aaronmallen.me Aaron Allen}
379
+ # @since 0.3.0
380
+ #
381
+ # @api private
382
+ #
383
+ # @param options [Hash{Symbol => Object}] the options to initialize the Segment with
384
+ # @option options background [String, nil] the Segment {#background}
385
+ # @option options foreground [String, nil] the Segment {#foreground}
386
+ # @option options encoded_end [Integer] the {Location#end_position end_position} of the Segment
387
+ # {#encoded_location}
388
+ # @option options encoded_start [Integer] the {Location#start_position start_position} of the Segment
389
+ # {#encoded_location}
390
+ # @option options stripped_end [Integer] the {Location#end_position end_position} of the Segment
391
+ # {#stripped_location}
392
+ # @option options stripped_start [Integer] the {Location#start_position start_position} of the Segment
393
+ # {#stripped_location}
394
+ # @option options styles [Array<String>] the Segment {#styles}
395
+ # @option options text [String] the Segment {#text}
396
+ #
397
+ # @return [Segment] the new instance of Segment
398
+ # @rbs (
399
+ # ?background: String?,
400
+ # ?foreground: String?,
401
+ # encoded_end: Integer,
402
+ # encoded_start: Integer,
403
+ # stripped_end: Integer,
404
+ # stripped_start: Integer,
405
+ # ?styles: Array[String],
406
+ # text: String
407
+ # ) -> void
408
+ def initialize(**options)
409
+ @background = options.fetch(:background, nil)
410
+ @foreground = options.fetch(:foreground, nil)
411
+ @encoded_location = Location.new(
412
+ end_position: options.fetch(:encoded_end), #: Integer
413
+ start_position: options.fetch(:encoded_start) #: Integer
414
+ )
415
+ @stripped_location = Location.new(
416
+ end_position: options.fetch(:stripped_end), #: Integer
417
+ start_position: options.fetch(:stripped_start) #: Integer
418
+ )
419
+ @styles = options.fetch(:styles, [])
420
+ @text = options.fetch(:text)
421
+
422
+ freeze
423
+ end
424
+
425
+ # The location of the {Segment} within a {SequencedString}
426
+ #
427
+ # @author {https://aaronmallen.me Aaron Allen}
428
+ # @since 0.3.0
429
+ #
430
+ # @api public
431
+ class Location
432
+ # The ending position of the Location
433
+ #
434
+ # @author {https://aaronmallen.me Aaron Allen}
435
+ # @since 0.3.0
436
+ #
437
+ # @api public
438
+ #
439
+ # @return [Integer] the end position
440
+ attr_reader :end_position #: Integer
441
+ alias end_pos end_position
442
+
443
+ # The starting position of the Location
444
+ #
445
+ # @author {https://aaronmallen.me Aaron Allen}
446
+ # @since 0.3.0
447
+ #
448
+ # @api public
449
+ #
450
+ # @return [Integer] the start position
451
+ attr_reader :start_position #: Integer
452
+ alias start_pos start_position
453
+
454
+ # Initialize a new instance of Location
455
+ #
456
+ # @author {https://aaronmallen.me Aaron Allen}
457
+ # @since 0.3.0
458
+ #
459
+ # @api private
460
+ #
461
+ # @param end_position [Integer] the {#end_position} of the location
462
+ # @param start_position [Integer] the {#start_position} of the location
463
+ #
464
+ # @return [Location] the new instance of Location
465
+ # @rbs (end_position: Integer, start_position: Integer) -> void
466
+ def initialize(end_position:, start_position:)
467
+ @end_position = end_position
468
+ @start_position = start_position
469
+ freeze
470
+ end
471
+ end
472
+ end
473
+ end
474
+ end
475
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sai
4
+ module ANSI
5
+ # Handles parsing of ANSI style codes
6
+ #
7
+ # @author {https://aaronmallen.me Aaron Allen}
8
+ # @since 0.3.1
9
+ #
10
+ # @api private
11
+ class StyleParser
12
+ # Matches the code portion of style sequences
13
+ #
14
+ # @author {https://aaronmallen.me Aaron Allen}
15
+ # @since 0.3.1
16
+ #
17
+ # @api private
18
+ #
19
+ # @return [Regexp] the pattern
20
+ STYLE_CODE_PATTERN = /^(?:[1-9]|2[1-9])$/ #: Regexp
21
+ private_constant :STYLE_CODE_PATTERN
22
+
23
+ # The current segment being processed
24
+ #
25
+ # @author {https://aaronmallen.me Aaron Allen}
26
+ # @since 0.3.1
27
+ #
28
+ # @api private
29
+ #
30
+ # @return [Hash] the current segment being processed
31
+ attr_reader :segment #: Hash[Symbol, untyped]
32
+
33
+ # Initialize a new instance of StyleParser
34
+ #
35
+ # @author {https://aaronmallen.me Aaron Allen}
36
+ # @since 0.3.1
37
+ #
38
+ # @api private
39
+ #
40
+ # @param segment [Hash] the segment to update
41
+ #
42
+ # @return [StyleParser] the new instance of StyleParser
43
+ # @rbs (Hash[Symbol, untyped] segment) -> void
44
+ def initialize(segment)
45
+ @segment = segment
46
+ end
47
+
48
+ # Parse a style code
49
+ #
50
+ # @author {https://aaronmallen.me Aaron Allen}
51
+ # @since 0.3.1
52
+ #
53
+ # @api private
54
+ #
55
+ # @param code [Integer] the style code
56
+ #
57
+ # @return [void]
58
+ # @rbs (Integer code) -> void
59
+ def parse(code)
60
+ return unless code.to_s.match?(STYLE_CODE_PATTERN)
61
+
62
+ segment[:styles] << code.to_s
63
+ end
64
+ end
65
+ end
66
+ end
data/lib/sai/ansi.rb CHANGED
@@ -27,33 +27,6 @@ module Sai
27
27
  white: 7
28
28
  }.freeze # Hash[Symbol, Integer]
29
29
 
30
- # Standard ANSI color names and their RGB values
31
- #
32
- # @author {https://aaronmallen.me Aaron Allen}
33
- # @since 0.1.0
34
- #
35
- # @api private
36
- #
37
- # @return [Hash{Symbol => Array<Integer>}] the color names and RGB values
38
- COLOR_NAMES = {
39
- black: [0, 0, 0],
40
- red: [205, 0, 0],
41
- green: [0, 205, 0],
42
- yellow: [205, 205, 0],
43
- blue: [0, 0, 238],
44
- magenta: [205, 0, 205],
45
- cyan: [0, 205, 205],
46
- white: [229, 229, 229],
47
- bright_black: [127, 127, 127],
48
- bright_red: [255, 0, 0],
49
- bright_green: [0, 255, 0],
50
- bright_yellow: [255, 255, 0],
51
- bright_blue: [92, 92, 255],
52
- bright_magenta: [255, 0, 255],
53
- bright_cyan: [0, 255, 255],
54
- bright_white: [255, 255, 255]
55
- }.freeze # Hash[Symbol, Array[Integer]]
56
-
57
30
  # ANSI escape sequence for resetting text formatting
58
31
  #
59
32
  # @author {https://aaronmallen.me Aaron Allen}
@@ -61,9 +61,9 @@ module Sai
61
61
  def advanced(rgb, style_type)
62
62
  code = style_type == :background ? 48 : 38
63
63
  color_code = if rgb.uniq.size == 1
64
- RGB.to_grayscale_index(rgb)
64
+ RGB.index.grayscale(rgb)
65
65
  else
66
- RGB.to_color_cube_index(rgb)
66
+ RGB.index.color_cube(rgb)
67
67
  end
68
68
 
69
69
  "\e[#{code};5;#{color_code}m"
@@ -86,7 +86,7 @@ module Sai
86
86
  brightness = (r + g + b) / 3.0
87
87
  is_bright = brightness > 0.5
88
88
 
89
- color = RGB.closest_ansi_color(r, g, b)
89
+ color = RGB.classify.closest_ansi_color(r, g, b)
90
90
  code = base_color_for_style_type(ANSI::COLOR_CODES[color], style_type)
91
91
  code += 60 if is_bright
92
92
  "\e[#{code}m"
@@ -122,7 +122,7 @@ module Sai
122
122
  # @rbs (Array[Integer] rgb, style_type style_type) -> String
123
123
  def basic(rgb, style_type)
124
124
  r, g, b = rgb.map { |c| c / 255.0 } #: [Float, Float, Float]
125
- color = RGB.closest_ansi_color(r, g, b)
125
+ color = RGB.classify.closest_ansi_color(r, g, b)
126
126
  code = base_color_for_style_type(ANSI::COLOR_CODES[color], style_type)
127
127
  "\e[#{code}m"
128
128
  end