abachrome 0.1.2 → 0.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a33e1980f4b275bc95646259d6c71b01715d2a77f019828b2c05ea98b24826d8
4
- data.tar.gz: 896c1479708b87a3862bc28bdbb1959aefc0bcb3619604980096f34ed7dd07e9
3
+ metadata.gz: 51acc020e50052f3aacc9d0e847809f62d84111fad86feb8889e3dc324d17385
4
+ data.tar.gz: 8649cab609da6a1c70dd94f9e77adf01b3fee441b8586d73dbcb2822c15eb7f2
5
5
  SHA512:
6
- metadata.gz: e6923ce91856cf5261cc52613c1e6eacd70e0fb9c3f7a1a2fe4f248b04ea326530ca8e6b56e84f07d4cc7f663ed61fb469c35ab1247ca40f6efeeb22b28952c3
7
- data.tar.gz: db5e02222ce2f664086a1deb5b2a4cf878c3e285c9053b652935b45688cac1d64286987b5bca0a02a0a8a884110996a2f071d283ac5df3b94f8d8722a620aef7
6
+ metadata.gz: 23b5d9b1aa20997c618adf7a337d7f85d533cb937d6d5005f4030dcb96a0b4f88a106ad9c311ecf5a7139a3b09a0ee54502133e6586a78e2983056b41cad2648
7
+ data.tar.gz: 5a9850d26a1e83b6b803de4862d5267499dfe92c752deb54abfef992c80239f83bf8af9f3095c445dcc61bc7a2bd40daa6afaaf443fd997d521789db467aca50
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.4] - 2025-10-12
4
+
5
+ - Added `Color#to_hex` method for convenient hex string output
6
+ - Added `Abachrome.parse` method for parsing CSS color strings
7
+
8
+ ## [0.1.3] - 2025-10-12
9
+
10
+ - Enhanced color parsing with support for additional CSS formats
11
+ - Improved documentation with comprehensive user guides
12
+ - Added LICENSE file for compliance
13
+ - Security contact information added to README
14
+
3
15
  ## [0.1.0] - 2025-02-09
4
16
 
