unmagic-icon 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/CHANGELOG.md +37 -0
- data/LICENSE +21 -0
- data/README.md +156 -0
- data/lib/tasks/unmagic/icon/download.rake +26 -0
- data/lib/tasks/unmagic/icon/install.rake +25 -0
- data/lib/unmagic/icon/action_view_helpers.rb +14 -0
- data/lib/unmagic/icon/configuration.rb +34 -0
- data/lib/unmagic/icon/engine.rb +44 -0
- data/lib/unmagic/icon/library/registry.rb +81 -0
- data/lib/unmagic/icon/library/source/devicons.rb +18 -0
- data/lib/unmagic/icon/library/source/feather.rb +18 -0
- data/lib/unmagic/icon/library/source/heroicons.rb +23 -0
- data/lib/unmagic/icon/library/source/lucide.rb +18 -0
- data/lib/unmagic/icon/library/source/material_file_icons.rb +64 -0
- data/lib/unmagic/icon/library/source/silk.rb +44 -0
- data/lib/unmagic/icon/library/source/simple_icons.rb +18 -0
- data/lib/unmagic/icon/library/source/tabler.rb +18 -0
- data/lib/unmagic/icon/library/source.rb +242 -0
- data/lib/unmagic/icon/library.rb +114 -0
- data/lib/unmagic/icon/version.rb +7 -0
- data/lib/unmagic/icon/web/public/favicon.png +0 -0
- data/lib/unmagic/icon/web/views/layout.html.erb +329 -0
- data/lib/unmagic/icon/web.rb +70 -0
- data/lib/unmagic/icon.rb +134 -0
- data/lib/unmagic_icon.rb +3 -0
- metadata +143 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rack"
|
|
4
|
+
require "rack/utils"
|
|
5
|
+
|
|
6
|
+
require "erb"
|
|
7
|
+
require "cgi"
|
|
8
|
+
|
|
9
|
+
module Unmagic
|
|
10
|
+
class Icon
|
|
11
|
+
class Web
|
|
12
|
+
def self.call(env)
|
|
13
|
+
app.call(env)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.app
|
|
17
|
+
@app ||= Rack::Builder.new do
|
|
18
|
+
public_path = File.expand_path("web/public", __dir__)
|
|
19
|
+
use Rack::Static, urls: [ "/public" ], root: File.dirname(public_path)
|
|
20
|
+
run Unmagic::Icon::Web.new
|
|
21
|
+
end.to_app
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(env)
|
|
25
|
+
@request = Rack::Request.new(env)
|
|
26
|
+
@libraries = Unmagic::Icon::Library::Registry.all
|
|
27
|
+
|
|
28
|
+
case @request.path_info
|
|
29
|
+
when "/"
|
|
30
|
+
# Redirect to first library
|
|
31
|
+
first_library = @libraries.first.name.to_param
|
|
32
|
+
if first_library.nil?
|
|
33
|
+
[ 404, { "content-type" => "text/plain" }, [ "No libraries found" ] ]
|
|
34
|
+
else
|
|
35
|
+
[ 302, { "location" => url(first_library, @request.params) }, [] ]
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
library_name = @request.path_info.delete_prefix("/")
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
@selected_library = Unmagic::Icon::Library::Registry.find(library_name)
|
|
42
|
+
rescue Unmagic::Icon::LibraryNotError
|
|
43
|
+
return [ 404, { "content-type" => "text/plain" }, [ "Library not found: #{@selected_library}" ] ]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
template_path = File.expand_path("web/views/layout.html.erb", __dir__)
|
|
47
|
+
template = ERB.new(File.read(template_path))
|
|
48
|
+
html = template.result(binding)
|
|
49
|
+
|
|
50
|
+
[ 200, { "content-type" => "text/html; charset=utf-8" }, [ html ] ]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def escape(text)
|
|
55
|
+
CGI.escapeHTML(text.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def url(path_parts, params_hash = nil)
|
|
59
|
+
url = [ @request.env["SCRIPT_NAME"], *path_parts ].compact.join("/")
|
|
60
|
+
url = url.gsub(/\/\//, "/")
|
|
61
|
+
|
|
62
|
+
if params_hash
|
|
63
|
+
"#{url}?#{Rack::Utils.build_query(params_hash)}"
|
|
64
|
+
else
|
|
65
|
+
url
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/unmagic/icon.rb
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "active_support/core_ext/object/blank"
|
|
5
|
+
require "active_support/core_ext/string/output_safety"
|
|
6
|
+
require "active_support/core_ext/string/inflections"
|
|
7
|
+
|
|
8
|
+
require_relative "icon/version"
|
|
9
|
+
require_relative "icon/configuration"
|
|
10
|
+
require_relative "icon/library"
|
|
11
|
+
require_relative "icon/library/registry"
|
|
12
|
+
require_relative "icon/action_view_helpers"
|
|
13
|
+
require_relative "icon/engine" if defined?(Rails)
|
|
14
|
+
|
|
15
|
+
module Unmagic
|
|
16
|
+
class Icon
|
|
17
|
+
class Error < StandardError; end
|
|
18
|
+
class LibraryNotFoundError < Error; end
|
|
19
|
+
class IconNotFoundError < Error; end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def init
|
|
23
|
+
yield(configuration) if block_given?
|
|
24
|
+
@initialized = true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialized?
|
|
28
|
+
@initialized == true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configure
|
|
32
|
+
yield(configuration) if block_given?
|
|
33
|
+
configuration
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def configuration
|
|
37
|
+
@configuration ||= Unmagic::Icon::Configuration.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def libraries
|
|
41
|
+
Unmagic::Icon::Library::Registry.all
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def find(reference)
|
|
45
|
+
library_path, icon_name = parse_reference(reference)
|
|
46
|
+
|
|
47
|
+
Unmagic::Icon::Library::Registry.find(library_path).find(icon_name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Parse a "library/name" reference into [library, name], tolerating the
|
|
51
|
+
# emoji-style ":library/name:" decoration so both kinds share one syntax.
|
|
52
|
+
def parse_reference(reference)
|
|
53
|
+
*library_parts, name = reference.to_s.gsub(/\A:|:\z/, "").split("/")
|
|
54
|
+
[ library_parts.join("/"), name ]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
attr_reader :name, :path
|
|
59
|
+
|
|
60
|
+
def initialize(name:, path:)
|
|
61
|
+
@name = name
|
|
62
|
+
@path = path
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def doc
|
|
66
|
+
Nokogiri::XML(raw_svg_content)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def attributes
|
|
70
|
+
extracted_svg[:attributes]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def contents
|
|
74
|
+
extracted_svg[:contents].strip
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def as_json
|
|
78
|
+
{ name: name, svg: to_svg }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Render the asset to HTML, dispatching on its kind. SVG inlines today; a
|
|
82
|
+
# future raster asset (png/gif) would emit an <img> here, so callers stay
|
|
83
|
+
# render-agnostic.
|
|
84
|
+
def render(options = {})
|
|
85
|
+
case File.extname(@path).downcase
|
|
86
|
+
when ".svg" then to_svg(options)
|
|
87
|
+
else
|
|
88
|
+
raise Unmagic::Icon::Error, "Don't know how to render #{@path}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Render the SVG with a `unmagic-icon` class (plus any caller class) and a
|
|
93
|
+
# `data-unmagic-icon` marker. Any other options are merged verbatim as
|
|
94
|
+
# attributes on the <svg> — so the caller controls accessibility
|
|
95
|
+
# (`aria-hidden`, `aria-label`, `role`), `id`, `data-*`, etc.
|
|
96
|
+
def to_svg(options = {})
|
|
97
|
+
return @svg_cache if options.empty? && @svg_cache
|
|
98
|
+
|
|
99
|
+
attributes = options.transform_keys(&:to_s)
|
|
100
|
+
css_classes = [ "unmagic-icon", attributes.delete("class") ].compact.join(" ")
|
|
101
|
+
|
|
102
|
+
svg = raw_svg_content.dup
|
|
103
|
+
svg.sub!(/<svg(\s+[^>]*)?>/i) do
|
|
104
|
+
existing = (::Regexp.last_match(1) || "").gsub(/\sclass=["'][^"']*["']/, "")
|
|
105
|
+
extra = attributes.map { |name, value| %(#{name}="#{CGI.escapeHTML(value.to_s)}") }
|
|
106
|
+
%(<svg#{existing} class="#{css_classes}" data-unmagic-icon="#{@name}"#{extra.empty? ? "" : " #{extra.join(' ')}"}>)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
svg = svg.html_safe
|
|
110
|
+
@svg_cache = svg if options.empty?
|
|
111
|
+
svg
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def raw_svg_content
|
|
117
|
+
@raw_svg_content ||= File.read(@path)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def extracted_svg
|
|
121
|
+
@xml ||=
|
|
122
|
+
begin
|
|
123
|
+
root = doc.at_css("//svg")
|
|
124
|
+
{
|
|
125
|
+
attributes: root.attributes.inject({}) do |hash, (key, value)|
|
|
126
|
+
hash[key] = value.value
|
|
127
|
+
hash
|
|
128
|
+
end,
|
|
129
|
+
contents: root.children.to_xml
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
data/lib/unmagic_icon.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: unmagic-icon
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Keith Pitt
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-06-21 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '7.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '7.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: railties
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '7.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '7.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: nokogiri
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 1.8.5
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.8.5
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.12'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.12'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '13.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '13.0'
|
|
83
|
+
description: Render SVG icons inline in Rails views, resolve them through library/name
|
|
84
|
+
references and aliases, and download popular icon sets (Heroicons, Lucide, Tabler,
|
|
85
|
+
Feather, and more)
|
|
86
|
+
email:
|
|
87
|
+
- keith@unreasonable-magic.com
|
|
88
|
+
executables: []
|
|
89
|
+
extensions: []
|
|
90
|
+
extra_rdoc_files: []
|
|
91
|
+
files:
|
|
92
|
+
- CHANGELOG.md
|
|
93
|
+
- LICENSE
|
|
94
|
+
- README.md
|
|
95
|
+
- lib/tasks/unmagic/icon/download.rake
|
|
96
|
+
- lib/tasks/unmagic/icon/install.rake
|
|
97
|
+
- lib/unmagic/icon.rb
|
|
98
|
+
- lib/unmagic/icon/action_view_helpers.rb
|
|
99
|
+
- lib/unmagic/icon/configuration.rb
|
|
100
|
+
- lib/unmagic/icon/engine.rb
|
|
101
|
+
- lib/unmagic/icon/library.rb
|
|
102
|
+
- lib/unmagic/icon/library/registry.rb
|
|
103
|
+
- lib/unmagic/icon/library/source.rb
|
|
104
|
+
- lib/unmagic/icon/library/source/devicons.rb
|
|
105
|
+
- lib/unmagic/icon/library/source/feather.rb
|
|
106
|
+
- lib/unmagic/icon/library/source/heroicons.rb
|
|
107
|
+
- lib/unmagic/icon/library/source/lucide.rb
|
|
108
|
+
- lib/unmagic/icon/library/source/material_file_icons.rb
|
|
109
|
+
- lib/unmagic/icon/library/source/silk.rb
|
|
110
|
+
- lib/unmagic/icon/library/source/simple_icons.rb
|
|
111
|
+
- lib/unmagic/icon/library/source/tabler.rb
|
|
112
|
+
- lib/unmagic/icon/version.rb
|
|
113
|
+
- lib/unmagic/icon/web.rb
|
|
114
|
+
- lib/unmagic/icon/web/public/favicon.png
|
|
115
|
+
- lib/unmagic/icon/web/views/layout.html.erb
|
|
116
|
+
- lib/unmagic_icon.rb
|
|
117
|
+
homepage: https://github.com/unreasonable-magic/unmagic-icon
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata:
|
|
121
|
+
homepage_uri: https://github.com/unreasonable-magic/unmagic-icon
|
|
122
|
+
source_code_uri: https://github.com/unreasonable-magic/unmagic-icon
|
|
123
|
+
changelog_uri: https://github.com/unreasonable-magic/unmagic-icon/blob/main/CHANGELOG.md
|
|
124
|
+
post_install_message:
|
|
125
|
+
rdoc_options: []
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '3.0'
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubygems_version: 3.5.22
|
|
140
|
+
signing_key:
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: Inline SVG icons for Rails, with downloadable icon libraries
|
|
143
|
+
test_files: []
|