abachrome 0.1.1 → 0.1.3
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 +4 -4
- data/CHANGELOG.md +12 -1
- data/CLA.md +45 -0
- data/CODE-OF-CONDUCT.md +9 -0
- data/LICENSE +19 -0
- data/README.md +212 -6
- data/Rakefile +8 -5
- data/SECURITY.md +94 -0
- data/abachrome.gemspec +36 -0
- data/devenv.lock +8 -8
- data/lib/abachrome/abc_decimal.rb +2 -2
- data/lib/abachrome/color.rb +13 -1
- data/lib/abachrome/color_models/lms.rb +34 -0
- data/lib/abachrome/color_models/xyz.rb +28 -0
- data/lib/abachrome/color_space.rb +12 -0
- data/lib/abachrome/converter.rb +1 -1
- data/lib/abachrome/converters/lms_to_lrgb.rb +36 -0
- data/lib/abachrome/converters/lms_to_srgb.rb +23 -0
- data/lib/abachrome/converters/lms_to_xyz.rb +30 -0
- data/lib/abachrome/converters/lrgb_to_lms.rb +0 -0
- data/lib/abachrome/converters/lrgb_to_xyz.rb +29 -0
- data/lib/abachrome/converters/oklab_to_lms.rb +41 -0
- data/lib/abachrome/converters/oklab_to_lrgb.rb +39 -44
- data/lib/abachrome/converters/oklch_to_lrgb.rb +63 -26
- data/lib/abachrome/converters/oklch_to_oklab.rb +4 -4
- data/lib/abachrome/converters/oklch_to_xyz.rb +66 -0
- data/lib/abachrome/converters/xyz_to_lms.rb +30 -0
- data/lib/abachrome/converters/xyz_to_oklab.rb +38 -0
- data/lib/abachrome/parsers/css.rb +437 -0
- data/lib/abachrome/version.rb +1 -1
- data/security/assesments/2025-10-12-SECURITY_ASSESSMENT.md +53 -0
- data/security/vex.json +21 -0
- metadata +20 -1
@@ -0,0 +1,437 @@
|
|
1
|
+
#
|
2
|
+
# Abachrome::Parsers::CSS - CSS color format parser
|
3
|
+
#
|
4
|
+
# This parser handles various CSS color formats including:
|
5
|
+
# - Named colors (red, blue, etc.)
|
6
|
+
# - Hex colors (#rgb, #rrggbb, #rgba, #rrggbbaa)
|
7
|
+
# - rgb() and rgba() functions
|
8
|
+
# - hsl() and hsla() functions
|
9
|
+
# - hwb() function
|
10
|
+
# - lab() and lch() functions
|
11
|
+
# - oklab() and oklch() functions
|
12
|
+
# - color() function
|
13
|
+
#
|
14
|
+
|
15
|
+
require_relative "hex"
|
16
|
+
require_relative "../named/css"
|
17
|
+
require_relative "../color"
|
18
|
+
|
19
|
+
module Abachrome
|
20
|
+
module Parsers
|
21
|
+
class CSS
|
22
|
+
def self.parse(input)
|
23
|
+
return nil unless input.is_a?(String)
|
24
|
+
|
25
|
+
input = input.strip.downcase
|
26
|
+
|
27
|
+
# Try named colors first
|
28
|
+
named_color = parse_named_color(input)
|
29
|
+
return named_color if named_color
|
30
|
+
|
31
|
+
# Try hex colors
|
32
|
+
hex_color = Hex.parse(input)
|
33
|
+
return hex_color if hex_color
|
34
|
+
|
35
|
+
# Try functional notation
|
36
|
+
parse_functional_color(input)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def self.parse_named_color(input)
|
42
|
+
# Check if input matches a named color
|
43
|
+
rgb_values = Named::CSS.method(input.to_sym)&.call
|
44
|
+
return nil unless rgb_values
|
45
|
+
|
46
|
+
# Convert 0-255 RGB values to 0-1 range
|
47
|
+
r, g, b = rgb_values.map { |v| v / 255.0 }
|
48
|
+
Color.from_rgb(r, g, b)
|
49
|
+
rescue NameError
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.parse_functional_color(input)
|
54
|
+
case input
|
55
|
+
when /^rgb\((.+)\)$/
|
56
|
+
parse_rgb($1)
|
57
|
+
when /^rgba\((.+)\)$/
|
58
|
+
parse_rgba($1)
|
59
|
+
when /^hsl\((.+)\)$/
|
60
|
+
parse_hsl($1)
|
61
|
+
when /^hsla\((.+)\)$/
|
62
|
+
parse_hsla($1)
|
63
|
+
when /^hwb\((.+)\)$/
|
64
|
+
parse_hwb($1)
|
65
|
+
when /^lab\((.+)\)$/
|
66
|
+
parse_lab($1)
|
67
|
+
when /^lch\((.+)\)$/
|
68
|
+
parse_lch($1)
|
69
|
+
when /^oklab\((.+)\)$/
|
70
|
+
parse_oklab($1)
|
71
|
+
when /^oklch\((.+)\)$/
|
72
|
+
parse_oklch($1)
|
73
|
+
when /^color\((.+)\)$/
|
74
|
+
parse_color_function($1)
|
75
|
+
else
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.parse_rgb(params)
|
81
|
+
values = parse_color_values(params, 3)
|
82
|
+
return nil unless values
|
83
|
+
|
84
|
+
r, g, b = values
|
85
|
+
Color.from_rgb(r, g, b)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.parse_rgba(params)
|
89
|
+
values = parse_color_values(params, 4)
|
90
|
+
return nil unless values
|
91
|
+
|
92
|
+
r, g, b, a = values
|
93
|
+
Color.from_rgb(r, g, b, a)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.parse_hsl(params)
|
97
|
+
values = parse_hsl_values(params, 3)
|
98
|
+
return nil unless values
|
99
|
+
|
100
|
+
h, s, l = values
|
101
|
+
rgb = hsl_to_rgb(h, s, l)
|
102
|
+
Color.from_rgb(*rgb)
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.parse_hsla(params)
|
106
|
+
values = parse_hsl_values(params, 4)
|
107
|
+
return nil unless values
|
108
|
+
|
109
|
+
h, s, l, a = values
|
110
|
+
rgb = hsl_to_rgb(h, s, l)
|
111
|
+
Color.from_rgb(*rgb, a)
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.parse_hwb(params)
|
115
|
+
values = parse_hwb_values(params)
|
116
|
+
return nil unless values
|
117
|
+
|
118
|
+
h, w, b, a = values
|
119
|
+
rgb = hwb_to_rgb(h, w, b)
|
120
|
+
Color.from_rgb(*rgb, a)
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.parse_lab(params)
|
124
|
+
values = parse_lab_values(params, 3)
|
125
|
+
return nil unless values
|
126
|
+
|
127
|
+
l, a, b = values
|
128
|
+
# Convert CIELAB to XYZ, then to sRGB
|
129
|
+
xyz = lab_to_xyz(l, a, b)
|
130
|
+
rgb = xyz_to_rgb(*xyz)
|
131
|
+
Color.from_rgb(*rgb)
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.parse_lch(params)
|
135
|
+
values = parse_lch_values(params, 3)
|
136
|
+
return nil unless values
|
137
|
+
|
138
|
+
l, c, h = values
|
139
|
+
# Convert CIELCH to CIELAB, then to XYZ, then to sRGB
|
140
|
+
lab = lch_to_lab(l, c, h)
|
141
|
+
xyz = lab_to_xyz(*lab)
|
142
|
+
rgb = xyz_to_rgb(*xyz)
|
143
|
+
Color.from_rgb(*rgb)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.parse_oklab(params)
|
147
|
+
values = parse_oklab_values(params, 3)
|
148
|
+
return nil unless values
|
149
|
+
|
150
|
+
l, a, b = values
|
151
|
+
Color.from_oklab(l, a, b)
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.parse_oklch(params)
|
155
|
+
values = parse_oklch_values(params, 3)
|
156
|
+
return nil unless values
|
157
|
+
|
158
|
+
l, c, h = values
|
159
|
+
Color.from_oklch(l, c, h)
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.parse_color_function(params)
|
163
|
+
# Parse color(space values...)
|
164
|
+
parts = params.split(/\s+/, 2)
|
165
|
+
return nil unless parts.length == 2
|
166
|
+
|
167
|
+
space = parts[0]
|
168
|
+
values_str = parts[1]
|
169
|
+
|
170
|
+
case space
|
171
|
+
when "srgb"
|
172
|
+
values = parse_color_values(values_str, 3)
|
173
|
+
return nil unless values
|
174
|
+
r, g, b = values
|
175
|
+
Color.from_rgb(r, g, b)
|
176
|
+
when "srgb-linear"
|
177
|
+
values = parse_color_values(values_str, 3)
|
178
|
+
return nil unless values
|
179
|
+
r, g, b = values
|
180
|
+
Color.from_lrgb(r, g, b)
|
181
|
+
when "display-p3"
|
182
|
+
# For now, approximate as sRGB
|
183
|
+
values = parse_color_values(values_str, 3)
|
184
|
+
return nil unless values
|
185
|
+
r, g, b = values
|
186
|
+
Color.from_rgb(r, g, b)
|
187
|
+
when "a98-rgb"
|
188
|
+
# For now, approximate as sRGB
|
189
|
+
values = parse_color_values(values_str, 3)
|
190
|
+
return nil unless values
|
191
|
+
r, g, b = values
|
192
|
+
Color.from_rgb(r, g, b)
|
193
|
+
when "prophoto-rgb"
|
194
|
+
# For now, approximate as sRGB
|
195
|
+
values = parse_color_values(values_str, 3)
|
196
|
+
return nil unless values
|
197
|
+
r, g, b = values
|
198
|
+
Color.from_rgb(r, g, b)
|
199
|
+
when "rec2020"
|
200
|
+
# For now, approximate as sRGB
|
201
|
+
values = parse_color_values(values_str, 3)
|
202
|
+
return nil unless values
|
203
|
+
r, g, b = values
|
204
|
+
Color.from_rgb(r, g, b)
|
205
|
+
else
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Helper methods for parsing values
|
211
|
+
|
212
|
+
def self.parse_color_values(str, expected_count)
|
213
|
+
values = str.split(/\s*,\s*/).map(&:strip)
|
214
|
+
return nil unless values.length == expected_count
|
215
|
+
|
216
|
+
values.map do |v|
|
217
|
+
parse_numeric_value(v)
|
218
|
+
end.compact
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.parse_hsl_values(str, expected_count)
|
222
|
+
values = str.split(/\s*,\s*/).map(&:strip)
|
223
|
+
return nil unless values.length == expected_count
|
224
|
+
|
225
|
+
parsed = []
|
226
|
+
values.each_with_index do |v, i|
|
227
|
+
if i == 0 # Hue
|
228
|
+
val = parse_angle_value(v)
|
229
|
+
return nil unless val
|
230
|
+
parsed << val
|
231
|
+
else # Saturation, Lightness, Alpha
|
232
|
+
val = parse_percentage_or_number(v)
|
233
|
+
return nil unless val
|
234
|
+
parsed << val
|
235
|
+
end
|
236
|
+
end
|
237
|
+
parsed
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.parse_hwb_values(str)
|
241
|
+
values = str.split(/\s*,\s*/).map(&:strip)
|
242
|
+
return nil unless values.length >= 3
|
243
|
+
|
244
|
+
h = parse_angle_value(values[0])
|
245
|
+
w = parse_percentage_or_number(values[1])
|
246
|
+
b = parse_percentage_or_number(values[2])
|
247
|
+
a = values[3] ? parse_numeric_value(values[3]) : 1.0
|
248
|
+
|
249
|
+
return nil unless h && w && b && a
|
250
|
+
|
251
|
+
[h, w, b, a]
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.parse_lab_values(str, expected_count)
|
255
|
+
values = str.split(/\s+/, expected_count).map(&:strip)
|
256
|
+
return nil unless values.length == expected_count
|
257
|
+
|
258
|
+
l = parse_percentage_or_number(values[0])
|
259
|
+
a = parse_numeric_value(values[1])
|
260
|
+
b = parse_numeric_value(values[2])
|
261
|
+
|
262
|
+
return nil unless l && a && b
|
263
|
+
|
264
|
+
[l, a, b]
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.parse_lch_values(str, expected_count)
|
268
|
+
values = str.split(/\s+/, expected_count).map(&:strip)
|
269
|
+
return nil unless values.length == expected_count
|
270
|
+
|
271
|
+
l = parse_percentage_or_number(values[0])
|
272
|
+
c = parse_numeric_value(values[1])
|
273
|
+
h = parse_angle_value(values[2])
|
274
|
+
|
275
|
+
return nil unless l && c && h
|
276
|
+
|
277
|
+
[l, c, h]
|
278
|
+
end
|
279
|
+
|
280
|
+
def self.parse_oklab_values(str, expected_count)
|
281
|
+
values = str.split(/\s+/, expected_count).map(&:strip)
|
282
|
+
return nil unless values.length == expected_count
|
283
|
+
|
284
|
+
l = parse_percentage_or_number(values[0])
|
285
|
+
a = parse_numeric_value(values[1])
|
286
|
+
b = parse_numeric_value(values[2])
|
287
|
+
|
288
|
+
return nil unless l && a && b
|
289
|
+
|
290
|
+
[l, a, b]
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.parse_oklch_values(str, expected_count)
|
294
|
+
values = str.split(/\s+/, expected_count).map(&:strip)
|
295
|
+
return nil unless values.length == expected_count
|
296
|
+
|
297
|
+
l = parse_percentage_or_number(values[0])
|
298
|
+
c = parse_numeric_value(values[1])
|
299
|
+
h = parse_angle_value(values[2])
|
300
|
+
|
301
|
+
return nil unless l && c && h
|
302
|
+
|
303
|
+
[l, c, h]
|
304
|
+
end
|
305
|
+
|
306
|
+
def self.parse_numeric_value(str)
|
307
|
+
return nil unless str
|
308
|
+
|
309
|
+
if str.end_with?('%')
|
310
|
+
(str.chomp('%').to_f / 100.0)
|
311
|
+
else
|
312
|
+
str.to_f
|
313
|
+
end
|
314
|
+
rescue
|
315
|
+
nil
|
316
|
+
end
|
317
|
+
|
318
|
+
def self.parse_percentage_or_number(str)
|
319
|
+
return nil unless str
|
320
|
+
|
321
|
+
if str.end_with?('%')
|
322
|
+
str.chomp('%').to_f / 100.0
|
323
|
+
else
|
324
|
+
str.to_f
|
325
|
+
end
|
326
|
+
rescue
|
327
|
+
nil
|
328
|
+
end
|
329
|
+
|
330
|
+
def self.parse_angle_value(str)
|
331
|
+
return nil unless str
|
332
|
+
|
333
|
+
if str.end_with?('deg')
|
334
|
+
str.chomp('deg').to_f
|
335
|
+
elsif str.end_with?('rad')
|
336
|
+
str.chomp('rad').to_f * 180.0 / Math::PI
|
337
|
+
elsif str.end_with?('grad')
|
338
|
+
str.chomp('grad').to_f * 0.9
|
339
|
+
elsif str.end_with?('turn')
|
340
|
+
str.chomp('turn').to_f * 360.0
|
341
|
+
else
|
342
|
+
str.to_f # Assume degrees
|
343
|
+
end
|
344
|
+
rescue
|
345
|
+
nil
|
346
|
+
end
|
347
|
+
|
348
|
+
# Color space conversion functions
|
349
|
+
|
350
|
+
def self.hsl_to_rgb(h, s, l)
|
351
|
+
h = h / 360.0 # Normalize hue to 0-1
|
352
|
+
|
353
|
+
c = (1 - (2 * l - 1).abs) * s
|
354
|
+
x = c * (1 - ((h * 6) % 2 - 1).abs)
|
355
|
+
m = l - c / 2
|
356
|
+
|
357
|
+
if h < 1.0/6
|
358
|
+
r, g, b = c, x, 0
|
359
|
+
elsif h < 2.0/6
|
360
|
+
r, g, b = x, c, 0
|
361
|
+
elsif h < 3.0/6
|
362
|
+
r, g, b = 0, c, x
|
363
|
+
elsif h < 4.0/6
|
364
|
+
r, g, b = 0, x, c
|
365
|
+
elsif h < 5.0/6
|
366
|
+
r, g, b = x, 0, c
|
367
|
+
else
|
368
|
+
r, g, b = c, 0, x
|
369
|
+
end
|
370
|
+
|
371
|
+
[r + m, g + m, b + m]
|
372
|
+
end
|
373
|
+
|
374
|
+
def self.hwb_to_rgb(h, w, b)
|
375
|
+
# Normalize values
|
376
|
+
h = h / 360.0
|
377
|
+
|
378
|
+
# Calculate RGB from HSL equivalent
|
379
|
+
if w + b >= 1
|
380
|
+
gray = w / (w + b)
|
381
|
+
[gray, gray, gray]
|
382
|
+
else
|
383
|
+
rgb = hsl_to_rgb(h * 360, 1, 0.5)
|
384
|
+
r, g, b_rgb = rgb
|
385
|
+
|
386
|
+
# Apply whiteness and blackness
|
387
|
+
r = r * (1 - w - b) + w
|
388
|
+
g = g * (1 - w - b) + w
|
389
|
+
b_rgb = b_rgb * (1 - w - b) + w
|
390
|
+
|
391
|
+
[r, g, b_rgb]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def self.lab_to_xyz(l, a, b)
|
396
|
+
# CIELAB to XYZ conversion (D65 white point)
|
397
|
+
y = (l + 16) / 116
|
398
|
+
x = a / 500 + y
|
399
|
+
z = y - b / 200
|
400
|
+
|
401
|
+
x = x**3 > 0.008856 ? x**3 : (x - 16/116) / 7.787
|
402
|
+
y = y**3 > 0.008856 ? y**3 : (y - 16/116) / 7.787
|
403
|
+
z = z**3 > 0.008856 ? z**3 : (z - 16/116) / 7.787
|
404
|
+
|
405
|
+
# D65 white point
|
406
|
+
x *= 0.95047
|
407
|
+
y *= 1.0
|
408
|
+
z *= 1.08883
|
409
|
+
|
410
|
+
[x, y, z]
|
411
|
+
end
|
412
|
+
|
413
|
+
def self.lch_to_lab(l, c, h)
|
414
|
+
h_rad = h * Math::PI / 180.0
|
415
|
+
a = c * Math.cos(h_rad)
|
416
|
+
b = c * Math.sin(h_rad)
|
417
|
+
[l, a, b]
|
418
|
+
end
|
419
|
+
|
420
|
+
def self.xyz_to_rgb(x, y, z)
|
421
|
+
# XYZ to linear RGB
|
422
|
+
r = x * 3.2406 + y * -1.5372 + z * -0.4986
|
423
|
+
g = x * -0.9689 + y * 1.8758 + z * 0.0415
|
424
|
+
b = x * 0.0557 + y * -0.2040 + z * 1.0570
|
425
|
+
|
426
|
+
# Linear RGB to sRGB
|
427
|
+
[r, g, b].map do |v|
|
428
|
+
if v > 0.0031308
|
429
|
+
1.055 * (v ** (1/2.4)) - 0.055
|
430
|
+
else
|
431
|
+
12.92 * v
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
data/lib/abachrome/version.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Initial Security Assessment for Abachrome
|
2
|
+
|
3
|
+
## Assessment Date
|
4
|
+
2025-10-12
|
5
|
+
|
6
|
+
## Scope
|
7
|
+
This assessment covers the Abachrome Ruby gem, focusing on color manipulation and conversion functionality.
|
8
|
+
|
9
|
+
## Findings
|
10
|
+
|
11
|
+
### Positive Security Aspects
|
12
|
+
- **No External Dependencies**: Pure Ruby implementation with minimal dependencies
|
13
|
+
- **Immutable Objects**: Color objects are immutable, preventing accidental modification
|
14
|
+
- **Input Validation**: All parsing operations include validation
|
15
|
+
- **No Network Operations**: All computations are local
|
16
|
+
- **No File System Access**: No reading/writing of files
|
17
|
+
- **No System Calls**: Pure mathematical computations
|
18
|
+
|
19
|
+
### Potential Risks
|
20
|
+
- **Parsing Complex Inputs**: CSS color parsing could be vulnerable to malformed input
|
21
|
+
- **BigDecimal Precision**: High precision could lead to DoS via very large numbers
|
22
|
+
- **Memory Usage**: Large color palettes could consume significant memory
|
23
|
+
|
24
|
+
### Recommendations
|
25
|
+
1. Implement input length limits for parsing operations
|
26
|
+
2. Add timeout protections for complex computations
|
27
|
+
3. Validate coordinate ranges strictly
|
28
|
+
4. Consider rate limiting for palette operations
|
29
|
+
5. Regular dependency updates and security scans
|
30
|
+
|
31
|
+
## Threat Model
|
32
|
+
|
33
|
+
### Actors
|
34
|
+
- **Users**: Developers using the gem
|
35
|
+
- **Attackers**: Malicious users attempting to exploit parsing or computation
|
36
|
+
|
37
|
+
### Assets
|
38
|
+
- System resources (CPU, memory)
|
39
|
+
- User data integrity
|
40
|
+
|
41
|
+
### Threats
|
42
|
+
- DoS via computationally expensive inputs
|
43
|
+
- Memory exhaustion via large data structures
|
44
|
+
- Parsing exploits in CSS color functions
|
45
|
+
|
46
|
+
### Mitigations
|
47
|
+
- Input sanitization and validation
|
48
|
+
- Reasonable limits on data sizes
|
49
|
+
- Immutable data structures
|
50
|
+
- Pure functional operations where possible
|
51
|
+
|
52
|
+
## Conclusion
|
53
|
+
The gem has a strong security posture due to its pure computational nature and lack of external interfaces. Focus should be on input validation and resource limits for production use.
|
data/security/vex.json
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
{
|
2
|
+
"document": {
|
3
|
+
"id": "abachrome-vex",
|
4
|
+
"author": "Durable Programming",
|
5
|
+
"timestamp": "2025-10-12T00:00:00Z",
|
6
|
+
"version": "1.0"
|
7
|
+
},
|
8
|
+
"product_tree": {
|
9
|
+
"branches": [
|
10
|
+
{
|
11
|
+
"type": "package-url",
|
12
|
+
"name": "pkg:gem/abachrome",
|
13
|
+
"product": {
|
14
|
+
"name": "Abachrome",
|
15
|
+
"version": "*"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
]
|
19
|
+
},
|
20
|
+
"vulnerabilities": []
|
21
|
+
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abachrome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Durable Programming
|
@@ -37,8 +37,13 @@ files:
|
|
37
37
|
- ".envrc"
|
38
38
|
- ".rubocop.yml"
|
39
39
|
- CHANGELOG.md
|
40
|
+
- CLA.md
|
41
|
+
- CODE-OF-CONDUCT.md
|
42
|
+
- LICENSE
|
40
43
|
- README.md
|
41
44
|
- Rakefile
|
45
|
+
- SECURITY.md
|
46
|
+
- abachrome.gemspec
|
42
47
|
- demos/ncurses/plasma.rb
|
43
48
|
- devenv.lock
|
44
49
|
- devenv.nix
|
@@ -54,23 +59,34 @@ files:
|
|
54
59
|
- lib/abachrome/color_mixins/to_oklch.rb
|
55
60
|
- lib/abachrome/color_mixins/to_srgb.rb
|
56
61
|
- lib/abachrome/color_models/hsv.rb
|
62
|
+
- lib/abachrome/color_models/lms.rb
|
57
63
|
- lib/abachrome/color_models/oklab.rb
|
58
64
|
- lib/abachrome/color_models/oklch.rb
|
59
65
|
- lib/abachrome/color_models/rgb.rb
|
66
|
+
- lib/abachrome/color_models/xyz.rb
|
60
67
|
- lib/abachrome/color_space.rb
|
61
68
|
- lib/abachrome/converter.rb
|
62
69
|
- lib/abachrome/converters/base.rb
|
70
|
+
- lib/abachrome/converters/lms_to_lrgb.rb
|
71
|
+
- lib/abachrome/converters/lms_to_srgb.rb
|
72
|
+
- lib/abachrome/converters/lms_to_xyz.rb
|
73
|
+
- lib/abachrome/converters/lrgb_to_lms.rb
|
63
74
|
- lib/abachrome/converters/lrgb_to_oklab.rb
|
64
75
|
- lib/abachrome/converters/lrgb_to_srgb.rb
|
76
|
+
- lib/abachrome/converters/lrgb_to_xyz.rb
|
77
|
+
- lib/abachrome/converters/oklab_to_lms.rb
|
65
78
|
- lib/abachrome/converters/oklab_to_lrgb.rb
|
66
79
|
- lib/abachrome/converters/oklab_to_oklch.rb
|
67
80
|
- lib/abachrome/converters/oklab_to_srgb.rb
|
68
81
|
- lib/abachrome/converters/oklch_to_lrgb.rb
|
69
82
|
- lib/abachrome/converters/oklch_to_oklab.rb
|
70
83
|
- lib/abachrome/converters/oklch_to_srgb.rb
|
84
|
+
- lib/abachrome/converters/oklch_to_xyz.rb
|
71
85
|
- lib/abachrome/converters/srgb_to_lrgb.rb
|
72
86
|
- lib/abachrome/converters/srgb_to_oklab.rb
|
73
87
|
- lib/abachrome/converters/srgb_to_oklch.rb
|
88
|
+
- lib/abachrome/converters/xyz_to_lms.rb
|
89
|
+
- lib/abachrome/converters/xyz_to_oklab.rb
|
74
90
|
- lib/abachrome/gamut/base.rb
|
75
91
|
- lib/abachrome/gamut/p3.rb
|
76
92
|
- lib/abachrome/gamut/rec2020.rb
|
@@ -86,11 +102,14 @@ files:
|
|
86
102
|
- lib/abachrome/palette_mixins/interpolate.rb
|
87
103
|
- lib/abachrome/palette_mixins/resample.rb
|
88
104
|
- lib/abachrome/palette_mixins/stretch_luminance.rb
|
105
|
+
- lib/abachrome/parsers/css.rb
|
89
106
|
- lib/abachrome/parsers/hex.rb
|
90
107
|
- lib/abachrome/to_abcd.rb
|
91
108
|
- lib/abachrome/version.rb
|
92
109
|
- logo.png
|
93
110
|
- logo.webp
|
111
|
+
- security/assesments/2025-10-12-SECURITY_ASSESSMENT.md
|
112
|
+
- security/vex.json
|
94
113
|
homepage: https://github.com/durableprogramming/abachrome
|
95
114
|
licenses:
|
96
115
|
- MIT
|