unmagic-icon 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17b7d09638783ea1144e26806aa2e7915d04e08645ed39c2df6a2f3026a69bdf
4
- data.tar.gz: 39b8e9e8fc1a88c494c1336224b88632c0f5153e002fa2a2663b1f476c42f27b
3
+ metadata.gz: 66fe0a90a06ca3989154088d6e1d8cda87f6f1055d9b0ddf368ee2e9a054fd2f
4
+ data.tar.gz: fb7ad24d2a2c4d56a9dc4246751ff12542424ebc392bddba9be78f8f3420bc47
5
5
  SHA512:
6
- metadata.gz: 58e783d9c8829e92c70149d48f95a012a092123fa85ec2e92f68eb30203d4028722f7db8aad66c487880749b5e9647062c11cc4118c06faf71b5784fd617a9ea
7
- data.tar.gz: d5ecd53c6ee72f8aaa9050221d4a619fc58a778f9998896a50530a1cee737564f3667e5bb4435040c69c645861dcb8a0ca829bcb3b923db025d51b32800a1882
6
+ metadata.gz: c5e2c6186cb26a61779090c520930a9941725db13d2807df6a61ecad308debecaf86c928ca9e4b5bc6ca3dd3768b16fa1456ae42802a34bd6a90ab8963db3b71
7
+ data.tar.gz: 65e389aa2b586335518e70919aa560601a8157cc8ef21bab78ed8ab52fdce9dc0ba35add14410a737d4fbc9690fafe673cc7d5a1ec9ec14aa31dd64e2649a694
data/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-07-31
11
+
12
+ ### Added
13
+ - New downloadable icon library: `lobe-icons` (AI/LLM brand logos from
14
+ LobeHub, with `-color` and `-text` variants alongside each bare mark)
15
+
16
+ ## [0.2.1] - 2026-07-14
17
+
18
+ ### Changed
19
+ - Archives are now extracted in pure Ruby (rubyzip for zips, stdlib
20
+ `Gem::Package::TarReader`/`Zlib` for tarballs) instead of shelling out to
21
+ `unzip`/`tar`, so `unmagic:icons:install` works in minimal containers —
22
+ such as Rails' default slim Docker image — where those binaries aren't
23
+ installed. Extraction also guards against zip-slip path traversal.
24
+
25
+ ### Added
26
+ - Runtime dependency on `rubyzip` (>= 2.3)
27
+
10
28
  ## [0.2.0] - 2026-06-22
11
29
 
12
30
  ### Added
@@ -41,6 +59,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41
59
  hooks into `assets:precompile`) and `unmagic:icons:download[library]`
42
60
  - `Unmagic::Icon::Web`, a Rack app for browsing the configured icon libraries
43
61
 
44
- [Unreleased]: https://github.com/unreasonable-magic/unmagic-icon/compare/v0.2.0...HEAD
62
+ [Unreleased]: https://github.com/unreasonable-magic/unmagic-icon/compare/v0.3.0...HEAD
63
+ [0.3.0]: https://github.com/unreasonable-magic/unmagic-icon/compare/v0.2.1...v0.3.0
64
+ [0.2.1]: https://github.com/unreasonable-magic/unmagic-icon/compare/v0.2.0...v0.2.1
45
65
  [0.2.0]: https://github.com/unreasonable-magic/unmagic-icon/compare/v0.1.0...v0.2.0
46
66
  [0.1.0]: https://github.com/unreasonable-magic/unmagic-icon/releases/tag/v0.1.0
data/README.md CHANGED
@@ -133,6 +133,7 @@ Available libraries:
133
133
  | `iconoir` | Iconoir | Free open source icons designed on a 24x24 grid |
134
134
  | `material-design-icons` | Material Design Icons | 7400+ Material Design icons (Pictogrammers @mdi) |
135
135
  | `phosphor` | Phosphor Icons | Flexible icon family with six weights (thin to fill, plus duotone) |
136
+ | `lobe-icons` | Lobe Icons | Popular AI / LLM model brand logos and icons |
136
137
 
137
138
  ### Browsing icons
138
139
 
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unmagic
4
+ class Icon
5
+ class Library
6
+ class Source
7
+ # LobeHub's AI/LLM brand logos, sourced from the static-svg npm package
8
+ # rather than the repo (the repo only ships React components). Each brand
9
+ # comes in up to three variants, distinguished by filename suffix: the
10
+ # bare mark (openai), the full-colour mark (openai-color), and the
11
+ # wordmark (openai-text).
12
+ class LobeIcons < Source
13
+ key :"lobe-icons"
14
+ title "Lobe Icons"
15
+ description "Popular AI / LLM model brand logos and icons"
16
+ url "https://registry.npmjs.org/@lobehub/icons-static-svg/-/icons-static-svg-1.94.0.tgz"
17
+ archive :tgz
18
+ dir "lobe-icons"
19
+ extract "package/icons/*.svg"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -6,7 +6,8 @@ require "json"
6
6
  require "pathname"
