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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +166 -0
  5. data/assets/icons/lucide/arrow-down.svg +16 -0
  6. data/assets/icons/lucide/arrow-left.svg +16 -0
  7. data/assets/icons/lucide/arrow-right.svg +16 -0
  8. data/assets/icons/lucide/arrow-up.svg +16 -0
  9. data/assets/icons/lucide/calendar.svg +18 -0
  10. data/assets/icons/lucide/check.svg +15 -0
  11. data/assets/icons/lucide/chevron-down.svg +15 -0
  12. data/assets/icons/lucide/chevron-left.svg +15 -0
  13. data/assets/icons/lucide/chevron-right.svg +15 -0
  14. data/assets/icons/lucide/chevron-up.svg +15 -0
  15. data/assets/icons/lucide/copy.svg +16 -0
  16. data/assets/icons/lucide/download.svg +17 -0
  17. data/assets/icons/lucide/external-link.svg +17 -0
  18. data/assets/icons/lucide/file-text.svg +19 -0
  19. data/assets/icons/lucide/folder.svg +15 -0
  20. data/assets/icons/lucide/home.svg +16 -0
  21. data/assets/icons/lucide/info.svg +17 -0
  22. data/assets/icons/lucide/link.svg +16 -0
  23. data/assets/icons/lucide/mail.svg +16 -0
  24. data/assets/icons/lucide/map-pin.svg +16 -0
  25. data/assets/icons/lucide/map.svg +17 -0
  26. data/assets/icons/lucide/menu.svg +17 -0
  27. data/assets/icons/lucide/moon.svg +15 -0
  28. data/assets/icons/lucide/printer.svg +17 -0
  29. data/assets/icons/lucide/rss.svg +17 -0
  30. data/assets/icons/lucide/search.svg +16 -0
  31. data/assets/icons/lucide/sun.svg +23 -0
  32. data/assets/icons/lucide/tag.svg +16 -0
  33. data/assets/icons/lucide/triangle-alert.svg +17 -0
  34. data/assets/icons/lucide/x.svg +16 -0
  35. data/assets/icons/simple/bluesky.svg +1 -0
  36. data/assets/icons/simple/discord.svg +1 -0
  37. data/assets/icons/simple/facebook.svg +1 -0
  38. data/assets/icons/simple/github.svg +1 -0
  39. data/assets/icons/simple/instagram.svg +1 -0
  40. data/assets/icons/simple/mastodon.svg +1 -0
  41. data/assets/icons/simple/medium.svg +1 -0
  42. data/assets/icons/simple/rss.svg +1 -0
  43. data/assets/icons/simple/spotify.svg +1 -0
  44. data/assets/icons/simple/telegram.svg +1 -0
  45. data/assets/icons/simple/tiktok.svg +1 -0
  46. data/assets/icons/simple/x.svg +1 -0
  47. data/assets/icons/simple/youtube.svg +1 -0
  48. data/lib/jekyll/icon_flow/adapter.rb +144 -0
  49. data/lib/jekyll/icon_flow/adapters/custom.rb +38 -0
  50. data/lib/jekyll/icon_flow/adapters/lucide.rb +28 -0
  51. data/lib/jekyll/icon_flow/adapters/simple.rb +28 -0
  52. data/lib/jekyll/icon_flow/error.rb +7 -0
  53. data/lib/jekyll/icon_flow/icon_ref_tag.rb +46 -0
  54. data/lib/jekyll/icon_flow/icon_tag.rb +156 -0
  55. data/lib/jekyll/icon_flow/svg_reader.rb +29 -0
  56. data/lib/jekyll/icon_flow/version.rb +7 -0
  57. data/lib/jekyll/icon_flow.rb +36 -0
  58. data/lib/jekyll-icon-flow.rb +3 -0
  59. 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module IconFlow
5
+ class Error < StandardError; end
6
+ end
7
+ 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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module IconFlow
5
+ VERSION = "0.1.0"
6
+ end
7
+ end