sai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +18 -0
- data/LICENSE +21 -0
- data/README.md +275 -0
- data/lib/sai/ansi.rb +94 -0
- data/lib/sai/conversion/color_sequence.rb +167 -0
- data/lib/sai/conversion/rgb.rb +322 -0
- data/lib/sai/decorator.rb +856 -0
- data/lib/sai/support.rb +115 -0
- data/lib/sai/terminal/capabilities.rb +121 -0
- data/lib/sai/terminal/color_mode.rb +63 -0
- data/lib/sai.rb +185 -0
- data/sig/sai/ansi.rbs +51 -0
- data/sig/sai/conversion/color_sequence.rbs +114 -0
- data/sig/sai/conversion/rgb.rbs +243 -0
- data/sig/sai/decorator.rbs +246 -0
- data/sig/sai/support.rbs +108 -0
- data/sig/sai/terminal/capabilities.rbs +81 -0
- data/sig/sai/terminal/color_mode.rbs +63 -0
- data/sig/sai.rbs +207 -0
- metadata +67 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1856d33db7e7b71a3ad164df38fc64a732e806540584e5e207ed6ee6220d0e07
|
4
|
+
data.tar.gz: f2aa1b1182b297dcdd14c97b01928e33b56e2bee79268e3f344ea4df315a338f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 318ead79460d995f10d20a031d8d9be16277bd5180d5663f32ba7a0f1aac3dbdfa5f7e7fba6c71c62662cbf9486db35b7cb95e6562ff3df9860cd837f2ca9125
|
7
|
+
data.tar.gz: a82a2ce509d1d6159cbac457d788e48bf88bb07498449beffedbf1fb10aa08a2e614c375f0af3207a22b4b6b32fe0b595137e6b36276f011e11489a786ec51af
|
data/.yardopts
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog], and this project adheres to [Break Versioning].
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
## 0.1.0 - 2025-01-19
|
10
|
+
|
11
|
+
* Initial release
|
12
|
+
|
13
|
+
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
14
|
+
[Break Versioning]: https://www.taoensso.com/break-versioning
|
15
|
+
|
16
|
+
<!-- versions -->
|
17
|
+
|
18
|
+
[Unreleased]: https://github.com/aaronmallen/sai/compare/0.1.0..HEAD
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Aaron Allen
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
# Sai
|
2
|
+
|
3
|
+
[](https://rubygems.org/gems/sai)
|
4
|
+
[](./LICENSE)
|
5
|
+
[](https://rubydoc.info/gems/sai/0.1.0)
|
6
|
+
[](https://github.com/aaronmallen/sai/issues?q=state%3Aopen%20)
|
7
|
+
|
8
|
+
An elegant color management system for crafting sophisticated CLI applications
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
puts Sai.rgb(255, 128, 0).bold.decorate('Create beautiful CLI applications')
|
12
|
+
puts Sai.hex('#4834d4').italic.decorate('with intuitive color management')
|
13
|
+
puts Sai.bright_cyan.on_blue.underline.decorate('that adapts to any terminal')
|
14
|
+
```
|
15
|
+
|
16
|
+
Sai (彩) - meaning 'coloring' or 'paint' in Japanese - is a powerful and intuitive system for managing color output in
|
17
|
+
command-line applications. Drawing inspiration from traditional Japanese artistic techniques, Sai brings vibrancy and
|
18
|
+
harmony to terminal interfaces through its sophisticated color management.
|
19
|
+
|
20
|
+
Sai empowers developers to create beautiful, colorful CLI applications that maintain visual consistency across different
|
21
|
+
terminal capabilities. Like its artistic namesake, it combines simplicity and sophistication to bring rich, adaptive
|
22
|
+
color to your terminal interfaces.
|
23
|
+
|
24
|
+
## Features
|
25
|
+
|
26
|
+
* Automatic color mode detection and downgrading
|
27
|
+
* Support for True Color (24-bit), 256 colors (8-bit), ANSI colors (4-bit), and basic colors (3-bit)
|
28
|
+
* Rich set of ANSI text styles
|
29
|
+
* Named color support with bright variants
|
30
|
+
* RGB and Hex color support
|
31
|
+
* Foreground and background colors
|
32
|
+
* Respects NO_COLOR environment variable
|
33
|
+
* Can be used directly or included in classes/modules
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
gem 'sai'
|
41
|
+
```
|
42
|
+
|
43
|
+
Or install it yourself as:
|
44
|
+
|
45
|
+
```bash
|
46
|
+
gem install sai
|
47
|
+
```
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
Sai can be used directly or included in your own classes and modules.
|
52
|
+
|
53
|
+
### Direct Usage
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'sai'
|
57
|
+
|
58
|
+
# Using named colors
|
59
|
+
puts Sai.red.decorate('Error!')
|
60
|
+
puts Sai.bright_blue.on_white.decorate('Info')
|
61
|
+
|
62
|
+
# Using RGB colors
|
63
|
+
puts Sai.rgb(255, 128, 0).decorate('Custom color')
|
64
|
+
puts Sai.on_rgb(0, 255, 128).decorate('Custom background')
|
65
|
+
|
66
|
+
# Using hex colors
|
67
|
+
puts Sai.hex('#FF8000').decorate('Hex color')
|
68
|
+
puts Sai.on_hex('#00FF80').decorate('Hex background')
|
69
|
+
|
70
|
+
# Applying styles
|
71
|
+
puts Sai.bold.underline.decorate('Important')
|
72
|
+
puts Sai.red.bold.italic.decorate('Error!')
|
73
|
+
|
74
|
+
# Complex combinations
|
75
|
+
puts Sai.bright_cyan
|
76
|
+
.on_blue
|
77
|
+
.bold
|
78
|
+
.italic
|
79
|
+
.decorate('Styled text')
|
80
|
+
```
|
81
|
+
|
82
|
+
### Module Inclusion
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
class CLI
|
86
|
+
include Sai
|
87
|
+
|
88
|
+
def error(message)
|
89
|
+
puts decorator.red.bold.decorate(message)
|
90
|
+
end
|
91
|
+
|
92
|
+
def info(message)
|
93
|
+
puts decorator.bright_blue.decorate(message)
|
94
|
+
end
|
95
|
+
|
96
|
+
def success(message)
|
97
|
+
puts decorator.green.decorate(message)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
cli = CLI.new
|
102
|
+
cli.error('Something went wrong!')
|
103
|
+
cli.info('Processing...')
|
104
|
+
cli.success('Done!')
|
105
|
+
```
|
106
|
+
|
107
|
+
### Defining Reusable Styles
|
108
|
+
|
109
|
+
Sai decorators can be assigned to constants to create reusable styles throughout your application:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
module Style
|
113
|
+
ERROR = Sai.bold.bright_white.on_red
|
114
|
+
WARNING = Sai.black.on_yellow
|
115
|
+
SUCCESS = Sai.bright_green
|
116
|
+
INFO = Sai.bright_blue
|
117
|
+
HEADER = Sai.bold.bright_cyan.underline
|
118
|
+
end
|
119
|
+
|
120
|
+
# Use your defined styles
|
121
|
+
puts Style::ERROR.decorate('Something went wrong!')
|
122
|
+
puts Style::SUCCESS.decorate('Operation completed successfully')
|
123
|
+
|
124
|
+
# Styles can be further customized when needed
|
125
|
+
puts Style::ERROR.italic.decorate('Critical error!')
|
126
|
+
```
|
127
|
+
|
128
|
+
> [!TIP]
|
129
|
+
> This pattern is particularly useful for maintaining consistent styling across your application and creating theme
|
130
|
+
> systems. You can even build on existing styles:
|
131
|
+
>
|
132
|
+
> ```ruby
|
133
|
+
> module Theme
|
134
|
+
> PRIMARY = Sai.rgb(63, 81, 181)
|
135
|
+
> SECONDARY = Sai.rgb(255, 87, 34)
|
136
|
+
>
|
137
|
+
> BUTTON = PRIMARY.bold
|
138
|
+
> LINK = SECONDARY.underline
|
139
|
+
> HEADER = PRIMARY.bold.underline
|
140
|
+
> end
|
141
|
+
> ```
|
142
|
+
|
143
|
+
## Features
|
144
|
+
|
145
|
+
### Color Support
|
146
|
+
|
147
|
+
Sai supports up to 16.7 million colors (true color/24-bit) depending on your terminal's capabilities. Colors can be
|
148
|
+
specified in several ways:
|
149
|
+
|
150
|
+
#### RGB Colors
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
# Specify any RGB color (0-255 per channel)
|
154
|
+
Sai.rgb(255, 128, 0).decorate('Orange text')
|
155
|
+
Sai.on_rgb(0, 255, 128).decorate('Custom green background')
|
156
|
+
```
|
157
|
+
|
158
|
+
#### Hex Colors
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# Use any hex color code
|
162
|
+
Sai.hex('#FF8000').decorate('Orange text')
|
163
|
+
Sai.on_hex('#00FF80').decorate('Custom green background')
|
164
|
+
```
|
165
|
+
|
166
|
+
#### Named Color Shortcuts
|
167
|
+
|
168
|
+
For convenience, Sai provides shortcuts for common ANSI colors:
|
169
|
+
|
170
|
+
Standard colors:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
Sai.red.decorate('Red text')
|
174
|
+
Sai.blue.decorate('Blue text')
|
175
|
+
Sai.on_green.decorate('Green background')
|
176
|
+
```
|
177
|
+
|
178
|
+
Bright variants:
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
Sai.bright_red.decorate('Bright red text')
|
182
|
+
Sai.bright_blue.decorate('Bright blue text')
|
183
|
+
Sai.on_bright_green.decorate('Bright green background')
|
184
|
+
```
|
185
|
+
|
186
|
+
Available named colors:
|
187
|
+
|
188
|
+
* black/bright_black
|
189
|
+
* red/bright_red
|
190
|
+
* green/bright_green
|
191
|
+
* yellow/bright_yellow
|
192
|
+
* blue/bright_blue
|
193
|
+
* magenta/bright_magenta
|
194
|
+
* cyan/bright_cyan
|
195
|
+
* white/bright_white
|
196
|
+
|
197
|
+
> [!TIP]
|
198
|
+
> While named colors provide convenient shortcuts, remember that Sai supports the full RGB color space. Don't feel
|
199
|
+
> limited to just these predefined colors!
|
200
|
+
|
201
|
+
### Text Styles
|
202
|
+
|
203
|
+
Available styles:
|
204
|
+
|
205
|
+
* bold
|
206
|
+
* dim
|
207
|
+
* italic
|
208
|
+
* underline
|
209
|
+
* blink
|
210
|
+
* rapid_blink
|
211
|
+
* reverse
|
212
|
+
* conceal
|
213
|
+
* strike
|
214
|
+
|
215
|
+
Style removal:
|
216
|
+
|
217
|
+
* no_blink
|
218
|
+
* no_italic
|
219
|
+
* no_underline
|
220
|
+
* no_reverse
|
221
|
+
* no_conceal
|
222
|
+
* no_strike
|
223
|
+
* normal_intensity
|
224
|
+
|
225
|
+
### Color Mode Detection & Downgrading
|
226
|
+
|
227
|
+
Sai automatically detects your terminal's color capabilities and adapts the output appropriately:
|
228
|
+
|
229
|
+
* True Color terminals (24-bit): Full access to all 16.7 million colors
|
230
|
+
* 256-color terminals (8-bit): Colors are mapped to the closest available color in the 256-color palette
|
231
|
+
* ANSI terminals (4-bit): Colors are mapped to the 16 standard ANSI colors
|
232
|
+
* Basic terminals (3-bit): Colors are mapped to the 8 basic ANSI colors
|
233
|
+
* NO_COLOR: All color sequences are stripped when the NO_COLOR environment variable is set
|
234
|
+
|
235
|
+
> [!NOTE]
|
236
|
+
> This automatic downgrading ensures your application looks great across all terminal types without any extra code!
|
237
|
+
|
238
|
+
### Terminal Support Detection
|
239
|
+
|
240
|
+
You can check the terminal's capabilities:
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
# Using directly
|
244
|
+
Sai.support.true_color? # => true/false
|
245
|
+
Sai.support.bit8? # => true/false
|
246
|
+
Sai.support.ansi? # => true/false
|
247
|
+
Sai.support.basic? # => true/false
|
248
|
+
Sai.support.color? # => true/false
|
249
|
+
|
250
|
+
# Using included module
|
251
|
+
class CLI
|
252
|
+
include Sai
|
253
|
+
|
254
|
+
def check_support
|
255
|
+
if terminal_color_support.true_color?
|
256
|
+
puts "Terminal supports true color!"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
## Contributing
|
263
|
+
|
264
|
+
We welcome contributions! Please see our [Contributing Guidelines](docs/CONTRIBUTING.md) for:
|
265
|
+
|
266
|
+
* Development setup and workflow
|
267
|
+
* Code style and documentation standards
|
268
|
+
* Testing requirements
|
269
|
+
* Pull request process
|
270
|
+
|
271
|
+
Before contributing, please review our [Code of Conduct](docs/CODE_OF_CONDUCT.md).
|
272
|
+
|
273
|
+
## License
|
274
|
+
|
275
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
data/lib/sai/ansi.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sai
|
4
|
+
# ANSI constants for encoding text styles and colors
|
5
|
+
#
|
6
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
7
|
+
# @since unreleased
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
module ANSI
|
11
|
+
# ANSI color code mappings
|
12
|
+
#
|
13
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
14
|
+
# @since unreleased
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
# @return [Hash{Symbol => Integer}] the color codes
|
19
|
+
COLOR_CODES = {
|
20
|
+
black: 0,
|
21
|
+
red: 1,
|
22
|
+
green: 2,
|
23
|
+
yellow: 3,
|
24
|
+
blue: 4,
|
25
|
+
magenta: 5,
|
26
|
+
cyan: 6,
|
27
|
+
white: 7
|
28
|
+
}.freeze # Hash[Symbol, Integer]
|
29
|
+
|
30
|
+
# Standard ANSI color names and their RGB values
|
31
|
+
#
|
32
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
33
|
+
# @since unreleased
|
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
|
+
# ANSI escape sequence for resetting text formatting
|
58
|
+
#
|
59
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
60
|
+
# @since unreleased
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
# @return [String] the ANSI escape sequence
|
65
|
+
RESET = "\e[0m" # String
|
66
|
+
|
67
|
+
# Standard ANSI style codes
|
68
|
+
#
|
69
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
70
|
+
# @since unreleased
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
#
|
74
|
+
# @return [Hash{Symbol => Integer}] the style codes
|
75
|
+
STYLES = {
|
76
|
+
bold: 1,
|
77
|
+
dim: 2,
|
78
|
+
italic: 3,
|
79
|
+
underline: 4,
|
80
|
+
blink: 5,
|
81
|
+
rapid_blink: 6,
|
82
|
+
reverse: 7,
|
83
|
+
conceal: 8,
|
84
|
+
strike: 9,
|
85
|
+
normal_intensity: 22,
|
86
|
+
no_italic: 23,
|
87
|
+
no_underline: 24,
|
88
|
+
no_blink: 25,
|
89
|
+
no_reverse: 27,
|
90
|
+
no_conceal: 28,
|
91
|
+
no_strike: 29
|
92
|
+
}.freeze # Hash[Symbol, Integer]
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sai/ansi'
|
4
|
+
require 'sai/conversion/rgb'
|
5
|
+
require 'sai/terminal/color_mode'
|
6
|
+
|
7
|
+
module Sai
|
8
|
+
module Conversion
|
9
|
+
# ANSI escape sequence utilities
|
10
|
+
#
|
11
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
12
|
+
# @since unreleased
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
module ColorSequence
|
16
|
+
# @rbs!
|
17
|
+
# type style_type = :foreground | :background
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# Convert a color to the appropriate ANSI escape sequence
|
21
|
+
#
|
22
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
23
|
+
# @since unreleased
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
# @param color [String, Array<Integer>] the color to convert
|
28
|
+
# @param mode [Integer] the terminal color mode
|
29
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
30
|
+
#
|
31
|
+
# @return [String] the ANSI escape sequence
|
32
|
+
# @rbs (Array[Integer] | String | Symbol color, Integer mode, ?style_type style_type) -> String
|
33
|
+
def resolve(color, mode, style_type = :foreground)
|
34
|
+
rgb = RGB.resolve(color)
|
35
|
+
style_type = validate_style_type(style_type) #: style_type
|
36
|
+
|
37
|
+
case mode
|
38
|
+
when Terminal::ColorMode::TRUE_COLOR then true_color(rgb, style_type)
|
39
|
+
when Terminal::ColorMode::BIT8 then bit8(rgb, style_type)
|
40
|
+
when Terminal::ColorMode::ANSI then ansi(rgb, style_type)
|
41
|
+
when Terminal::ColorMode::BASIC then basic(rgb, style_type)
|
42
|
+
else
|
43
|
+
''
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Convert RGB values to a 4-bit ANSI color sequence
|
50
|
+
#
|
51
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
52
|
+
# @since unreleased
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
#
|
56
|
+
# @param rgb [Array<Integer>] the RGB components
|
57
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
58
|
+
#
|
59
|
+
# @return [String] the ANSI escape sequence
|
60
|
+
# @rbs (Array[Integer] rgb, style_type style_type) -> String
|
61
|
+
def ansi(rgb, style_type)
|
62
|
+
r, g, b = rgb.map { |c| c / 255.0 } #: [Float, Float, Float]
|
63
|
+
brightness = (r + g + b) / 3.0
|
64
|
+
is_bright = brightness > 0.5
|
65
|
+
|
66
|
+
color = RGB.closest_ansi_color(r, g, b)
|
67
|
+
code = base_color_for_style_type(ANSI::COLOR_CODES[color], style_type)
|
68
|
+
code += 60 if is_bright
|
69
|
+
"\e[#{code}m"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Convert a base color to a foreground or background sequence
|
73
|
+
#
|
74
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
75
|
+
# @since unreleased
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
#
|
79
|
+
# @param base_code [Integer] the base color code
|
80
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
81
|
+
#
|
82
|
+
# @return [Integer] the code for the color sequence
|
83
|
+
# @rbs (Integer base_code, style_type style_type) -> Integer
|
84
|
+
def base_color_for_style_type(base_code, style_type)
|
85
|
+
style_type == :background ? base_code + 40 : base_code + 30
|
86
|
+
end
|
87
|
+
|
88
|
+
# Convert RGB values to a 3-bit basic color sequence
|
89
|
+
#
|
90
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
91
|
+
# @since unreleased
|
92
|
+
#
|
93
|
+
# @api private
|
94
|
+
#
|
95
|
+
# @param rgb [Array<Integer>] the RGB components
|
96
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
97
|
+
#
|
98
|
+
# @return [String] the ANSI escape sequence
|
99
|
+
# @rbs (Array[Integer] rgb, style_type style_type) -> String
|
100
|
+
def basic(rgb, style_type)
|
101
|
+
r, g, b = rgb.map { |c| c / 255.0 } #: [Float, Float, Float]
|
102
|
+
color = RGB.closest_ansi_color(r, g, b)
|
103
|
+
code = base_color_for_style_type(ANSI::COLOR_CODES[color], style_type)
|
104
|
+
"\e[#{code}m"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Convert RGB values to an 8-bit color sequence
|
108
|
+
#
|
109
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
110
|
+
# @since unreleased
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
#
|
114
|
+
# @param rgb [Array<Integer>] the RGB components
|
115
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
116
|
+
#
|
117
|
+
# @return [String] the ANSI escape sequence
|
118
|
+
# @rbs (Array[Integer] rgb, style_type type) -> String
|
119
|
+
def bit8(rgb, style_type)
|
120
|
+
code = style_type == :background ? 48 : 38
|
121
|
+
color_code = if rgb.uniq.size == 1
|
122
|
+
RGB.to_grayscale_index(rgb)
|
123
|
+
else
|
124
|
+
RGB.to_color_cube_index(rgb)
|
125
|
+
end
|
126
|
+
|
127
|
+
"\e[#{code};5;#{color_code}m"
|
128
|
+
end
|
129
|
+
|
130
|
+
# Convert RGB values to a true color (24-bit) sequence
|
131
|
+
#
|
132
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
133
|
+
# @since unreleased
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
#
|
137
|
+
# @param rgb [Array<Integer>] the RGB components
|
138
|
+
# @param style_type [Symbol] the type of color (foreground or background)
|
139
|
+
#
|
140
|
+
# @return [String] the ANSI escape sequence
|
141
|
+
# @rbs (Array[Integer] rgb, style_type type) -> String
|
142
|
+
def true_color(rgb, style_type)
|
143
|
+
code = style_type == :background ? 48 : 38
|
144
|
+
"\e[#{code};2;#{rgb.join(';')}m"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Validate a color style type
|
148
|
+
#
|
149
|
+
# @author {https://aaronmallen.me Aaron Allen}
|
150
|
+
# @since unreleased
|
151
|
+
#
|
152
|
+
# @api private
|
153
|
+
#
|
154
|
+
# @param style_type [Symbol] the style type to validate
|
155
|
+
#
|
156
|
+
# @raise [ArgumentError] if the style type is invalid
|
157
|
+
# @return [Symbol] the validated style type
|
158
|
+
# @rbs (Symbol style_type) -> Symbol
|
159
|
+
def validate_style_type(style_type)
|
160
|
+
raise ArgumentError, "Invalid style type: #{style_type}" unless %i[foreground background].include?(style_type)
|
161
|
+
|
162
|
+
style_type
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|