jekyll-icon-flow 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 +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +166 -0
- data/assets/icons/lucide/arrow-down.svg +16 -0
- data/assets/icons/lucide/arrow-left.svg +16 -0
- data/assets/icons/lucide/arrow-right.svg +16 -0
- data/assets/icons/lucide/arrow-up.svg +16 -0
- data/assets/icons/lucide/calendar.svg +18 -0
- data/assets/icons/lucide/check.svg +15 -0
- data/assets/icons/lucide/chevron-down.svg +15 -0
- data/assets/icons/lucide/chevron-left.svg +15 -0
- data/assets/icons/lucide/chevron-right.svg +15 -0
- data/assets/icons/lucide/chevron-up.svg +15 -0
- data/assets/icons/lucide/copy.svg +16 -0
- data/assets/icons/lucide/download.svg +17 -0
- data/assets/icons/lucide/external-link.svg +17 -0
- data/assets/icons/lucide/file-text.svg +19 -0
- data/assets/icons/lucide/folder.svg +15 -0
- data/assets/icons/lucide/home.svg +16 -0
- data/assets/icons/lucide/info.svg +17 -0
- data/assets/icons/lucide/link.svg +16 -0
- data/assets/icons/lucide/mail.svg +16 -0
- data/assets/icons/lucide/map-pin.svg +16 -0
- data/assets/icons/lucide/map.svg +17 -0
- data/assets/icons/lucide/menu.svg +17 -0
- data/assets/icons/lucide/moon.svg +15 -0
- data/assets/icons/lucide/printer.svg +17 -0
- data/assets/icons/lucide/rss.svg +17 -0
- data/assets/icons/lucide/search.svg +16 -0
- data/assets/icons/lucide/sun.svg +23 -0
- data/assets/icons/lucide/tag.svg +16 -0
- data/assets/icons/lucide/triangle-alert.svg +17 -0
- data/assets/icons/lucide/x.svg +16 -0
- data/assets/icons/simple/bluesky.svg +1 -0
- data/assets/icons/simple/discord.svg +1 -0
- data/assets/icons/simple/facebook.svg +1 -0
- data/assets/icons/simple/github.svg +1 -0
- data/assets/icons/simple/instagram.svg +1 -0
- data/assets/icons/simple/mastodon.svg +1 -0
- data/assets/icons/simple/medium.svg +1 -0
- data/assets/icons/simple/rss.svg +1 -0
- data/assets/icons/simple/spotify.svg +1 -0
- data/assets/icons/simple/telegram.svg +1 -0
- data/assets/icons/simple/tiktok.svg +1 -0
- data/assets/icons/simple/x.svg +1 -0
- data/assets/icons/simple/youtube.svg +1 -0
- data/lib/jekyll/icon_flow/adapter.rb +144 -0
- data/lib/jekyll/icon_flow/adapters/custom.rb +38 -0
- data/lib/jekyll/icon_flow/adapters/lucide.rb +28 -0
- data/lib/jekyll/icon_flow/adapters/simple.rb +28 -0
- data/lib/jekyll/icon_flow/error.rb +7 -0
- data/lib/jekyll/icon_flow/icon_ref_tag.rb +46 -0
- data/lib/jekyll/icon_flow/icon_tag.rb +156 -0
- data/lib/jekyll/icon_flow/svg_reader.rb +29 -0
- data/lib/jekyll/icon_flow/version.rb +7 -0
- data/lib/jekyll/icon_flow.rb +36 -0
- data/lib/jekyll-icon-flow.rb +3 -0
- metadata +124 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- @license lucide-static v1.48.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-search"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<path d="m21 21-4.34-4.34" />
|
|
15
|
+
<circle cx="11" cy="11" r="8" />
|
|
16
|
+
</svg>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!-- @license lucide-static v1.48.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-sun"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<circle cx="12" cy="12" r="4" />
|
|
15
|
+
<path d="M12 2v2" />
|
|
16
|
+
<path d="M12 20v2" />
|
|
17
|
+
<path d="m4.93 4.93 1.41 1.41" />
|
|
18
|
+
<path d="m17.66 17.66 1.41 1.41" />
|
|
19
|
+
<path d="M2 12h2" />
|
|
20
|
+
<path d="M20 12h2" />
|
|
21
|
+
<path d="m6.34 17.66-1.41 1.41" />
|
|
22
|
+
<path d="m19.07 4.93-1.41 1.41" />
|
|
23
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- @license lucide-static v1.48.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-tag"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<path d="M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z" />
|
|
15
|
+
<circle cx="7.5" cy="7.5" r=".5" fill="currentColor" />
|
|
16
|
+
</svg>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!-- @license lucide-static v1.48.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-triangle-alert"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" />
|
|
15
|
+
<path d="M12 9v4" />
|
|
16
|
+
<path d="M12 17h.01" />
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!-- @license lucide-static v1.48.0 - ISC -->
|
|
2
|
+
<svg
|
|
3
|
+
class="lucide lucide-x"
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="24"
|
|
6
|
+
height="24"
|
|
7
|
+
viewBox="0 0 24 24"
|
|
8
|
+
fill="none"
|
|
9
|
+
stroke="currentColor"
|
|
10
|
+
stroke-width="2"
|
|
11
|
+
stroke-linecap="round"
|
|
12
|
+
stroke-linejoin="round"
|
|
13
|
+
>
|
|
14
|
+
<path d="M18 6 6 18" />
|
|
15
|
+
<path d="m6 6 12 12" />
|
|
16
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Discord</title><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Facebook</title><path d="M9.101 23.691v-7.98H6.627v-3.667h2.474v-1.58c0-4.085 1.848-5.978 5.858-5.978.401 0 .955.042 1.468.103a8.68 8.68 0 0 1 1.141.195v3.325a8.623 8.623 0 0 0-.653-.036 26.805 26.805 0 0 0-.733-.009c-.707 0-1.259.096-1.675.309a1.686 1.686 0 0 0-.679.622c-.258.42-.374.995-.374 1.752v1.297h3.919l-.386 2.103-.287 1.564h-3.246v8.245C19.396 23.238 24 18.179 24 12.044c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.628 3.874 10.35 9.101 11.647Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Instagram</title><path d="M7.0301.084c-1.2768.0602-2.1487.264-2.911.5634-.7888.3075-1.4575.72-2.1228 1.3877-.6652.6677-1.075 1.3368-1.3802 2.127-.2954.7638-.4956 1.6365-.552 2.914-.0564 1.2775-.0689 1.6882-.0626 4.947.0062 3.2586.0206 3.6671.0825 4.9473.061 1.2765.264 2.1482.5635 2.9107.308.7889.72 1.4573 1.388 2.1228.6679.6655 1.3365 1.0743 2.1285 1.38.7632.295 1.6361.4961 2.9134.552 1.2773.056 1.6884.069 4.9462.0627 3.2578-.0062 3.668-.0207 4.9478-.0814 1.28-.0607 2.147-.2652 2.9098-.5633.7889-.3086 1.4578-.72 2.1228-1.3881.665-.6682 1.0745-1.3378 1.3795-2.1284.2957-.7632.4966-1.636.552-2.9124.056-1.2809.0692-1.6898.063-4.948-.0063-3.2583-.021-3.6668-.0817-4.9465-.0607-1.2797-.264-2.1487-.5633-2.9117-.3084-.7889-.72-1.4568-1.3876-2.1228C21.2982 1.33 20.628.9208 19.8378.6165 19.074.321 18.2017.1197 16.9244.0645 15.6471.0093 15.236-.005 11.977.0014 8.718.0076 8.31.0215 7.0301.0839m.1402 21.6932c-1.17-.0509-1.8053-.2453-2.2287-.408-.5606-.216-.96-.4771-1.3819-.895-.422-.4178-.6811-.8186-.9-1.378-.1644-.4234-.3624-1.058-.4171-2.228-.0595-1.2645-.072-1.6442-.079-4.848-.007-3.2037.0053-3.583.0607-4.848.05-1.169.2456-1.805.408-2.2282.216-.5613.4762-.96.895-1.3816.4188-.4217.8184-.6814 1.3783-.9003.423-.1651 1.0575-.3614 2.227-.4171 1.2655-.06 1.6447-.072 4.848-.079 3.2033-.007 3.5835.005 4.8495.0608 1.169.0508 1.8053.2445 2.228.408.5608.216.96.4754 1.3816.895.4217.4194.6816.8176.9005 1.3787.1653.4217.3617 1.056.4169 2.2263.0602 1.2655.0739 1.645.0796 4.848.0058 3.203-.0055 3.5834-.061 4.848-.051 1.17-.245 1.8055-.408 2.2294-.216.5604-.4763.96-.8954 1.3814-.419.4215-.8181.6811-1.3783.9-.4224.1649-1.0577.3617-2.2262.4174-1.2656.0595-1.6448.072-4.8493.079-3.2045.007-3.5825-.006-4.848-.0608M16.953 5.5864A1.44 1.44 0 1 0 18.39 4.144a1.44 1.44 0 0 0-1.437 1.4424M5.8385 12.012c.0067 3.4032 2.7706 6.1557 6.173 6.1493 3.4026-.0065 6.157-2.7701 6.1506-6.1733-.0065-3.4032-2.771-6.1565-6.174-6.1498-3.403.0067-6.156 2.771-6.1496 6.1738M8 12.0077a4 4 0 1 1 4.008 3.9921A3.9996 3.9996 0 0 1 8 12.0077"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Mastodon</title><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Medium</title><path d="M4.21 0A4.201 4.201 0 0 0 0 4.21v15.58A4.201 4.201 0 0 0 4.21 24h15.58A4.201 4.201 0 0 0 24 19.79v-1.093c-.137.013-.278.02-.422.02-2.577 0-4.027-2.146-4.09-4.832a7.592 7.592 0 0 1 .022-.708c.093-1.186.475-2.241 1.105-3.022a3.885 3.885 0 0 1 1.395-1.1c.468-.237 1.127-.367 1.664-.367h.023c.101 0 .202.004.303.01V4.211A4.201 4.201 0 0 0 19.79 0Zm.198 5.583h4.165l3.588 8.435 3.59-8.435h3.864v.146l-.019.004c-.705.16-1.063.397-1.063 1.254h-.003l.003 10.274c.06.676.424.885 1.063 1.03l.02.004v.145h-4.923v-.145l.019-.005c.639-.144.994-.353 1.054-1.03V7.267l-4.745 11.15h-.261L6.15 7.569v9.445c0 .857.358 1.094 1.063 1.253l.02.004v.147H4.405v-.147l.019-.004c.705-.16 1.065-.397 1.065-1.253V6.987c0-.857-.358-1.094-1.064-1.254l-.018-.004zm19.25 3.668c-1.086.023-1.733 1.323-1.813 3.124H24V9.298a1.378 1.378 0 0 0-.342-.047Zm-1.862 3.632c-.1 1.756.86 3.239 2.204 3.634v-3.634z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>RSS</title><path d="M19.199 24C19.199 13.467 10.533 4.8 0 4.8V0c13.165 0 24 10.835 24 24h-4.801zM3.291 17.415c1.814 0 3.293 1.479 3.293 3.295 0 1.813-1.485 3.29-3.301 3.29C1.47 24 0 22.526 0 20.71s1.475-3.294 3.291-3.295zM15.909 24h-4.665c0-6.169-5.075-11.245-11.244-11.245V8.09c8.727 0 15.909 7.184 15.909 15.91z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Spotify</title><path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Telegram</title><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TikTok</title><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>X</title><path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module IconFlow
|
|
7
|
+
# Base icon-pack adapter. Resolves an icon name to an SVG file and
|
|
8
|
+
# normalizes the markup so every pack shares the same styling contract:
|
|
9
|
+
#
|
|
10
|
+
# * class "icon icon-<name>" merged into the root element
|
|
11
|
+
# * fixed width/height attributes removed; size via inline style —
|
|
12
|
+
# named sizes (xxs..xxl, see SIZES) or a literal CSS size; the
|
|
13
|
+
# default `m` = 1em fits the surrounding line height
|
|
14
|
+
# * color normalized to currentColor according to the pack's
|
|
15
|
+
# COLOR_MODEL (:stroke, :fill, or :auto for arbitrary SVGs)
|
|
16
|
+
# * data-icon-pack="<pack>" and role="img" on the root element
|
|
17
|
+
class Adapter
|
|
18
|
+
COLOR_MODEL = :auto
|
|
19
|
+
|
|
20
|
+
ICON_NAME = /\A[a-z0-9_-]+\z/
|
|
21
|
+
CSS_SIZE = /\A(?:0|(?:\d+(?:\.\d+)?|\.\d+)(?:px|em|rem|%|vw|vh|vmin|vmax|ch|ex))\z/i
|
|
22
|
+
|
|
23
|
+
# Named sizes — relative (em) so every step scales with the
|
|
24
|
+
# surrounding text/container; `m` (1em) fits the line height and
|
|
25
|
+
# is the default. A literal CSS size still works via size:.
|
|
26
|
+
SIZES = {
|
|
27
|
+
"xxs" => "0.5em",
|
|
28
|
+
"xs" => "0.75em",
|
|
29
|
+
"s" => "0.875em",
|
|
30
|
+
"m" => "1em",
|
|
31
|
+
"l" => "1.25em",
|
|
32
|
+
"xl" => "1.5em",
|
|
33
|
+
"xxl" => "2em"
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
def initialize(site = nil)
|
|
37
|
+
@site = site
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def pack_name
|
|
41
|
+
raise NotImplementedError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Absolute path of the SVG for icon_name, or nil when the pack
|
|
45
|
+
# does not contain it.
|
|
46
|
+
def path_for(_icon_name)
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def icon_names
|
|
51
|
+
return [] unless Dir.exist?(icons_dir)
|
|
52
|
+
|
|
53
|
+
Dir.children(icons_dir).grep(/\.svg\z/).map { |f| f.delete_suffix(".svg") }.sort
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def render(icon_name, options = {})
|
|
57
|
+
name = icon_name.to_s.strip.downcase
|
|
58
|
+
raise Error, "invalid icon name '#{icon_name}'" unless name.match?(ICON_NAME)
|
|
59
|
+
|
|
60
|
+
path = path_for(name)
|
|
61
|
+
raise Error, "icon '#{name}' not found in pack '#{pack_name}'" unless path
|
|
62
|
+
|
|
63
|
+
normalize(SvgReader.read(path), name, options)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def icons_dir
|
|
69
|
+
raise NotImplementedError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def normalize(svg, icon_name, options)
|
|
73
|
+
svg = svg.strip.sub(/\A<\?xml[^?]*\?>\s*/, "")
|
|
74
|
+
svg = normalize_custom_colors(svg) if self.class::COLOR_MODEL == :auto
|
|
75
|
+
svg = svg.sub(/<svg[^>]*>/) { |tag| normalize_root(tag, icon_name, options) }
|
|
76
|
+
options["title"] ? inject_title(svg, options["title"].to_s) : svg
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def normalize_root(tag, icon_name, options)
|
|
80
|
+
size = icon_size(options)
|
|
81
|
+
tag = tag.gsub(/\s+(width|height)="[^"]*"/, "")
|
|
82
|
+
tag = ensure_attr(tag, "class", icon_classes(icon_name, options))
|
|
83
|
+
tag = merge_style(tag, "width:#{size};height:#{size}")
|
|
84
|
+
tag = normalize_color(tag)
|
|
85
|
+
tag = ensure_attr(tag, "data-icon-pack", pack_name)
|
|
86
|
+
ensure_attr(tag, "role", "img")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def normalize_custom_colors(svg)
|
|
90
|
+
svg.gsub(/(\s)(fill|stroke)=(['"])(.*?)\3/) do
|
|
91
|
+
space, name, quote, value = Regexp.last_match.captures
|
|
92
|
+
%(#{space}#{name}=#{quote}#{preserve_color?(value) ? value : 'currentColor'}#{quote})
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def preserve_color?(value)
|
|
97
|
+
value == "none" || value == "currentColor" || value.start_with?("url(")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def inject_title(svg, title)
|
|
101
|
+
svg.sub(/<svg[^>]*>/) { |tag| "#{tag}<title>#{CGI.escapeHTML(title)}</title>" }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# :stroke packs (lucide) already carry stroke="currentColor";
|
|
105
|
+
# :fill packs (simple-icons) get fill on the root so paths inherit;
|
|
106
|
+
# :auto (custom) only injects fill when the SVG declares neither.
|
|
107
|
+
def normalize_color(tag)
|
|
108
|
+
case self.class::COLOR_MODEL
|
|
109
|
+
when :stroke then ensure_attr(tag, "stroke", "currentColor")
|
|
110
|
+
when :fill then ensure_attr(tag, "fill", "currentColor")
|
|
111
|
+
else tag =~ /\s(fill|stroke)=/ ? tag : tag.sub("<svg", '<svg fill="currentColor"')
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def icon_classes(icon_name, options)
|
|
116
|
+
CGI.escapeHTML("icon icon-#{icon_name} #{options['class']}".strip)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def icon_size(options)
|
|
120
|
+
raw = (options["size"] || "m").to_s.strip
|
|
121
|
+
size = SIZES.fetch(raw.downcase, raw)
|
|
122
|
+
raise Error, "invalid icon size '#{options['size']}'" unless size.match?(CSS_SIZE)
|
|
123
|
+
|
|
124
|
+
size
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def ensure_attr(tag, attr, value)
|
|
128
|
+
if tag =~ /\s#{attr}="([^"]*)"/
|
|
129
|
+
attr == "class" ? tag.sub(/class="([^"]*)"/, %(class="\\1 #{value}")) : tag
|
|
130
|
+
else
|
|
131
|
+
tag.sub("<svg", %(<svg #{attr}="#{value}"))
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def merge_style(tag, style)
|
|
136
|
+
if tag =~ /\sstyle="([^"]*)"/
|
|
137
|
+
tag.sub(/style="([^"]*)"/, %(style="\\1;#{style}"))
|
|
138
|
+
else
|
|
139
|
+
tag.sub("<svg", %(<svg style="#{style}"))
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module IconFlow
|
|
5
|
+
module Adapters
|
|
6
|
+
# Custom — arbitrary SVGs provided by the site in
|
|
7
|
+
# icon_flow.custom_dir (default "assets/icons/custom"). This is
|
|
8
|
+
# the adapter for sources without a bundleable package, e.g.
|
|
9
|
+
# downloads from https://www.svgrepo.com/. It also serves named
|
|
10
|
+
# extra packs declared in icon_flow.packs (name → site-relative
|
|
11
|
+
# directory), so a downloaded library like Font Awesome or Tabler
|
|
12
|
+
# gets a pack without gem code.
|
|
13
|
+
class Custom < Adapter
|
|
14
|
+
COLOR_MODEL = :auto
|
|
15
|
+
|
|
16
|
+
attr_reader :pack_name
|
|
17
|
+
|
|
18
|
+
def initialize(site = nil, pack_name: "custom", dir: nil)
|
|
19
|
+
super(site)
|
|
20
|
+
@pack_name = pack_name
|
|
21
|
+
@dir = dir
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def path_for(icon_name)
|
|
25
|
+
path = File.join(icons_dir, "#{icon_name}.svg")
|
|
26
|
+
File.file?(path) ? path : nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def icons_dir
|
|
32
|
+
dir = @dir || @site&.config&.dig("icon_flow", "custom_dir") || "assets/icons/custom"
|
|
33
|
+
File.expand_path(dir, @site&.source || Dir.pwd)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module IconFlow
|
|
5
|
+
module Adapters
|
|
6
|
+
# Lucide (ISC license) — stroke-based UI icons vendored in
|
|
7
|
+
# assets/icons/lucide/. https://lucide.dev
|
|
8
|
+
class Lucide < Adapter
|
|
9
|
+
COLOR_MODEL = :stroke
|
|
10
|
+
|
|
11
|
+
def pack_name
|
|
12
|
+
"lucide"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def path_for(icon_name)
|
|
16
|
+
path = File.join(icons_dir, "#{icon_name}.svg")
|
|
17
|
+
File.file?(path) ? path : nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def icons_dir
|
|
23
|
+
File.expand_path("../../../../assets/icons/lucide", __dir__)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module IconFlow
|
|
5
|
+
module Adapters
|
|
6
|
+
# simple-icons (CC0) — brand/logo icons vendored in
|
|
7
|
+
# assets/icons/simple/. https://simpleicons.org
|
|
8
|
+
class Simple < Adapter
|
|
9
|
+
COLOR_MODEL = :fill
|
|
10
|
+
|
|
11
|
+
def pack_name
|
|
12
|
+
"simple"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def path_for(icon_name)
|
|
16
|
+
path = File.join(icons_dir, "#{icon_name}.svg")
|
|
17
|
+
File.file?(path) ? path : nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def icons_dir
|
|
23
|
+
File.expand_path("../../../../assets/icons/simple", __dir__)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module IconFlow
|
|
5
|
+
# {% icon_ref <key> %} — semantic icon names resolved through the
|
|
6
|
+
# icon_flow.registry config map (the central-registry pattern):
|
|
7
|
+
# call sites name intent ("download", "delete"), the map picks the
|
|
8
|
+
# library icon. Swapping packs means editing one map, not grepping
|
|
9
|
+
# templates.
|
|
10
|
+
#
|
|
11
|
+
# icon_flow:
|
|
12
|
+
# registry:
|
|
13
|
+
# download: lucide:arrow-down
|
|
14
|
+
# github: simple:github
|
|
15
|
+
# danger: lucide:triangle-alert:has-text-danger # 3rd field = class
|
|
16
|
+
#
|
|
17
|
+
# {% icon_ref download %}
|
|
18
|
+
# {% icon_ref danger size:1.5em %}
|
|
19
|
+
class IconRefTag < IconTag
|
|
20
|
+
def render(context)
|
|
21
|
+
site = context.registers[:site]
|
|
22
|
+
return "" if site&.config&.dig("icon_flow", "enabled") == false
|
|
23
|
+
|
|
24
|
+
render_entry(context, site)
|
|
25
|
+
rescue Error => e
|
|
26
|
+
missing!(site, e)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def render_entry(context, site)
|
|
32
|
+
key, params = name_and_params(context)
|
|
33
|
+
pack, name, extra_class = registry_entry(site, key)
|
|
34
|
+
params["class"] = [extra_class, params["class"]].compact.join(" ").strip
|
|
35
|
+
adapter_instance(site, pack).render(name, params)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def registry_entry(site, key)
|
|
39
|
+
entry = site&.config&.dig("icon_flow", "registry", key)
|
|
40
|
+
raise Error, "no icon_flow.registry entry for '#{key}'" unless entry
|
|
41
|
+
|
|
42
|
+
entry.to_s.split(":", 3)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
|
|
5
|
+
module Jekyll
|
|
6
|
+
module IconFlow
|
|
7
|
+
# {% icon %} and per-pack {% icon_<pack> %} tags.
|
|
8
|
+
#
|
|
9
|
+
# {% icon search %} default pack (icon_flow.pack)
|
|
10
|
+
# {% icon include.name pack: include["pack"] %} variables resolve from context
|
|
11
|
+
# {% icon_lucide "file-text" size:1.5em class:"has-text-link" %}
|
|
12
|
+
# {% icon_simple github %}
|
|
13
|
+
# {% icon_custom logo %}
|
|
14
|
+
#
|
|
15
|
+
# Aliases matching the fleet convention (jekyll-lucide): lucide_icon,
|
|
16
|
+
# simple_icon, custom_icon.
|
|
17
|
+
#
|
|
18
|
+
# Params: size (any CSS size, default 1em), class (merged onto the svg),
|
|
19
|
+
# title (accessible label, becomes an svg <title>). The generic {% icon %}
|
|
20
|
+
# also accepts pack: to pick an adapter explicitly; bound tags ignore it.
|
|
21
|
+
class IconTag < Liquid::Tag
|
|
22
|
+
NAME_TOKEN = /\A\s*("[^"]*"|'[^']*'|[^\s]+)/
|
|
23
|
+
# A bare token that starts with a letter/underscore and contains a
|
|
24
|
+
# dot or bracket is a variable path (include.pack, page.icon[0]).
|
|
25
|
+
VAR_PATH = /\A[A-Za-z_]\S*[.\[]/
|
|
26
|
+
|
|
27
|
+
def self.for(pack_key)
|
|
28
|
+
Class.new(self) do
|
|
29
|
+
define_method(:bound_pack) { pack_key }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The name check lives in render, not initialize: Liquid still parses
|
|
34
|
+
# tags inside {% comment %} blocks, so a documented example like
|
|
35
|
+
# "{% icon %}" must parse cleanly even though it never renders.
|
|
36
|
+
def initialize(tag_name, markup, options)
|
|
37
|
+
super
|
|
38
|
+
@name_token = markup[NAME_TOKEN, 1]
|
|
39
|
+
@params_markup = markup[NAME_TOKEN] ? markup.delete_prefix(markup[NAME_TOKEN]) : ""
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render(context)
|
|
43
|
+
site = context.registers[:site]
|
|
44
|
+
return "" if site&.config&.dig("icon_flow", "enabled") == false
|
|
45
|
+
|
|
46
|
+
name, params = name_and_params(context)
|
|
47
|
+
render_icon(site, name, params)
|
|
48
|
+
rescue Error => e
|
|
49
|
+
missing!(site, e)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def name_and_params(context)
|
|
55
|
+
raise Error, "icon tag requires a name" unless @name_token
|
|
56
|
+
|
|
57
|
+
[resolve(@name_token, context), parse_params(context)]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Missing icons are untrusted user input — report via the host logger
|
|
61
|
+
# and render empty by default (icon_flow.on_missing: warn). Set
|
|
62
|
+
# on_missing: strict to fail the build instead.
|
|
63
|
+
def missing!(site, error)
|
|
64
|
+
raise error if site&.config&.dig("icon_flow", "on_missing") == "strict"
|
|
65
|
+
|
|
66
|
+
Jekyll.logger.warn("icon_flow:", error.message)
|
|
67
|
+
""
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def parse_params(context)
|
|
71
|
+
scanner = StringScanner.new(@params_markup)
|
|
72
|
+
params = {}
|
|
73
|
+
until scanner.eos?
|
|
74
|
+
key, value = next_param(scanner)
|
|
75
|
+
next unless key && value
|
|
76
|
+
|
|
77
|
+
resolved = resolve(value, context, allow_nil: true)
|
|
78
|
+
params[key.delete_suffix(":")] = resolved unless resolved.nil?
|
|
79
|
+
end
|
|
80
|
+
params
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def next_param(scanner)
|
|
84
|
+
scanner.skip(/\s+/)
|
|
85
|
+
key = scanner.scan(/\w+:/)
|
|
86
|
+
unless key
|
|
87
|
+
scanner.pos += 1 unless scanner.eos?
|
|
88
|
+
return nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
scanner.skip(/\s*/)
|
|
92
|
+
[key, scanner.scan(/"[^"]*"|'[^']*'|\S+/)]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Quoted tokens are literals; bare tokens resolve through the Liquid
|
|
96
|
+
# context first (so include.name works) and fall back to the literal.
|
|
97
|
+
# Unresolved variable paths in params are treated as absent so a
|
|
98
|
+
# wrapper include can forward optional params unconditionally —
|
|
99
|
+
# literal values (icon names, "1.5em") never match VAR_PATH.
|
|
100
|
+
def resolve(token, context, allow_nil: false)
|
|
101
|
+
return token[1..-2] if token.start_with?('"', "'")
|
|
102
|
+
|
|
103
|
+
value = context[token]
|
|
104
|
+
return nil if allow_nil && value.nil? && token.match?(VAR_PATH)
|
|
105
|
+
|
|
106
|
+
value || token
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# The unbound {% icon %} tag searches packs in order and renders the
|
|
110
|
+
# first hit — custom_dir and named icon_flow.packs dirs, then the
|
|
111
|
+
# bundled packs (default chain: custom → simple → lucide, so a brand
|
|
112
|
+
# name like "github" just works). pack: or icon_flow.pack pin the
|
|
113
|
+
# lookup to a single pack; bound {% icon_<pack> %} tags always use
|
|
114
|
+
# their own pack only.
|
|
115
|
+
def render_icon(site, name, params)
|
|
116
|
+
keys = candidate_keys(site, params)
|
|
117
|
+
adapters = keys.map { |key| adapter_instance(site, key) }
|
|
118
|
+
hit = adapters.find { |adapter| adapter.path_for(name) }
|
|
119
|
+
return hit.render(name, params) if hit
|
|
120
|
+
|
|
121
|
+
plural = keys.size == 1 ? "pack" : "packs"
|
|
122
|
+
raise Error, "icon '#{name}' not found in #{plural} '#{keys.join(', ')}'"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def candidate_keys(site, params)
|
|
126
|
+
return [bound_pack] if bound_pack
|
|
127
|
+
return [params.delete("pack").to_s] if params["pack"]
|
|
128
|
+
|
|
129
|
+
pack = site&.config&.dig("icon_flow", "pack")
|
|
130
|
+
pack ? [pack.to_s] : search_keys(site)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def search_keys(site)
|
|
134
|
+
search = site&.config&.dig("icon_flow", "search")
|
|
135
|
+
search = %w[custom simple lucide] if search.nil? || search.empty?
|
|
136
|
+
Array(search).map(&:to_s)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Built-in packs instantiate their own class; any other name resolves
|
|
140
|
+
# through icon_flow.packs (name → site-relative dir of *.svg files).
|
|
141
|
+
def adapter_instance(site, key)
|
|
142
|
+
klass = ADAPTERS[key]
|
|
143
|
+
return klass.new(site) if klass
|
|
144
|
+
|
|
145
|
+
dir = site&.config&.dig("icon_flow", "packs", key)
|
|
146
|
+
return Adapters::Custom.new(site, pack_name: key, dir: dir) if dir
|
|
147
|
+
|
|
148
|
+
raise Error, "unknown icon pack '#{key}'"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def bound_pack
|
|
152
|
+
nil # overridden by .for subclasses
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module IconFlow
|
|
5
|
+
# Reads an SVG file as UTF-8 text. Custom and named-pack SVGs are
|
|
6
|
+
# user-supplied bytes of unknown provenance: honour an XML encoding
|
|
7
|
+
# declaration when present, keep UTF-8 when the bytes validate, and
|
|
8
|
+
# fall back to Latin-1 — so a stray-encoded icon degrades instead of
|
|
9
|
+
# breaking the build with an ArgumentError deep in normalize.
|
|
10
|
+
module SvgReader
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def read(path)
|
|
14
|
+
bytes = File.binread(path)
|
|
15
|
+
bytes.force_encoding(declared_encoding(bytes) || Encoding::UTF_8)
|
|
16
|
+
bytes.force_encoding(Encoding::ISO_8859_1) unless bytes.valid_encoding?
|
|
17
|
+
bytes.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
|
|
18
|
+
.delete_prefix("\uFEFF")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def declared_encoding(bytes)
|
|
22
|
+
name = bytes[/\A\s*<\?xml[^?]*\bencoding\s*=\s*["']([^"']+)/i, 1]
|
|
23
|
+
name && Encoding.find(name)
|
|
24
|
+
rescue ArgumentError
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|