5
- - Initial release
17
+ - Initial release with core color manipulation functionality
18
+ - Support for RGB, OKLAB, OKLCH color spaces
19
+ - Basic color conversion and gamut mapping
20
+ - CSS color output formatting
21
+ - High-precision calculations using BigDecimal
data/CLA.md ADDED
@@ -0,0 +1,45 @@
1
+ # Contributor License Agreement
2
+
3
+ This Contributor License Agreement ("Agreement") is entered into between the undersigned contributor ("Contributor") and Durable Programming, LLC ("Company").
4
+
5
+ ## Recitals
6
+
7
+ WHEREAS, Contributor desires to contribute to the abachrome project ("Project");
8
+
9
+ WHEREAS, Company desires to receive contributions to the Project;
10
+
11
+ NOW, THEREFORE, in consideration of the mutual promises contained herein, the parties agree as follows:
12
+
13
+ ## Grant of Copyright
14
+
15
+ Contributor hereby assigns to Company all right, title, and interest in and to the copyright in the contributions made by Contributor to the Project, including all modifications, enhancements, and derivative works thereof.
16
+
17
+ ## Scope
18
+
19
+ This Agreement applies to all contributions made by Contributor to the Project, whether made before or after the date of this Agreement.
20
+
21
+ ## Representations and Warranties
22
+
23
+ Contributor represents and warrants that:
24
+
25
+ 1. Contributor has the right to enter into this Agreement and to grant the rights granted herein.
26
+
27
+ 2. The contributions are original works of Contributor and do not infringe any third-party rights.
28
+
29
+ 3. Contributor has not assigned or licensed the contributions to any other party.
30
+
31
+ ## Governing Law
32
+
33
+ This Agreement shall be governed by the laws of the State of Delaware, without regard to conflict of laws principles.
34
+
35
+ ## Entire Agreement
36
+
37
+ This Agreement constitutes the entire agreement between the parties and supersedes all prior agreements.
38
+
39
+ IN WITNESS WHEREOF, the parties have executed this Agreement as of the date first above written.
40
+
41
+ Contributor: ___________________________ Date: __________
42
+
43
+ Company: Durable Programming, LLC
44
+
45
+ By: ___________________________ Date: __________
@@ -0,0 +1,9 @@
1
+ # Code of Conduct
2
+
3
+ This document provides community guidelines for a safe, respectful, productive, and collaborative place for any person who is willing to contribute to the Abachrome community. It applies to all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.).
4
+
5
+ - Participants will be tolerant of opposing views.
6
+ - Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
7
+ - When interpreting the words and actions of others, participants should always assume good intentions.
8
+ - Behaviour which can be reasonably considered harassment will not be tolerated.
9
+
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2023 Durable Programming
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Abachrome
2
2
 
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![Ruby](https://img.shields.io/badge/ruby-3.0+-red.svg)](https://www.ruby-lang.org/en/)
5
+ [![Gem Version](https://badge.fury.io/rb/abachrome.svg)](https://badge.fury.io/rb/abachrome)
6
+ [![Downloads](https://img.shields.io/gem/dt/abachrome)](https://rubygems.org/gems/abachrome)
7
+ [![GitHub repo size](https://img.shields.io/github/repo-size/durableprogramming/abachrome)](https://github.com/durableprogramming/abachrome)
8
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
9
+ [![RuboCop](https://img.shields.io/badge/code_style-rubocop-orange.svg)](https://github.com/rubocop/rubocop)
10
+ [![Maturity Level](https://img.shields.io/badge/Maturity-Level%202%20--%20First%20Release-yellowgreen.svg)](https://github.com/tophat/getting-started/blob/master/scorecard.md)
11
+ [![Website](https://img.shields.io/website-up-down-green-red/http/durableprogramming.com.svg)](https://durableprogramming.com)
12
+
3
13
  Abachrome is a Ruby gem for parsing, manipulating, and managing colors. It provides a robust set of tools for working with various color formats including hex, RGB, HSL, and named colors.
4
14
 
5
15
  ![Abachrome Logo](logo.png)
@@ -67,20 +77,112 @@ color = Abachrome.from_rgb(1.2, -0.1, 0.5)
67
77
  mapped_color = srgb_gamut.map(color.coordinates) # Map color to gamut
68
78
  ```
69
79
 
80
+ ### Parsing Colors
81
+
82
+ Abachrome supports parsing colors from various string formats using the CSS parser:
83
+
84
+ ```ruby
85
+ # Hex colors
86
+ color = Abachrome::Parsers::CSS.parse('#ff0000')
87
+ color = Abachrome::Parsers::CSS.parse('#f00') # Short hex
88
+
89
+ # RGB and HSL strings
90
+ color = Abachrome::Parsers::CSS.parse('rgb(255, 0, 0)')
91
+ color = Abachrome::Parsers::CSS.parse('rgba(255, 0, 0, 0.5)')
92
+ color = Abachrome::Parsers::CSS.parse('hsl(0, 100%, 50%)')
93
+
94
+ # Named colors
95
+ color = Abachrome::Parsers::CSS.parse('red')
96
+
97
+ # Advanced formats
98
+ color = Abachrome::Parsers::CSS.parse('oklab(0.5 0.2 -0.1)')
99
+ color = Abachrome::Parsers::CSS.parse('oklch(0.5 0.2 120)')
100
+ ```
101
+
102
+ ### Color Manipulation
103
+
104
+ Abachrome provides methods for blending and adjusting colors:
105
+
106
+ ```ruby
107
+ color1 = Abachrome.from_rgb(1.0, 0.0, 0.0)
108
+ color2 = Abachrome.from_rgb(0.0, 1.0, 0.0)
109
+
110
+ # Blend two colors
111
+ blended = color1.blend(color2, 0.5) # 50% blend
112
+
113
+ # Adjust lightness
114
+ lighter = color1.lighten(0.2) # Lighten by 20%
115
+ ```
116
+
117
+ ### Working with Palettes
118
+
119
+ Create and manipulate color palettes:
120
+
121
+ ```ruby
122
+ palette = Abachrome::Palette.new([color1, color2])
123
+
124
+ # Interpolate between colors
125
+ interpolated = palette.interpolate(steps: 5) # Add 5 interpolated colors between each pair
126
+ ```
127
+
128
+ ### Advanced Gamut Mapping
129
+
130
+ Map colors to different color gamuts for accurate display:
131
+
132
+ ```ruby
133
+ srgb_gamut = Abachrome::Gamut::SRGB.new
134
+ p3_gamut = Abachrome::Gamut::P3.new
135
+
136
+ out_of_gamut = Abachrome.from_rgb(1.2, -0.1, 0.5)
137
+ mapped_srgb = srgb_gamut.map(out_of_gamut.coordinates)
138
+ mapped_p3 = p3_gamut.map(out_of_gamut.coordinates)
139
+ ```
140
+
70
141
  ## Features
71
142
 
72
- - Support for multiple color spaces (RGB, HSL, Lab, Oklab)
73
- - Color space conversion
74
- - Gamut mapping
75
- - CSS color parsing and formatting
143
+ - Support for multiple color spaces (RGB, HSL, Lab, Oklab, OKLCH, XYZ, LMS)
144
+ - Color space conversion between all supported spaces
145
+ - Advanced gamut mapping for sRGB, P3, and Rec.2020
146
+ - Comprehensive CSS color parsing and formatting
76
147
  - Support for CSS named colors
148
+ - Color blending and lightness adjustments
149
+ - Palette creation and interpolation
77
150
  - High-precision color calculations using BigDecimal
78
- - Alpha channel support
151
+ - Alpha channel support throughout all operations
79
152
 
80
153
  ## Requirements
81
154
 
82
155
  - Ruby >= 3.0.0
83
156
 
157
+ ## Dependencies
158
+
159
+ Abachrome has minimal dependencies to maintain performance and compatibility:
160
+
161
+ ### Runtime Dependencies
162
+ - **dry-inflector** (~> 1.0): Provides utilities for dynamic class loading and color space management
163
+
164
+ ### Development Dependencies
165
+ - Testing framework (minitest)
166
+ - Code quality tools (rubocop)
167
+
168
+ ### Dependency Management
169
+ Dependencies are specified in `abachrome.gemspec` and managed via Bundler.
170
+
171
+ To check for dependency updates:
172
+ ```bash
173
+ bundle outdated
174
+ ```
175
+
176
+ To update dependencies:
177
+ ```bash
178
+ bundle update
179
+ ```
180
+
181
+ All dependencies are selected based on:
182
+ - Active maintenance and community support
183
+ - Minimal footprint and performance impact
184
+ - Compatibility with Ruby 3.0+ requirements
185
+
84
186
  ## License
85
187
 
86
188
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -91,9 +193,113 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
91
193
 
92
194
  To install this gem onto your local machine, run `bundle exec rake install`.
93
195
 
196
+ ### Testing
197
+
198
+ Tests are run automatically in CI/CD pipelines for all pull requests and pushes to the main branch. The test suite includes:
199
+
200
+ - Unit tests for all color models and converters
201
+ - Integration tests for parsing and output functionality
202
+ - Performance benchmarks for critical operations
203
+
204
+ To run tests locally:
205
+ ```bash
206
+ rake test
207
+ ```
208
+
209
+ Tests must pass before any changes can be merged to the main branch.
210
+
211
+ ### Testing Policy
212
+
213
+ - **All changes**: Must not break existing tests
214
+ - **New features**: Must include comprehensive unit tests
215
+ - **Bug fixes**: Must include regression tests
216
+ - **Major changes**: Require code review and may need additional integration tests
217
+ - **Performance changes**: Must include benchmark comparisons
218
+
219
+ All pull requests require tests to pass CI checks before merging.
220
+
94
221
  ## Contributing
95
222
 
96
- Bug reports and pull requests are welcome on GitHub at https://github.com/durableprogramming/abachrome.
223
+ We welcome contributions from the community! Here's how you can help:
224
+
225
+ ### Reporting Issues
226
+
227
+ Please use GitHub issues to report bugs or request features.
228
+
229
+ ### Contributing Code
230
+
231
+ 1. Fork the repository
232
+ 2. Create a feature branch (`git checkout -b feature/my-feature`)
233
+ 3. Make your changes
234
+ 4. Ensure tests pass (`rake test`)
235
+ 5. Follow the code style (run `rubocop` if available)
236
+ 6. Commit your changes
237
+ 7. Push to your fork
238
+ 8. Open a pull request
239
+
240
+ ### Code Style
241
+
242
+ - Follow Ruby style guidelines
243
+ - Use meaningful variable and method names
244
+ - Add tests for new functionality
245
+ - Update documentation as needed
246
+
247
+ ### Testing
248
+
249
+ All contributions must include appropriate tests. Run the test suite with `rake test`.
250
+
251
+ ### Developer Certificate of Origin
252
+
253
+ By contributing to this project, you agree to the Developer Certificate of Origin (DCO). To certify your contribution, add a sign-off to your commits:
254
+
255
+ ```
256
+ git commit -s -m "Your commit message"
257
+ ```
258
+
259
+ This adds `Signed-off-by: Your Name <your.email@example.com>` to certify that you have the right to submit the work.
260
+
261
+ ## Reporting Defects
262
+
263
+ To report bugs or defects, please use GitHub issues at https://github.com/durableprogramming/abachrome/issues. Provide as much detail as possible, including:
264
+
265
+ - Steps to reproduce the issue
266
+ - Expected behavior
267
+ - Actual behavior
268
+ - Ruby version and gem version
269
+ - Any relevant code snippets
270
+
271
+ ## Security
272
+
273
+ For security-related issues, please contact us directly at commercial@durableprogramming.com instead of using public issues.
274
+
275
+ We take security seriously and will respond to vulnerability reports within 48 hours.
276
+
277
+ ## Governance
278
+
279
+ ### Project Members
280
+ - **Durable Programming**: Lead developer and maintainer (commercial@durableprogramming.com)
281
+
282
+ ### Roles and Responsibilities
283
+ - **Maintainer**: Responsible for code reviews, releases, and overall project direction
284
+ - **Security Contact**: Handles security vulnerability reports and coordinates responses
285
+
286
+ ### Sensitive Resources
287
+ Access to the following resources is limited to project maintainers:
288
+ - GitHub repository (push access)
289
+ - RubyGems publishing credentials
290
+ - CI/CD pipeline configurations
291
+
292
+ ### Contributor Permissions Policy
293
+
294
+ Before granting elevated permissions (write access, maintainer status):
295
+
296
+ 1. **Review Process**: Contributors must demonstrate consistent quality contributions over at least 3 months
297
+ 2. **Code Quality**: All contributions must follow project standards and pass CI checks
298
+ 3. **Security Review**: Security-related contributions receive additional scrutiny
299
+ 4. **Community Engagement**: Active participation in issue discussions and reviews
300
+ 5. **Maintainer Approval**: Final decision by existing maintainers via consensus
301
+
302
+ New contributors start with read-only access and issue reporting. Permissions are escalated gradually based on demonstrated responsibility.
97
303
 
98
304
  # Acknowledgement
99
305
 
data/SECURITY.md ADDED
@@ -0,0 +1,94 @@
1
+ # Security Policy
2
+
3
+ ## Reporting Vulnerabilities
4
+
5
+ If you discover a security vulnerability in Abachrome, please report it to us as follows:
6
+
7
+ ### Contact
8
+ Email: security@durableprogramming.com
9
+
10
+ ### Response Time
11
+ We will acknowledge receipt of your report within 72 hours and provide a more detailed response within 7 days indicating our next steps.
12
+
13
+ ### Disclosure
14
+ We ask that you do not publicly disclose the vulnerability until we have had a chance to address it. We will work with you to determine an appropriate disclosure timeline.
15
+
16
+ ## Vulnerability Management
17
+
18
+ ### Classification
19
+ Vulnerabilities are classified as:
20
+ - **Critical**: Immediate threat to user data or system security
21
+ - **High**: Significant security risk
22
+ - **Medium**: Moderate security concern
23
+ - **Low**: Minor security issue
24
+
25
+ ### Remediation Timeline
26
+ - **Critical**: Within 48 hours
27
+ - **High**: Within 7 days
28
+ - **Medium**: Within 30 days
29
+ - **Low**: Within 90 days
30
+
31
+ ### Process
32
+ 1. Report received and acknowledged
33
+ 2. Assessment and classification
34
+ 3. Development of fix
35
+ 4. Testing and validation
36
+ 5. Release of patched version
37
+ 6. Public disclosure (if applicable)
38
+
39
+ ## Automated Security Scanning
40
+
41
+ ### SCA (Software Composition Analysis)
42
+ - **Tool**: bundle-audit
43
+ - **Frequency**: Run on all CI builds and weekly scheduled scans
44
+ - **Threshold**: All vulnerabilities must be addressed before release
45
+ - **Policy**: Critical and High severity vulnerabilities must be fixed immediately. Medium severity within 30 days. Low severity tracked but may be accepted with risk assessment.
46
+
47
+ ### SAST (Static Application Security Testing)
48
+ - **Tool**: Brakeman
49
+ - **Frequency**: Run on all CI builds
50
+ - **Threshold**: All warnings must be reviewed. High-confidence findings must be fixed. False positives documented.
51
+ - **Policy**: Security findings block releases. Code review required for waivers.
52
+
53
+ ### Dependency Updates
54
+ - **Process**: Automated PRs for patch updates. Manual review for minor/major updates.
55
+ - **Testing**: Full test suite run on dependency updates.
56
+ - **Rollback**: Ability to rollback if issues discovered post-update.
57
+
58
+ ## Security Updates
59
+ Security updates will be released as patch versions with descriptive changelogs. Subscribe to releases on GitHub to stay informed.
60
+
61
+ ## Verifying Releases
62
+
63
+ To ensure the integrity and authenticity of Abachrome releases:
64
+
65
+ 1. **Checksum Verification**: Each release includes SHA256 checksums for all assets. Download the checksum file and verify using:
66
+ ```
67
+ sha256sum -c abachrome-<version>.gem.sha256
68
+ ```
69
+
70
+ 2. **Signature Verification** (future): Releases will be signed with GPG. Verify using:
71
+ ```
72
+ gpg --verify abachrome-<version>.gem.asc abachrome-<version>.gem
73
+ ```
74
+
75
+ 3. **Source Verification**: Always build from the official GitHub repository source.
76
+
77
+ ## Support and Maintenance
78
+
79
+ ### Support Scope
80
+ Abachrome provides support for:
81
+ - Current stable Ruby versions (3.0+)
82
+ - Reported security vulnerabilities
83
+ - Critical bugs affecting core functionality
84
+
85
+ ### Support Duration
86
+ - **Active Support**: Latest major version receives full support
87
+ - **Security Support**: Versions receive security updates for 1 year after release
88
+ - **End of Life**: Versions without security support are documented in release notes
89
+
90
+ ### Security Update Policy
91
+ Security updates are provided for the current major version and the previous major version for up to 1 year after the new major version's release. Older versions may receive critical security fixes at our discretion.
92
+
93
+ ## Current Known Vulnerabilities
94
+ None at this time.
data/abachrome.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/abachrome/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "abachrome"
7
+ spec.version = Abachrome::VERSION
8
+ spec.authors = ["Durable Programming"]
9
+ spec.email = ["commercial@durableprogramming.com"]
10
+
11
+ spec.summary = "A Ruby gem for parsing, manipulating, and managing colors"
12
+ spec.description = "Abachrome provides a robust set of tools for working with various color formats including hex, RGB, HSL, and named colors. Features support for multiple color spaces (RGB, HSL, Lab, Oklab), color space conversion, gamut mapping, CSS color parsing and formatting, and high-precision color calculations using BigDecimal."
13
+ spec.homepage = "https://github.com/durableprogramming/abachrome"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.0.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/durableprogramming/abachrome"
19
+ spec.metadata["changelog_uri"] = "https://github.com/durableprogramming/abachrome/blob/main/CHANGELOG.md"
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject do |f|
25
+ (File.expand_path(f) == __FILE__) ||
26
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+
32
+ # Runtime dependencies
33
+ spec.add_dependency "dry-inflector", "~> 1.0"
34
+
35
+ end
36
+
data/devenv.lock CHANGED
@@ -3,10 +3,10 @@
3
3
  "devenv": {
4
4
  "locked": {
5
5
  "dir": "src/modules",
6
- "lastModified": 1749202057,
6
+ "lastModified": 1760162706,
7
7
  "owner": "cachix",
8
8
  "repo": "devenv",
9
- "rev": "1c28c31f09f8a5ab134e1943b29b9d2f302cfbcd",
9
+ "rev": "0d5ad578728fe4bce66eb4398b8b1e66deceb4e4",
10
10
  "type": "github"
11
11
  },
12
12
  "original": {
@@ -72,10 +72,10 @@
72
72
  ]
73
73
  },
74
74
  "locked": {
75
- "lastModified": 1747372754,
75
+ "lastModified": 1759523803,
76
76
  "owner": "cachix",
77
77
  "repo": "git-hooks.nix",
78
- "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
78
+ "rev": "cfc9f7bb163ad8542029d303e599c0f7eee09835",
79
79
  "type": "github"
80
80
  },
81
81
  "original": {
@@ -106,10 +106,10 @@
106
106
  },
107
107
  "nixpkgs": {
108
108
  "locked": {
109
- "lastModified": 1746807397,
109
+ "lastModified": 1758532697,
110
110
  "owner": "cachix",
111
111
  "repo": "devenv-nixpkgs",
112
- "rev": "c5208b594838ea8e6cca5997fbf784b7cca1ca90",
112
+ "rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
113
113
  "type": "github"
114
114
  },
115
115
  "original": {
@@ -128,10 +128,10 @@
128
128
  ]
129
129
  },
130
130
  "locked": {
131
- "lastModified": 1747288354,
131
+ "lastModified": 1759902829,
132
132
  "owner": "bobvanderlinden",
133
133
  "repo": "nixpkgs-ruby",
134
- "rev": "5d7598f3059fff0cbd0dc4756f9d87f8cb7f3f7c",
134
+ "rev": "5fba6c022a63f1e76dee4da71edddad8959f088a",
135
135
  "type": "github"
136
136
  },
137
137
  "original": {
@@ -134,7 +134,7 @@ module Abachrome
134
134
  end
135
135
 
136
136
  # Returns a string representation of the color in the format "ColorSpaceName(coord1, coord2, coord3, alpha)"
137
- #
137
+ #
138
138
  # @return [String] A human-readable string representation of the color showing its
139
139
  # color space name, coordinate values rounded to 3 decimal places, and alpha value
140
140
  # (if not 1.0)
@@ -144,6 +144,14 @@ module Abachrome
144
144
  "#{color_space.name}(#{coord_str}#{alpha_str})"
145
145
  end
146
146
 
147
+ # Returns the color as a hexadecimal string representation.
148
+ #
149
+ # @return [String] A hex color string in the format "#RRGGBB" or "#RRGGBBAA" if alpha < 1.0
150
+ def to_hex
151
+ require_relative "outputs/css"
152
+ Outputs::CSS.format_hex(self)
153
+ end
154
+
147
155
  private
148
156
 
149
157
  # Validates that the number of coordinates matches the expected number for the color space.
@@ -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
@@ -1,5 +1,5 @@
1
1
  #
2
2
 
3
3
  module Abachrome
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/abachrome.rb CHANGED
@@ -129,7 +129,7 @@ module Abachrome
129
129
  end
130
130
 
131
131
  # Creates a color object from a CSS color name.
132
- #
132
+ #
133
133
  # @param color_name [String] The CSS color name (e.g., 'red', 'blue', 'cornflowerblue').
134
134
  # Case-insensitive.
135
135
  # @return [Abachrome::Color, nil] A color object in the RGB color space if the name is valid,
@@ -141,6 +141,15 @@ module Abachrome
141
141
  from_rgb(*rgb_values.map { |v| v / 255.0 })
142
142
  end
143
143
 
144
+ # Parses a CSS color string and returns a Color object.
145
+ #
146
+ # @param css_string [String] The CSS color string to parse (e.g., "#ff0000", "rgb(255, 0, 0)", "red")
147
+ # @return [Abachrome::Color, nil] A Color object if parsing succeeds, nil otherwise
148
+ def parse(css_string)
149
+ require_relative "abachrome/parsers/css"
150
+ Parsers::CSS.parse(css_string)
151
+ end
152
+
144
153
  # Convert a color from its current color space to another color space.
145
154
  #
146
155
  # @param color [Abachrome::Color] The color object to convert
@@ -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.2
4
+ version: 0.1.4
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
@@ -97,11 +102,14 @@ files:
97
102
  - lib/abachrome/palette_mixins/interpolate.rb
98
103
  - lib/abachrome/palette_mixins/resample.rb
99
104
  - lib/abachrome/palette_mixins/stretch_luminance.rb
105
+ - lib/abachrome/parsers/css.rb
100
106
  - lib/abachrome/parsers/hex.rb
101
107
  - lib/abachrome/to_abcd.rb
102
108
  - lib/abachrome/version.rb
103
109
  - logo.png
104
110
  - logo.webp
111
+ - security/assesments/2025-10-12-SECURITY_ASSESSMENT.md
112
+ - security/vex.json
105
113
  homepage: https://github.com/durableprogramming/abachrome
106
114
  licenses:
107
115
  - MIT