7
7
  require "fileutils"
8
8
  require "tmpdir"
9
- require "open3"
9
+ require "zlib"
10
+ require "rubygems/package"
10
11
 
11
12
  # Terminal progress is a nicety, not a requirement. The downloader runs fine
12
13
  # (with plain output) when these sibling gems aren't installed.
@@ -211,19 +212,76 @@ module Unmagic
211
212
  end
212
213
  end
213
214
 
215
+ # Pure-Ruby extraction — no `unzip`/`tar` binaries required, so this
216
+ # works in minimal containers (e.g. Rails' default slim Docker image).
214
217
  def extract_archive(archive_path, destination, type)
215
218
  case type
216
219
  when :zip
217
- _, stderr, status = Open3.capture3("unzip", "-q", "-o", archive_path, "-d", destination)
218
- raise ExtractionError, "Failed to extract zip: #{stderr}" unless status.success?
220
+ extract_zip(archive_path, destination)
219
221
  when :tgz, :tar
220
- _, stderr, status = Open3.capture3("tar", "-xzf", archive_path, "-C", destination)
221
- raise ExtractionError, "Failed to extract tar: #{stderr}" unless status.success?
222
+ extract_tar(archive_path, destination)
222
223
  else
223
224
  raise ExtractionError, "Unknown archive type: #{type}"
224
225
  end
225
226
  end
226
227
 
228
+ def extract_zip(archive_path, destination)
229
+ require "zip"
230
+
231
+ Zip::File.open(archive_path) do |zip|
232
+ zip.each do |entry|
233
+ path = safe_extraction_path(entry.name, destination)
234
+
235
+ if entry.directory?
236
+ FileUtils.mkdir_p(path)
237
+ elsif entry.file?
238
+ FileUtils.mkdir_p(File.dirname(path))
239
+ entry.get_input_stream do |input|
240
+ File.open(path, "wb") { |file| IO.copy_stream(input, file) }
241
+ end
242
+ end
243
+ end
244
+ end
245
+ rescue Zip::Error => e
246
+ raise ExtractionError, "Failed to extract zip: #{e.message}"
247
+ end
248
+
249
+ GZIP_MAGIC = "\x1f\x8b".b
250
+
251
+ def extract_tar(archive_path, destination)
252
+ File.open(archive_path, "rb") do |file|
253
+ stream = file.read(2) == GZIP_MAGIC ? Zlib::GzipReader.new(file.tap(&:rewind)) : file.tap(&:rewind)
254
+
255
+ Gem::Package::TarReader.new(stream) do |tar|
256
+ tar.each do |entry|
257
+ path = safe_extraction_path(entry.full_name, destination)
258
+
259
+ if entry.directory?
260
+ FileUtils.mkdir_p(path)
261
+ elsif entry.file?
262
+ FileUtils.mkdir_p(File.dirname(path))
263
+ File.open(path, "wb") { |output| IO.copy_stream(entry, output) }
264
+ end
265
+ end
266
+ end
267
+ end
268
+ rescue Zlib::Error, Gem::Package::Error => e
269
+ raise ExtractionError, "Failed to extract tar: #{e.message}"
270
+ end
271
+
272
+ # Guards against zip-slip: entries like "../../etc/passwd" must not
273
+ # escape the extraction directory.
274
+ def safe_extraction_path(entry_name, destination)
275
+ destination = File.expand_path(destination)
276
+ path = File.expand_path(entry_name, destination)
277
+
278
+ unless path == destination || path.start_with?(destination + File::SEPARATOR)
279
+ raise ExtractionError, "Archive entry escapes extraction directory: #{entry_name}"
280
+ end
281
+
282
+ path
283
+ end
284
+
227
285
  def count_svgs(directory)
228
286
  Dir.glob(File.join(directory, "**", "*.svg")).size
229
287
  end
@@ -246,3 +304,4 @@ require_relative "source/octicons"
246
304
  require_relative "source/iconoir"
247
305
  require_relative "source/material_design_icons"
248
306
  require_relative "source/phosphor"
307
+ require_relative "source/lobe_icons"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Unmagic
4
4
  class Icon
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unmagic-icon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Pitt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-22 00:00:00.000000000 Z
11
+ date: 2026-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.8.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubyzip
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '2.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '2.3'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +121,7 @@ files:
107
121
  - lib/unmagic/icon/library/source/feather.rb
108
122
  - lib/unmagic/icon/library/source/heroicons.rb
109
123
  - lib/unmagic/icon/library/source/iconoir.rb
124
+ - lib/unmagic/icon/library/source/lobe_icons.rb
110
125
  - lib/unmagic/icon/library/source/lucide.rb
111
126
  - lib/unmagic/icon/library/source/material_design_icons.rb
112
127
  - lib/unmagic/icon/library/source/material_file_icons.rb