glyphs 0.4.0 → 0.4.1
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 +17 -0
- data/README.md +37 -0
- data/lib/glyphs/configuration.rb +5 -1
- data/lib/glyphs/icon_pruner.rb +23 -20
- data/lib/glyphs/prunable_icon_error.rb +8 -0
- data/lib/glyphs/prune_guard.rb +45 -0
- data/lib/glyphs/prune_runner.rb +6 -0
- data/lib/glyphs/source_scanner.rb +36 -3
- data/lib/glyphs/version.rb +1 -1
- data/lib/glyphs.rb +19 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e0ed91cb2af5c3414aa3db654c772ec9c3ad506f4f7d9998223b76144897d33
|
|
4
|
+
data.tar.gz: 37a81b162e603e83060c7adfd395cef55f1d20c4896d9bb4d4b0ec96e3e83908
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82eb60a70f7fd553e38130cf57025834ca8daf9c02c54c315df9900c07346138eb1230d48783204509ab897791203434db38cf62ed1035337bf3ded8aee7b2ed
|
|
7
|
+
data.tar.gz: 188c29bbe5a8a8f6dbae785e39af4a6b49ee4c660a302bf8fd032525012c47ce7b6559e45c342f5b6544f06994162ab443b4e4fc8e51eb81b2c0bbc6ceb38152
|
data/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,23 @@
|
|
|
15
15
|
`Glyphs::MainRactor::Unavailable` naming the icon, and `Glyphs.preload_svg`
|
|
16
16
|
still seeds a Ractor's cache. glyphs does not depend on ractor-dispatch: add
|
|
17
17
|
it outside Rails 8.2. Refs zoolutions/phlex-reactive#363
|
|
18
|
+
- **`config.raise_on_prunable` fails a render the prune would delete.** Off by
|
|
19
|
+
default. With it on, typically in the test environment, rendering such an icon
|
|
20
|
+
raises `Glyphs::PrunableIconError`, naming the library, variant and name, so a
|
|
21
|
+
test fails instead of production. The guard asks the pruner, so under one
|
|
22
|
+
configuration the two agree. The first uncached render scans the source, once
|
|
23
|
+
per process; with the setting off nothing is scanned. Enabling it may fail specs, and each such
|
|
24
|
+
failure is an icon that would have gone missing in production. Refs #35
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`glyphs:prune_icons` no longer deletes icons forwarded through a wrapper
|
|
29
|
+
component.** A file that passes a non-literal under a key like an icon
|
|
30
|
+
(`Badge(icon:)`, `Badge(icon: row.first)`) now keeps its icon-name-shaped
|
|
31
|
+
literals for every dynamically-rendered library. They were dropped when the
|
|
32
|
+
file itself had no dynamic icon call, so the icon raised
|
|
33
|
+
`Icons::IconNotFound` in production only. Pruning can only keep a little more
|
|
34
|
+
than before. Refs #35
|
|
18
35
|
|
|
19
36
|
## [0.3.0] - 2026-10-15
|
|
20
37
|
|
data/README.md
CHANGED
|
@@ -180,11 +180,48 @@ The scanner resolves them from source, so you rarely need `keep_icons` at all:
|
|
|
180
180
|
constant named like one (`ICON = :bell`, `STATUS_ICONS = { .. => :warning }`,
|
|
181
181
|
including trailing `.freeze`) — are kept for every dynamically-rendered
|
|
182
182
|
library, so a name declared in one file and rendered from another survives.
|
|
183
|
+
- **Forwarded** — a file that passes a non-literal under a key like an icon
|
|
184
|
+
(`Badge(icon:)`, `Badge(icon: row.first)`) hands a name on to a wrapper
|
|
185
|
+
component, so every icon-name-shaped literal in that file is kept for every
|
|
186
|
+
dynamically-rendered library. `ROWS = [[:bell, 1]]` survives when the row
|
|
187
|
+
reaches `Badge(icon:)`, and `Badge`'s `LucideIcon(@icon)` lives in another file.
|
|
183
188
|
|
|
184
189
|
Only literals are harvested, so the scanner never invents a reference; the
|
|
185
190
|
worst case is keeping a coincidentally icon-named string, which the post-prune
|
|
186
191
|
verification tolerates.
|
|
187
192
|
|
|
193
|
+
A static scan can't follow every value. A literal passed positionally to a
|
|
194
|
+
wrapper (`m.row("Alerts", :anchor)`) is still pruned, and so is a forwarded
|
|
195
|
+
keyword in a template, which is text-scanned. Nothing fails until production:
|
|
196
|
+
development and test keep every SVG, and the post-prune verification doesn't
|
|
197
|
+
assert harvested names. `raise_on_prunable` closes that gap.
|
|
198
|
+
|
|
199
|
+
### `raise_on_prunable` — fail the test that renders a prunable icon
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
# config/environments/test.rb, or an initializer guarded by Rails.env.test?
|
|
203
|
+
Glyphs.configure do |config|
|
|
204
|
+
config.raise_on_prunable = true
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
With it on, rendering an icon the prune would delete raises
|
|
209
|
+
`Glyphs::PrunableIconError`, naming the library, variant and name. Every page a
|
|
210
|
+
test renders becomes a check, and each failure is a production bug: name the icon
|
|
211
|
+
in an icon position (`icon: :anchor`, an `ICON` constant) or list it in
|
|
212
|
+
`keep_icons`.
|
|
213
|
+
|
|
214
|
+
The guard asks the pruner itself, so the two agree, including on `keep_icons`
|
|
215
|
+
globs and on the libraries a prune leaves alone. That holds for the configuration
|
|
216
|
+
the test runs with: a `keep_icons`, `fallback_icons` or `prune_source_globs` that
|
|
217
|
+
differs by `Rails.env` makes the guard answer for a different keep-set than your
|
|
218
|
+
image build prunes with. The first uncached render scans
|
|
219
|
+
your source, once per process (a parallel test worker scans for itself), and
|
|
220
|
+
cache hits never reach the check. A name that doesn't exist at all still meets
|
|
221
|
+
the missing-icon policy. The default is `false`, which scans nothing, and
|
|
222
|
+
source changed after the scan isn't seen until the process restarts, so keep this
|
|
223
|
+
in the test environment.
|
|
224
|
+
|
|
188
225
|
### `keep_icons` — the last-resort escape hatch
|
|
189
226
|
|
|
190
227
|
For names a static scan genuinely can't see — read from a database, an ENV var,
|
data/lib/glyphs/configuration.rb
CHANGED
|
@@ -24,8 +24,11 @@ module Glyphs
|
|
|
24
24
|
# prune_source_globs: extra locations the pruner's scanner text-scans IN
|
|
25
25
|
# ADDITION to the built-in Ruby/template defaults — e.g. a config file that
|
|
26
26
|
# names icons. Never replaces the defaults (nil = defaults only).
|
|
27
|
+
# raise_on_prunable: boolean; when true, rendering an icon glyphs:prune_icons
|
|
28
|
+
# would delete raises Glyphs::PrunableIconError. For the test environment:
|
|
29
|
+
# the first uncached render scans the source, once per process.
|
|
27
30
|
attr_accessor :raise_on_missing, :on_missing_icon, :fallback_icons, :cache_svgs,
|
|
28
|
-
:keep_icons, :prune_source_globs
|
|
31
|
+
:keep_icons, :prune_source_globs, :raise_on_prunable
|
|
29
32
|
attr_reader :svg_cache_size
|
|
30
33
|
|
|
31
34
|
def initialize
|
|
@@ -36,6 +39,7 @@ module Glyphs
|
|
|
36
39
|
@svg_cache_size = DEFAULT_SVG_CACHE_SIZE
|
|
37
40
|
@keep_icons = []
|
|
38
41
|
@prune_source_globs = nil
|
|
42
|
+
@raise_on_prunable = false
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def svg_cache_size=(size)
|
data/lib/glyphs/icon_pruner.rb
CHANGED
|
@@ -38,14 +38,23 @@ module Glyphs
|
|
|
38
38
|
next
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
stat = prune_group(library, variant, files, keep, deleted_names)
|
|
43
|
-
stats << stat
|
|
41
|
+
stats << prune_group(library, variant, files, unkept_files(library, variant, files), deleted_names)
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
PruneReport.new(stats:, deleted_names:, dry_run: @dry_run)
|
|
47
45
|
end
|
|
48
46
|
|
|
47
|
+
# The Set of IconReferences #call would delete: same files, same keep-sets.
|
|
48
|
+
# Deletes nothing and warns about nothing.
|
|
49
|
+
def prunable
|
|
50
|
+
groups = grouped_files.select { |(library, _), _| library_used?(library) }
|
|
51
|
+
groups.each_with_object(Set.new) do |((library, variant), files), references|
|
|
52
|
+
unkept_files(library, variant, files).each do |path|
|
|
53
|
+
references << IconReference.new(library:, variant:, name: File.basename(path, ".svg"))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
49
58
|
private
|
|
50
59
|
|
|
51
60
|
# { [library, variant] => [absolute svg paths] }, excluding the animated lib.
|
|
@@ -69,25 +78,19 @@ module Glyphs
|
|
|
69
78
|
end
|
|
70
79
|
end
|
|
71
80
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
files.each do |path|
|
|
78
|
-
name = File.basename(path, ".svg")
|
|
79
|
-
if keep_file?(name, keep)
|
|
80
|
-
kept += 1
|
|
81
|
-
next
|
|
82
|
-
end
|
|
81
|
+
# The one decision of what goes, shared by #call and #prunable.
|
|
82
|
+
def unkept_files(library, variant, files)
|
|
83
|
+
keep = keep_names_for(library, variant)
|
|
84
|
+
files.reject { |path| keep_file?(File.basename(path, ".svg"), keep) }
|
|
85
|
+
end
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
end
|
|
87
|
+
def prune_group(library, variant, files, unkept, deleted_names)
|
|
88
|
+
bytes_freed = unkept.sum { |path| File.size(path) }
|
|
89
|
+
deleted_names.concat(unkept.map { |path| File.basename(path, ".svg") })
|
|
90
|
+
unkept.each { |path| File.delete(path) } unless @dry_run
|
|
89
91
|
|
|
90
|
-
PruneReport::LibraryStat.new(library:, variant:, kept
|
|
92
|
+
PruneReport::LibraryStat.new(library:, variant:, kept: files.size - unkept.size, deleted: unkept.size,
|
|
93
|
+
bytes_freed:)
|
|
91
94
|
end
|
|
92
95
|
|
|
93
96
|
def keep_file?(name, keep)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glyphs
|
|
4
|
+
# Raised on render, with Configuration#raise_on_prunable on, for an icon that
|
|
5
|
+
# exists now and that glyphs:prune_icons would delete from the image. Not an
|
|
6
|
+
# Icons::IconNotFound: the missing-icon policy must not turn it into a fallback.
|
|
7
|
+
class PrunableIconError < StandardError; end
|
|
8
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glyphs
|
|
4
|
+
# Backs Configuration#raise_on_prunable: a render of an icon the prune would
|
|
5
|
+
# delete fails in the test suite instead of in production, where development's
|
|
6
|
+
# full SVG tree no longer hides it. Dynamic keeps are advisory, so
|
|
7
|
+
# PruneRunner#verify! cannot catch this; a render names a real icon, so here
|
|
8
|
+
# it is safe to fail.
|
|
9
|
+
#
|
|
10
|
+
# It asks the pruner, so under one configuration the two agree, and it asks
|
|
11
|
+
# once: the scan runs in the constructor and #check! only reads the result. It
|
|
12
|
+
# deletes nothing. Glyphs keeps one per process, replacing it when #current?
|
|
13
|
+
# says the settings behind that answer changed.
|
|
14
|
+
class PruneGuard
|
|
15
|
+
def initialize(config: Glyphs.configuration, runner: PruneRunner.new(config:))
|
|
16
|
+
@keep_settings = keep_settings(config)
|
|
17
|
+
@prunable = runner.prunable
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def current?(config)
|
|
21
|
+
@keep_settings == keep_settings(config)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def check!(name:, library:, variant:)
|
|
25
|
+
library = library.to_sym
|
|
26
|
+
variant = variant.nil? ? IconReference.default_variant_for(library) : IconReference.normalize_variant(variant)
|
|
27
|
+
reference = IconReference.new(library:, variant:, name: name.to_s)
|
|
28
|
+
raise PrunableIconError, message(reference) if @prunable.include?(reference)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# A hash of the values, so a list changed in place reads as a change.
|
|
34
|
+
def keep_settings(config)
|
|
35
|
+
[config.keep_icons, config.fallback_icons, config.prune_source_globs].hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def message(reference)
|
|
39
|
+
icon = [reference.library, reference.variant, reference.name].compact.join("/")
|
|
40
|
+
"glyphs:prune_icons would delete #{icon}, so this render would fail in production. " \
|
|
41
|
+
"Name it in an icon position (`icon: :#{reference.name.tr('-', '_')}`, an `ICON` constant) " \
|
|
42
|
+
"or list it in keep_icons."
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/glyphs/prune_runner.rb
CHANGED
|
@@ -28,6 +28,12 @@ module Glyphs
|
|
|
28
28
|
report
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# The IconReferences a prune would delete, deleting nothing: the pruner's own
|
|
32
|
+
# answer over the same scan and keep-sets #call uses.
|
|
33
|
+
def prunable
|
|
34
|
+
pruner.prunable
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
# Asserts every kept icon resolves to a file on disk. Raises
|
|
32
38
|
# VerificationError listing the first few misses.
|
|
33
39
|
def verify!
|
|
@@ -156,7 +156,7 @@ module Glyphs
|
|
|
156
156
|
# name the AST can't read (`LucideIcon(tile[:icon])`) — across every scanned
|
|
157
157
|
# Ruby file, then turns that evidence into IconReferences.
|
|
158
158
|
#
|
|
159
|
-
#
|
|
159
|
+
# Three sources of candidate names, all harvested only from literals so we
|
|
160
160
|
# never invent a reference:
|
|
161
161
|
#
|
|
162
162
|
# 1. file-scoped — a file that dynamically renders library L contributes
|
|
@@ -166,6 +166,10 @@ module Glyphs
|
|
|
166
166
|
# (a hash pair keyed `/icon/i`, or a constant named `/ICON/`) are
|
|
167
167
|
# candidates for EVERY dynamically-rendered library, since a bare
|
|
168
168
|
# `ICON = :bell` in one file is often rendered from another.
|
|
169
|
+
# 3. forwarded — a file passing a non-literal under a key like an icon
|
|
170
|
+
# (`Badge(icon:)`, `Badge(icon: row.first)`) hands a name on to a wrapper
|
|
171
|
+
# that renders it elsewhere, so ALL its literals are candidates for
|
|
172
|
+
# every dynamically-rendered library, as declarations are.
|
|
169
173
|
#
|
|
170
174
|
# Names resolve at the library's default variant: a dynamic call names no
|
|
171
175
|
# variant in practice, and the pruner keeps per (library, variant).
|
|
@@ -179,6 +183,7 @@ module Glyphs
|
|
|
179
183
|
# Folds one file's visitor results into the running accumulator.
|
|
180
184
|
def absorb(visitor)
|
|
181
185
|
@declaration_literals.merge(visitor.declaration_literals)
|
|
186
|
+
@declaration_literals.merge(visitor.file_literals) if visitor.forwards_icon
|
|
182
187
|
return if visitor.dynamic_libraries.empty?
|
|
183
188
|
|
|
184
189
|
@dynamic_libraries.merge(visitor.dynamic_libraries)
|
|
@@ -205,18 +210,28 @@ module Glyphs
|
|
|
205
210
|
# Excludes CSS classes (spaces/slashes), paths, and interpolated strings.
|
|
206
211
|
ICON_NAME_LITERAL = /\A[a-z][a-z0-9_-]*\z/
|
|
207
212
|
|
|
213
|
+
# Values of an icon-keyed pair that forward nothing: a declaration harvest
|
|
214
|
+
# reads the names, and `icon: nil` or `icon_size: 16` names no icon at all.
|
|
215
|
+
LITERAL_VALUES = [
|
|
216
|
+
Prism::SymbolNode, Prism::StringNode, Prism::NilNode, Prism::TrueNode, Prism::FalseNode,
|
|
217
|
+
Prism::IntegerNode, Prism::FloatNode
|
|
218
|
+
].freeze
|
|
219
|
+
|
|
208
220
|
# Libraries this file renders with a dynamic (non-literal) first argument.
|
|
209
221
|
attr_reader :dynamic_libraries
|
|
210
222
|
# Icon-name-shaped literals anywhere in this file (file-scoped candidates).
|
|
211
223
|
attr_reader :file_literals
|
|
212
224
|
# Literals in icon-declaration positions (`icon:` pairs, `ICON` constants).
|
|
213
225
|
attr_reader :declaration_literals
|
|
226
|
+
# Whether this file passes a non-literal under a key like an icon.
|
|
227
|
+
attr_reader :forwards_icon
|
|
214
228
|
|
|
215
229
|
def initialize(references)
|
|
216
230
|
@references = references
|
|
217
231
|
@dynamic_libraries = Set.new
|
|
218
232
|
@file_literals = Set.new
|
|
219
233
|
@declaration_literals = Set.new
|
|
234
|
+
@forwards_icon = false
|
|
220
235
|
super()
|
|
221
236
|
end
|
|
222
237
|
|
|
@@ -247,10 +262,14 @@ module Glyphs
|
|
|
247
262
|
end
|
|
248
263
|
|
|
249
264
|
# `icon: :activity` / `menu_icon: "gear"` — a hash pair keyed like an icon
|
|
250
|
-
# declares an icon name, wherever it lives.
|
|
265
|
+
# declares an icon name, wherever it lives. A non-literal value there
|
|
266
|
+
# (`icon:`, `icon: row.first`) forwards a name instead.
|
|
251
267
|
def visit_assoc_node(node)
|
|
252
268
|
key = node.key
|
|
253
|
-
|
|
269
|
+
if key.is_a?(Prism::SymbolNode) && key.unescaped.match?(/icon/i)
|
|
270
|
+
collect_declaration(node.value)
|
|
271
|
+
@forwards_icon ||= forwarded_value?(node.value)
|
|
272
|
+
end
|
|
254
273
|
super
|
|
255
274
|
end
|
|
256
275
|
|
|
@@ -258,6 +277,20 @@ module Glyphs
|
|
|
258
277
|
|
|
259
278
|
attr_reader :references
|
|
260
279
|
|
|
280
|
+
# A container forwards when anything inside it does: `icon: [:a, pick]`.
|
|
281
|
+
def forwarded_value?(value_node)
|
|
282
|
+
value_node = unwrap_declaration_wrappers(value_node)
|
|
283
|
+
case value_node
|
|
284
|
+
when Prism::ArrayNode then value_node.elements.any? { |element| forwarded_value?(element) }
|
|
285
|
+
when Prism::HashNode then value_node.elements.any? { |element| forwarded_pair?(element) }
|
|
286
|
+
else LITERAL_VALUES.none? { |literal| value_node.is_a?(literal) }
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def forwarded_pair?(element)
|
|
291
|
+
!element.is_a?(Prism::AssocNode) || forwarded_value?(element.value)
|
|
292
|
+
end
|
|
293
|
+
|
|
261
294
|
def record(node)
|
|
262
295
|
method = node.name.to_s
|
|
263
296
|
if GENERIC_CALLS.include?(node.name)
|
data/lib/glyphs/version.rb
CHANGED
data/lib/glyphs.rb
CHANGED
|
@@ -38,6 +38,8 @@ module Glyphs
|
|
|
38
38
|
# readers never take it, so svg_for runs in any Ractor once finalized.
|
|
39
39
|
@lock = Monitor.new
|
|
40
40
|
@configuration = Configuration.new
|
|
41
|
+
@prune_guard = nil
|
|
42
|
+
@prune_guard_lock = Mutex.new
|
|
41
43
|
|
|
42
44
|
class << self
|
|
43
45
|
# The current configuration: a plain read, shareable after finalize!.
|
|
@@ -49,6 +51,7 @@ module Glyphs
|
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
def reset_configuration!
|
|
54
|
+
@prune_guard_lock.synchronize { @prune_guard = nil }
|
|
52
55
|
@lock.synchronize { @configuration = Configuration.new }
|
|
53
56
|
end
|
|
54
57
|
|
|
@@ -128,12 +131,28 @@ module Glyphs
|
|
|
128
131
|
resolve_svg(name:, library:, variant:, attributes:)
|
|
129
132
|
end
|
|
130
133
|
|
|
134
|
+
# Only a miss gets here, and only on the main Ractor. A name with no file is
|
|
135
|
+
# never prunable, so it passes the guard and meets the missing-icon policy.
|
|
131
136
|
def resolve_svg(name:, library:, variant:, attributes:)
|
|
137
|
+
prune_guard&.check!(name:, library:, variant:)
|
|
132
138
|
build_svg(name:, library:, variant:, attributes:)
|
|
133
139
|
rescue Icons::IconNotFound => e
|
|
134
140
|
handle_missing_icon(e, name:, library:, variant:, attributes:)
|
|
135
141
|
end
|
|
136
142
|
|
|
143
|
+
# nil while raise_on_prunable is off, so that path reads nothing. A racing
|
|
144
|
+
# thread waits for the first scan rather than repeating it. Not @lock: this
|
|
145
|
+
# runs under the SvgCache mutex, and a configure block holding @lock may render.
|
|
146
|
+
def prune_guard
|
|
147
|
+
config = configuration
|
|
148
|
+
return unless config.raise_on_prunable
|
|
149
|
+
|
|
150
|
+
@prune_guard_lock.synchronize do
|
|
151
|
+
@prune_guard = PruneGuard.new(config:) unless @prune_guard&.current?(config)
|
|
152
|
+
@prune_guard
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
137
156
|
def build_svg(name:, library:, variant:, attributes:)
|
|
138
157
|
Icons::Icon.new(name:, library:, variant:, arguments: attributes).svg
|
|
139
158
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: glyphs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -79,6 +79,8 @@ files:
|
|
|
79
79
|
- lib/glyphs/lucide_icon.rb
|
|
80
80
|
- lib/glyphs/main_ractor.rb
|
|
81
81
|
- lib/glyphs/phosphor_icon.rb
|
|
82
|
+
- lib/glyphs/prunable_icon_error.rb
|
|
83
|
+
- lib/glyphs/prune_guard.rb
|
|
82
84
|
- lib/glyphs/prune_report.rb
|
|
83
85
|
- lib/glyphs/prune_runner.rb
|
|
84
86
|
- lib/glyphs/radix_icon.rb
|