docyard 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -1
  3. data/LICENSE.vscode-icons +42 -0
  4. data/README.md +46 -5
  5. data/lib/docyard/asset_handler.rb +33 -0
  6. data/lib/docyard/components/base_processor.rb +24 -0
  7. data/lib/docyard/components/callout_processor.rb +121 -0
  8. data/lib/docyard/components/code_block_processor.rb +55 -0
  9. data/lib/docyard/components/code_detector.rb +59 -0
  10. data/lib/docyard/components/icon_detector.rb +57 -0
  11. data/lib/docyard/components/icon_processor.rb +51 -0
  12. data/lib/docyard/components/registry.rb +34 -0
  13. data/lib/docyard/components/tabs_parser.rb +60 -0
  14. data/lib/docyard/components/tabs_processor.rb +44 -0
  15. data/lib/docyard/config/validator.rb +171 -0
  16. data/lib/docyard/config.rb +133 -0
  17. data/lib/docyard/constants.rb +5 -0
  18. data/lib/docyard/icons/LICENSE.phosphor +21 -0
  19. data/lib/docyard/icons/file_types.rb +92 -0
  20. data/lib/docyard/icons/phosphor.rb +63 -0
  21. data/lib/docyard/icons.rb +40 -0
  22. data/lib/docyard/initializer.rb +20 -2
  23. data/lib/docyard/language_mapping.rb +52 -0
  24. data/lib/docyard/markdown.rb +14 -3
  25. data/lib/docyard/rack_application.rb +76 -7
  26. data/lib/docyard/renderer.rb +40 -7
  27. data/lib/docyard/server.rb +5 -2
  28. data/lib/docyard/sidebar_builder.rb +10 -2
  29. data/lib/docyard/templates/assets/css/code.css +150 -2
  30. data/lib/docyard/templates/assets/css/components/callout.css +169 -0
  31. data/lib/docyard/templates/assets/css/components/code-block.css +196 -0
  32. data/lib/docyard/templates/assets/css/components/icon.css +16 -0
  33. data/lib/docyard/templates/assets/css/components/logo.css +44 -0
  34. data/lib/docyard/templates/assets/css/{components.css → components/navigation.css} +47 -47
  35. data/lib/docyard/templates/assets/css/components/tabs.css +298 -0
  36. data/lib/docyard/templates/assets/css/components/theme-toggle.css +61 -0
  37. data/lib/docyard/templates/assets/css/layout.css +14 -4
  38. data/lib/docyard/templates/assets/css/markdown.css +9 -8
  39. data/lib/docyard/templates/assets/css/reset.css +4 -0
  40. data/lib/docyard/templates/assets/css/variables.css +94 -3
  41. data/lib/docyard/templates/assets/favicon.svg +16 -0
  42. data/lib/docyard/templates/assets/js/components/code-block.js +162 -0
  43. data/lib/docyard/templates/assets/js/components/tabs.js +338 -0
  44. data/lib/docyard/templates/assets/js/theme.js +16 -0
  45. data/lib/docyard/templates/assets/logo-dark.svg +4 -0
  46. data/lib/docyard/templates/assets/logo.svg +12 -0
  47. data/lib/docyard/templates/config/docyard.yml.erb +20 -0
  48. data/lib/docyard/templates/layouts/default.html.erb +31 -3
  49. data/lib/docyard/templates/markdown/components/callouts.md.erb +204 -0
  50. data/lib/docyard/templates/markdown/components/icons.md.erb +125 -0
  51. data/lib/docyard/templates/markdown/components/tabs.md.erb +686 -0
  52. data/lib/docyard/templates/markdown/configuration.md.erb +202 -0
  53. data/lib/docyard/templates/partials/_callout.html.erb +11 -0
  54. data/lib/docyard/templates/partials/_code_block.html.erb +6 -0
  55. data/lib/docyard/templates/partials/_icon.html.erb +1 -0
  56. data/lib/docyard/templates/partials/_icon_file_extension.html.erb +1 -0
  57. data/lib/docyard/templates/partials/_tabs.html.erb +40 -0
  58. data/lib/docyard/templates/partials/_theme_toggle.html.erb +13 -0
  59. data/lib/docyard/version.rb +1 -1
  60. metadata +41 -2
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Layout/LineLength
4
+ module Docyard
5
+ module Icons
6
+ # Phosphor Icons - https://phosphoricons.com
7
+ # Copyright (c) 2020-2023 Phosphor Icons
8
+ # Licensed under MIT License (see LICENSE.phosphor)
9
+ PHOSPHOR = {
10
+ "regular" => {
11
+ "heart" => '<path d="M178,40c-20.65,0-38.73,8.88-50,23.89C116.73,48.88,98.65,40,78,40a62.07,62.07,0,0,0-62,62c0,70,103.79,126.66,108.21,129a8,8,0,0,0,7.58,0C136.21,228.66,240,172,240,102A62.07,62.07,0,0,0,178,40ZM128,214.8C109.74,204.16,32,155.69,32,102A46.06,46.06,0,0,1,78,56c19.45,0,35.78,10.36,42.6,27a8,8,0,0,0,14.8,0c6.82-16.67,23.15-27,42.6-27a46.06,46.06,0,0,1,46,46C224,155.61,146.24,204.15,128,214.8Z"/>',
12
+ "arrow-right" => '<path d="M221.66,133.66l-72,72a8,8,0,0,1-11.32-11.32L196.69,136H40a8,8,0,0,1,0-16H196.69L138.34,61.66a8,8,0,0,1,11.32-11.32l72,72A8,8,0,0,1,221.66,133.66Z"/>',
13
+ "arrow-left" => '<path d="M224,128a8,8,0,0,1-8,8H59.31l58.35,58.34a8,8,0,0,1-11.32,11.32l-72-72a8,8,0,0,1,0-11.32l72-72a8,8,0,0,1,11.32,11.32L59.31,120H216A8,8,0,0,1,224,128Z"/>',
14
+ "arrow-up" => '<path d="M205.66,117.66a8,8,0,0,1-11.32,0L136,59.31V216a8,8,0,0,1-16,0V59.31L61.66,117.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0l72,72A8,8,0,0,1,205.66,117.66Z"/>',
15
+ "arrow-down" => '<path d="M205.66,149.66l-72,72a8,8,0,0,1-11.32,0l-72-72a8,8,0,0,1,11.32-11.32L120,196.69V40a8,8,0,0,1,16,0V196.69l58.34-58.35a8,8,0,0,1,11.32,11.32Z"/>',
16
+ "check" => '<path d="M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z"/>',
17
+ "x" => '<path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/>',
18
+ "warning" => '<path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"/>',
19
+ "info" => '<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"/>',
20
+ "code" => '<path d="M69.12,94.15,28.5,128l40.62,33.85a8,8,0,1,1-10.24,12.29l-48-40a8,8,0,0,1,0-12.29l48-40a8,8,0,0,1,10.24,12.3Zm176,27.7-48-40a8,8,0,1,0-10.24,12.3L227.5,128l-40.62,33.85a8,8,0,1,0,10.24,12.29l48-40a8,8,0,0,0,0-12.29ZM162.73,32.48a8,8,0,0,0-10.25,4.79l-64,176a8,8,0,0,0,4.79,10.26A8.14,8.14,0,0,0,96,224a8,8,0,0,0,7.52-5.27l64-176A8,8,0,0,0,162.73,32.48Z"/>',
21
+ "terminal" => '<path d="M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,160H40V56H216V200ZM117.31,134.17,93.65,152l23.66,17.82a8,8,0,1,1-9.63,12.79l-32-24a8,8,0,0,1,0-12.8l32-24a8,8,0,0,1,9.63,12.79ZM160,168H136a8,8,0,0,1,0-16h24a8,8,0,0,1,0,16Z"/>',
22
+ "package" => '<path d="M223.68,66.15,135.68,18a15.88,15.88,0,0,0-15.36,0l-88,48.17a16,16,0,0,0-8.32,14v95.64a16,16,0,0,0,8.32,14l88,48.17a15.88,15.88,0,0,0,15.36,0l88-48.17a16,16,0,0,0,8.32-14V80.18A16,16,0,0,0,223.68,66.15ZM128,32l80.34,44-29.77,16.3-80.35-44ZM128,120,47.66,76l33.9-18.56,80.34,44ZM40,90l80,43.78v85.79L40,175.82Zm176,85.78h0l-80,43.79V133.82l32-17.51V152a8,8,0,0,0,16,0V107.55L216,90v85.77Z"/>',
23
+ "rocket-launch" => '<path d="M223.85,47.12a16,16,0,0,0-15-15c-12.58-.75-44.73.4-71.41,27.07L132.69,64H74.36A15.91,15.91,0,0,0,63,68.68L28.7,103a16,16,0,0,0,9.07,27.16l38.47,5.37,44.21,44.21,5.37,38.49a15.94,15.94,0,0,0,10.78,12.92,16.11,16.11,0,0,0,5.1.83A15.91,15.91,0,0,0,153,227.3L187.32,193A15.91,15.91,0,0,0,192,181.64V123.31l4.77-4.77C223.45,91.86,224.6,59.71,223.85,47.12ZM74.36,80h42.33L77.16,119.52,40,114.34Zm74.41-9.45a76.65,76.65,0,0,1,59.11-22.47,76.46,76.46,0,0,1-22.42,59.16L128,164.68,91.32,128ZM176,181.64,141.67,216l-5.19-37.17L176,139.31Zm-74.16,9.5C97.34,201,82.29,224,40,224a8,8,0,0,1-8-8c0-42.29,23-57.34,32.86-61.85a8,8,0,0,1,6.64,14.56c-6.43,2.93-20.62,12.36-23.12,38.91,26.55-2.5,36-16.69,38.91-23.12a8,8,0,1,1,14.56,6.64Z"/>',
24
+ "star" => '<path d="M239.18,97.26A16.38,16.38,0,0,0,224.92,86l-59-4.76L143.14,26.15a16.36,16.36,0,0,0-30.27,0L90.11,81.23,31.08,86a16.46,16.46,0,0,0-9.37,28.86l45,38.83L53,211.75a16.38,16.38,0,0,0,24.5,17.82L128,198.49l50.53,31.08A16.4,16.4,0,0,0,203,211.75l-13.76-58.07,45-38.83A16.43,16.43,0,0,0,239.18,97.26Zm-15.34,5.47-48.7,42a8,8,0,0,0-2.56,7.91l14.88,62.8a.37.37,0,0,1-.17.48c-.18.14-.23.11-.38,0l-54.72-33.65a8,8,0,0,0-8.38,0L69.09,215.94c-.15.09-.19.12-.38,0a.37.37,0,0,1-.17-.48l14.88-62.8a8,8,0,0,0-2.56-7.91l-48.7-42c-.12-.1-.23-.19-.13-.5s.18-.27.33-.29l63.92-5.16A8,8,0,0,0,103,91.86l24.62-59.61c.08-.17.11-.25.35-.25s.27.08.35.25L153,91.86a8,8,0,0,0,6.75,4.92l63.92,5.16c.15,0,.24,0,.33.29S224,102.63,223.84,102.73Z"/>',
25
+ "lightning" => '<path d="M215.79,118.17a8,8,0,0,0-5-5.66L153.18,90.9l14.66-73.33a8,8,0,0,0-13.69-7l-112,120a8,8,0,0,0,3,13l57.63,21.61L88.16,238.43a8,8,0,0,0,13.69,7l112-120A8,8,0,0,0,215.79,118.17ZM109.37,214l10.47-52.38a8,8,0,0,0-5-9.06L62,132.71l84.62-90.66L136.16,94.43a8,8,0,0,0,5,9.06l52.8,19.8Z"/>',
26
+ "moon-stars" => '<path d="M240,96a8,8,0,0,1-8,8H216v16a8,8,0,0,1-16,0V104H184a8,8,0,0,1,0-16h16V72a8,8,0,0,1,16,0V88h16A8,8,0,0,1,240,96ZM144,56h8v8a8,8,0,0,0,16,0V56h8a8,8,0,0,0,0-16h-8V32a8,8,0,0,0-16,0v8h-8a8,8,0,0,0,0,16Zm72.77,97a8,8,0,0,1,1.43,8A96,96,0,1,1,95.07,37.8a8,8,0,0,1,10.6,9.06A88.07,88.07,0,0,0,209.14,150.33,8,8,0,0,1,216.77,153Zm-19.39,14.88c-1.79.09-3.59.14-5.38.14A104.11,104.11,0,0,1,88,64c0-1.79,0-3.59.14-5.38A80,80,0,1,0,197.38,167.86Z"/>',
27
+ "sun" => '<path d="M120,40V16a8,8,0,0,1,16,0V40a8,8,0,0,1-16,0Zm72,88a64,64,0,1,1-64-64A64.07,64.07,0,0,1,192,128Zm-16,0a48,48,0,1,0-48,48A48.05,48.05,0,0,0,176,128ZM58.34,69.66A8,8,0,0,0,69.66,58.34l-16-16A8,8,0,0,0,42.34,53.66Zm0,116.68-16,16a8,8,0,0,0,11.32,11.32l16-16a8,8,0,0,0-11.32-11.32ZM192,72a8,8,0,0,0,5.66-2.34l16-16a8,8,0,0,0-11.32-11.32l-16,16A8,8,0,0,0,192,72Zm5.66,114.34a8,8,0,0,0-11.32,11.32l16,16a8,8,0,0,0,11.32-11.32ZM48,128a8,8,0,0,0-8-8H16a8,8,0,0,0,0,16H40A8,8,0,0,0,48,128Zm80,80a8,8,0,0,0-8,8v24a8,8,0,0,0,16,0V216A8,8,0,0,0,128,208Zm112-88H216a8,8,0,0,0,0,16h24a8,8,0,0,0,0-16Z"/>',
28
+ "link-external" => '<path d="M224,104a8,8,0,0,1-16,0V59.32l-66.33,66.34a8,8,0,0,1-11.32-11.32L196.68,48H152a8,8,0,0,1,0-16h64a8,8,0,0,1,8,8Zm-40,24a8,8,0,0,0-8,8v72H48V80h72a8,8,0,0,0,0-16H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V136A8,8,0,0,0,184,128Z"/>',
29
+ "copy" => '<path d="M216,32H88a8,8,0,0,0-8,8V80H40a8,8,0,0,0-8,8V216a8,8,0,0,0,8,8H168a8,8,0,0,0,8-8V176h40a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32ZM160,208H48V96H160Zm48-48H176V88a8,8,0,0,0-8-8H96V48H208Z"/>',
30
+ "github" => '<path d="M208.31,75.68A59.78,59.78,0,0,0,202.93,28,8,8,0,0,0,196,24a59.75,59.75,0,0,0-48,24H124A59.75,59.75,0,0,0,76,24a8,8,0,0,0-6.93,4,59.78,59.78,0,0,0-5.38,47.68A58.14,58.14,0,0,0,56,104v8a56.06,56.06,0,0,0,48.44,55.47A39.8,39.8,0,0,0,96,192v8H72a24,24,0,0,1-24-24A40,40,0,0,0,8,136a8,8,0,0,0,0,16,24,24,0,0,1,24,24,40,40,0,0,0,40,40H96v16a8,8,0,0,0,16,0V192a24,24,0,0,1,48,0v40a8,8,0,0,0,16,0V192a39.8,39.8,0,0,0-8.44-24.53A56.06,56.06,0,0,0,216,112v-8A58.14,58.14,0,0,0,208.31,75.68ZM200,112a40,40,0,0,1-40,40H112a40,40,0,0,1-40-40v-8a41.74,41.74,0,0,1,6.9-22.48,8,8,0,0,0,1.1-7.69,43.81,43.81,0,0,1,.79-33.58,43.88,43.88,0,0,1,32.32,20.06,8,8,0,0,0,6.71,3.69h32.35a8,8,0,0,0,6.74-3.69,43.87,43.87,0,0,1,32.32-20.06,43.81,43.81,0,0,1,.77,33.58,8.09,8.09,0,0,0,1,7.65,41.76,41.76,0,0,1,7,22.52Z"/>',
31
+ "question" => '<path d="M140,180a12,12,0,1,1-12-12A12,12,0,0,1,140,180ZM128,72c-22.06,0-40,16.15-40,36v4a8,8,0,0,0,16,0v-4c0-11,10.77-20,24-20s24,9,24,20-10.77,20-24,20a8,8,0,0,0-8,8v8a8,8,0,0,0,16,0v-.72c18.24-3.35,32-17.9,32-35.28C168,88.15,150.06,72,128,72Zm104,56A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128Z"/>',
32
+ "lightbulb" => '<path d="M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z"/>',
33
+ "warning-circle" => '<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"/>',
34
+ "warning-octagon" => '<path d="M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z"/>',
35
+ "siren" => '<path d="M120,16V8a8,8,0,0,1,16,0v8a8,8,0,0,1-16,0Zm80,32a8,8,0,0,0,5.66-2.34l8-8a8,8,0,0,0-11.32-11.32l-8,8A8,8,0,0,0,200,48ZM50.34,45.66A8,8,0,0,0,61.66,34.34l-8-8A8,8,0,0,0,42.34,37.66Zm87,26.45a8,8,0,1,0-2.64,15.78C153.67,91.08,168,108.32,168,128a8,8,0,0,0,16,0C184,100.6,163.93,76.57,137.32,72.11ZM232,176v24a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V176a16,16,0,0,1,16-16V128a88,88,0,0,1,88.67-88c48.15.36,87.33,40.29,87.33,89v31A16,16,0,0,1,232,176ZM56,160H200V129c0-40-32.05-72.71-71.45-73H128a72,72,0,0,0-72,72Zm160,40V176H40v24H216Z"/>',
36
+ "file" => '<path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Z"/>',
37
+ "terminal-window" => '<path d="M128,128a8,8,0,0,1-3,6.25l-40,32a8,8,0,1,1-10-12.5L107.19,128,75,102.25a8,8,0,1,1,10-12.5l40,32A8,8,0,0,1,128,128Zm48,24H136a8,8,0,0,0,0,16h40a8,8,0,0,0,0-16Zm56-96V200a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V56A16,16,0,0,1,40,40H216A16,16,0,0,1,232,56ZM216,200V56H40V200H216Z"/>'
38
+ },
39
+ "bold" => {
40
+ "heart" => '<path d="M178,36c-20.09,0-37.92,7.93-50,21.56C115.92,43.93,98.09,36,78,36a66.08,66.08,0,0,0-66,66c0,72.34,105.81,130.14,110.31,132.57a12,12,0,0,0,11.38,0C138.19,232.14,244,174.34,244,102A66.08,66.08,0,0,0,178,36Zm-5.49,142.36A328.69,328.69,0,0,1,128,210.16a328.69,328.69,0,0,1-44.51-31.8C61.82,159.77,36,131.42,36,102A42,42,0,0,1,78,60c17.8,0,32.7,9.4,38.89,24.54a12,12,0,0,0,22.22,0C145.3,69.4,160.2,60,178,60a42,42,0,0,1,42,42C220,131.42,194.18,159.77,172.51,178.36Z"/>'
41
+ },
42
+ "fill" => {
43
+ "heart" => '<path d="M240,102c0,70-103.79,126.66-108.21,129a8,8,0,0,1-7.58,0C119.79,228.66,16,172,16,102A62.07,62.07,0,0,1,78,40c20.65,0,38.73,8.88,50,23.89C139.27,48.88,157.35,40,178,40A62.07,62.07,0,0,1,240,102Z"/>'
44
+ },
45
+ "light" => {
46
+ "heart" => '<path d="M178,42c-21,0-39.26,9.47-50,25.34C117.26,51.47,99,42,78,42a60.07,60.07,0,0,0-60,60c0,29.2,18.2,59.59,54.1,90.31a334.68,334.68,0,0,0,53.06,37,6,6,0,0,0,5.68,0,334.68,334.68,0,0,0,53.06-37C219.8,161.59,238,131.2,238,102A60.07,60.07,0,0,0,178,42ZM128,217.11C111.59,207.64,30,157.72,30,102A48.05,48.05,0,0,1,78,54c20.28,0,37.31,10.83,44.45,28.27a6,6,0,0,0,11.1,0C140.69,64.83,157.72,54,178,54a48.05,48.05,0,0,1,48,48C226,157.72,144.41,207.64,128,217.11Z"/>'
47
+ },
48
+ "thin" => {
49
+ "heart" => '<path d="M178,44c-21.44,0-39.92,10.19-50,27.07C117.92,54.19,99.44,44,78,44a58.07,58.07,0,0,0-58,58c0,28.59,18,58.47,53.4,88.79a333.81,333.81,0,0,0,52.7,36.73,4,4,0,0,0,3.8,0,333.81,333.81,0,0,0,52.7-36.73C218,160.47,236,130.59,236,102A58.07,58.07,0,0,0,178,44ZM128,219.42c-14-8-100-59.35-100-117.42A50.06,50.06,0,0,1,78,52c21.11,0,38.85,11.31,46.3,29.51a4,4,0,0,0,7.4,0C139.15,63.31,156.89,52,178,52a50.06,50.06,0,0,1,50,50C228,160,142,211.46,128,219.42Z"/>'
50
+ },
51
+ "duotone" => {
52
+ "heart" => '<path d="M232,102c0,66-104,122-104,122S24,168,24,102A54,54,0,0,1,78,48c22.59,0,41.94,12.31,50,32,8.06-19.69,27.41-32,50-32A54,54,0,0,1,232,102Z" opacity="0.2"/><path d="M178,40c-20.65,0-38.73,8.88-50,23.89C116.73,48.88,98.65,40,78,40a62.07,62.07,0,0,0-62,62c0,70,103.79,126.66,108.21,129a8,8,0,0,0,7.58,0C136.21,228.66,240,172,240,102A62.07,62.07,0,0,0,178,40ZM128,214.8C109.74,204.16,32,155.69,32,102A46.06,46.06,0,0,1,78,56c19.45,0,35.78,10.36,42.6,27a8,8,0,0,0,14.8,0c6.82-16.67,23.15-27,42.6-27a46.06,46.06,0,0,1,46,46C224,155.61,146.24,204.15,128,214.8Z"/>',
53
+ "info" => '<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"/>',
54
+ "warning" => '<path d="M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z"/>',
55
+ "lightbulb" => '<path d="M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z"/>',
56
+ "warning-circle" => '<path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"/>',
57
+ "warning-octagon" => '<path d="M120,136V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0ZM232,91.55v72.9a15.86,15.86,0,0,1-4.69,11.31l-51.55,51.55A15.86,15.86,0,0,1,164.45,232H91.55a15.86,15.86,0,0,1-11.31-4.69L28.69,175.76A15.86,15.86,0,0,1,24,164.45V91.55a15.86,15.86,0,0,1,4.69-11.31L80.24,28.69A15.86,15.86,0,0,1,91.55,24h72.9a15.86,15.86,0,0,1,11.31,4.69l51.55,51.55A15.86,15.86,0,0,1,232,91.55Zm-16,0L164.45,40H91.55L40,91.55v72.9L91.55,216h72.9L216,164.45ZM128,160a12,12,0,1,0,12,12A12,12,0,0,0,128,160Z"/>',
58
+ "siren" => '<path d="M120,16V8a8,8,0,0,1,16,0v8a8,8,0,0,1-16,0Zm80,32a8,8,0,0,0,5.66-2.34l8-8a8,8,0,0,0-11.32-11.32l-8,8A8,8,0,0,0,200,48ZM50.34,45.66A8,8,0,0,0,61.66,34.34l-8-8A8,8,0,0,0,42.34,37.66Zm87,26.45a8,8,0,1,0-2.64,15.78C153.67,91.08,168,108.32,168,128a8,8,0,0,0,16,0C184,100.6,163.93,76.57,137.32,72.11ZM232,176v24a16,16,0,0,1-16,16H40a16,16,0,0,1-16-16V176a16,16,0,0,1,16-16V128a88,88,0,0,1,88.67-88c48.15.36,87.33,40.29,87.33,89v31A16,16,0,0,1,232,176ZM56,160H200V129c0-40-32.05-72.71-71.45-73H128a72,72,0,0,0-72,72Zm160,40V176H40v24H216Z"/>'
59
+ }
60
+ }.freeze
61
+ end
62
+ end
63
+ # rubocop:enable Layout/LineLength
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "icons/phosphor"
4
+ require_relative "icons/file_types"
5
+ require_relative "renderer"
6
+
7
+ module Docyard
8
+ module Icons
9
+ LIBRARIES = {
10
+ phosphor: PHOSPHOR
11
+ }.freeze
12
+
13
+ def self.render(name, weight = "regular")
14
+ icon_data = LIBRARIES.dig(:phosphor, weight, name)
15
+ return nil unless icon_data
16
+
17
+ Renderer.new.render_partial(
18
+ "_icon", {
19
+ name: name,
20
+ icon_data: icon_data
21
+ }
22
+ )
23
+ end
24
+
25
+ def self.render_file_extension(extension)
26
+ svg_content = FileTypes.svg(extension)
27
+
28
+ if svg_content
29
+ Renderer.new.render_partial(
30
+ "_icon_file_extension", {
31
+ extension: extension,
32
+ svg_content: svg_content
33
+ }
34
+ )
35
+ else
36
+ render("file")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -6,6 +6,7 @@ module Docyard
6
6
  class Initializer
7
7
  DOCS_DIR = "docs"
8
8
  TEMPLATE_DIR = File.join(__dir__, "templates", "markdown")
9
+ CONFIG_TEMPLATE_DIR = File.join(__dir__, "templates", "config")
9
10
 
10
11
  TEMPLATES = {
11
12
  "index.md" => "index.md.erb",
@@ -13,7 +14,10 @@ module Docyard
13
14
  "getting-started/installation.md" => "getting-started/installation.md.erb",
14
15
  "getting-started/quick-start.md" => "getting-started/quick-start.md.erb",
15
16
  "core-concepts/file-structure.md" => "core-concepts/file-structure.md.erb",
16
- "core-concepts/markdown.md" => "core-concepts/markdown.md.erb"
17
+ "core-concepts/markdown.md" => "core-concepts/markdown.md.erb",
18
+ "components/callouts.md" => "components/callouts.md.erb",
19
+ "components/icons.md" => "components/icons.md.erb",
20
+ "components/tabs.md" => "components/tabs.md.erb"
17
21
  }.freeze
18
22
 
19
23
  def initialize(path = ".")
@@ -44,6 +48,8 @@ module Docyard
44
48
  TEMPLATES.each do |output_name, template_name|
45
49
  copy_template(template_name, output_name)
46
50
  end
51
+
52
+ create_example_config
47
53
  end
48
54
 
49
55
  def copy_template(template_name, output_name)
@@ -57,6 +63,16 @@ module Docyard
57
63
  File.write(output_path, content)
58
64
  end
59
65
 
66
+ def create_example_config
67
+ config_path = File.join(@path, "docyard.yml")
68
+ return if File.exist?(config_path)
69
+
70
+ template_path = File.join(CONFIG_TEMPLATE_DIR, "docyard.yml.erb")
71
+ config_content = File.read(template_path)
72
+
73
+ File.write(config_path, config_content)
74
+ end
75
+
60
76
  def print_already_exists_error
61
77
  puts "Error: #{DOCS_DIR}/ folder already exists"
62
78
  puts " Remove it first or run docyard in a different directory"
@@ -67,10 +83,12 @@ module Docyard
67
83
  puts ""
68
84
  puts "Created:"
69
85
  TEMPLATES.each_key { |file| puts " #{DOCS_DIR}/#{file}" }
86
+ puts " docyard.yml (configuration - optional)"
70
87
  puts ""
71
88
  puts "Next steps:"
72
89
  puts " 1. Edit your markdown files in #{DOCS_DIR}/"
73
- puts " 2. Run 'docyard serve' to preview your documentation locally"
90
+ puts " 2. Customize docyard.yml (optional)"
91
+ puts " 3. Run 'docyard serve' to preview your documentation locally"
74
92
  end
75
93
  end
76
94
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module LanguageMapping
5
+ TERMINAL_LANGUAGES = %w[bash sh shell powershell].freeze
6
+
7
+ LANGUAGE_TO_EXTENSION = {
8
+ "js" => "js",
9
+ "javascript" => "js",
10
+ "ts" => "ts",
11
+ "typescript" => "ts",
12
+ "jsx" => "jsx",
13
+ "tsx" => "tsx",
14
+ "py" => "py",
15
+ "python" => "py",
16
+ "rb" => "rb",
17
+ "ruby" => "rb",
18
+ "go" => "go",
19
+ "golang" => "go",
20
+ "rs" => "rs",
21
+ "rust" => "rs",
22
+ "php" => "php",
23
+ "html" => "html",
24
+ "htm" => "html",
25
+ "html5" => "html",
26
+ "css" => "css",
27
+ "json" => "json",
28
+ "yaml" => "yaml",
29
+ "yml" => "yaml",
30
+ "toml" => "toml",
31
+ "sql" => "sql",
32
+ "mysql" => "mysql",
33
+ "postgresql" => "pgsql",
34
+ "postgres" => "pgsql",
35
+ "pgsql" => "pgsql",
36
+ "graphql" => "graphql",
37
+ "gql" => "graphql",
38
+ "vue" => "vue",
39
+ "svelte" => "svelte",
40
+ "proto" => "proto",
41
+ "protobuf" => "proto"
42
+ }.freeze
43
+
44
+ def self.extension_for(language)
45
+ LANGUAGE_TO_EXTENSION[language.to_s.downcase]
46
+ end
47
+
48
+ def self.terminal_language?(language)
49
+ TERMINAL_LANGUAGES.include?(language.to_s.downcase)
50
+ end
51
+ end
52
+ end
@@ -3,6 +3,12 @@
3
3
  require "kramdown"
4
4
  require "kramdown-parser-gfm"
5
5
  require "yaml"
6
+ require_relative "components/registry"
7
+ require_relative "components/base_processor"
8
+ require_relative "components/callout_processor"
9
+ require_relative "components/tabs_processor"
10
+ require_relative "components/icon_processor"
11
+ require_relative "components/code_block_processor"
6
12
 
7
13
  module Docyard
8
14
  class Markdown
@@ -50,12 +56,17 @@ module Docyard
50
56
  end
51
57
 
52
58
  def render_html
53
- Kramdown::Document.new(
54
- content,
59
+ preprocessed_content = Components::Registry.run_preprocessors(content)
60
+
61
+ raw_html = Kramdown::Document.new(
62
+ preprocessed_content,
55
63
  input: "GFM",
56
64
  hard_wrap: false,
57
- syntax_highlighter: "rouge"
65
+ syntax_highlighter: "rouge",
66
+ parse_block_html: true
58
67
  ).to_html
68
+
69
+ Components::Registry.run_postprocessors(raw_html)
59
70
  end
60
71
  end
61
72
  end
@@ -3,12 +3,14 @@
3
3
  require "json"
4
4
  require "rack"
5
5
  require_relative "sidebar_builder"
6
+ require_relative "constants"
6
7
 
7
8
  module Docyard
8
9
  class RackApplication
9
- def initialize(docs_path:, file_watcher:)
10
+ def initialize(docs_path:, file_watcher:, config: nil)
10
11
  @docs_path = docs_path
11
12
  @file_watcher = file_watcher
13
+ @config = config
12
14
  @router = Router.new(docs_path: docs_path)
13
15
  @renderer = Renderer.new
14
16
  @asset_handler = AssetHandler.new
@@ -20,7 +22,7 @@ module Docyard
20
22
 
21
23
  private
22
24
 
23
- attr_reader :docs_path, :file_watcher, :router, :renderer, :asset_handler
25
+ attr_reader :docs_path, :file_watcher, :config, :router, :renderer, :asset_handler
24
26
 
25
27
  def handle_request(env)
26
28
  path = env["PATH_INFO"]
@@ -37,15 +39,82 @@ module Docyard
37
39
  result = router.resolve(path)
38
40
 
39
41
  if result.found?
40
- sidebar = SidebarBuilder.new(docs_path: docs_path, current_path: path)
41
- html = renderer.render_file(result.file_path, sidebar_html: sidebar.to_html)
42
- [Constants::STATUS_OK, { "Content-Type" => Constants::CONTENT_TYPE_HTML }, [html]]
42
+ render_documentation_page(result.file_path, path)
43
43
  else
44
- html = renderer.render_not_found
45
- [Constants::STATUS_NOT_FOUND, { "Content-Type" => Constants::CONTENT_TYPE_HTML }, [html]]
44
+ render_not_found_page
46
45
  end
47
46
  end
48
47
 
48
+ def render_documentation_page(file_path, current_path)
49
+ html = renderer.render_file(
50
+ file_path,
51
+ sidebar_html: build_sidebar(current_path),
52
+ branding: branding_options
53
+ )
54
+
55
+ [Constants::STATUS_OK, { "Content-Type" => Constants::CONTENT_TYPE_HTML }, [html]]
56
+ end
57
+
58
+ def render_not_found_page
59
+ html = renderer.render_not_found
60
+ [Constants::STATUS_NOT_FOUND, { "Content-Type" => Constants::CONTENT_TYPE_HTML }, [html]]
61
+ end
62
+
63
+ def build_sidebar(current_path)
64
+ SidebarBuilder.new(
65
+ docs_path: docs_path,
66
+ current_path: current_path,
67
+ config: config
68
+ ).to_html
69
+ end
70
+
71
+ def branding_options
72
+ return default_branding unless config
73
+
74
+ default_branding.merge(config_branding_options)
75
+ end
76
+
77
+ def default_branding
78
+ {
79
+ site_title: Constants::DEFAULT_SITE_TITLE,
80
+ site_description: "",
81
+ logo: Constants::DEFAULT_LOGO_PATH,
82
+ logo_dark: Constants::DEFAULT_LOGO_DARK_PATH,
83
+ favicon: nil,
84
+ display_logo: true,
85
+ display_title: true
86
+ }
87
+ end
88
+
89
+ def config_branding_options
90
+ site = config.site
91
+ branding = config.branding
92
+
93
+ {
94
+ site_title: site.title || Constants::DEFAULT_SITE_TITLE,
95
+ site_description: site.description || "",
96
+ logo: resolve_logo(branding.logo, branding.logo_dark),
97
+ logo_dark: resolve_logo_dark(branding.logo, branding.logo_dark),
98
+ favicon: branding.favicon
99
+ }.merge(appearance_options(branding.appearance))
100
+ end
101
+
102
+ def appearance_options(appearance)
103
+ appearance ||= {}
104
+ {
105
+ display_logo: appearance["logo"] != false,
106
+ display_title: appearance["title"] != false
107
+ }
108
+ end
109
+
110
+ def resolve_logo(logo, logo_dark)
111
+ logo || logo_dark || Constants::DEFAULT_LOGO_PATH
112
+ end
113
+
114
+ def resolve_logo_dark(logo, logo_dark)
115
+ logo_dark || logo || Constants::DEFAULT_LOGO_DARK_PATH
116
+ end
117
+
49
118
  def handle_reload_check(env)
50
119
  since = parse_since_timestamp(env)
51
120
  reload_needed = file_watcher.changed_since?(since)
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "erb"
4
+ require_relative "constants"
4
5
 
5
6
  module Docyard
6
7
  class Renderer
7
8
  LAYOUTS_PATH = File.join(__dir__, "templates", "layouts")
8
9
  ERRORS_PATH = File.join(__dir__, "templates", "errors")
10
+ PARTIALS_PATH = File.join(__dir__, "templates", "partials")
9
11
 
10
12
  attr_reader :layout_path
11
13
 
@@ -13,7 +15,7 @@ module Docyard
13
15
  @layout_path = File.join(LAYOUTS_PATH, "#{layout}.html.erb")
14
16
  end
15
17
 
16
- def render_file(file_path, sidebar_html: "")
18
+ def render_file(file_path, sidebar_html: "", branding: {})
17
19
  markdown_content = File.read(file_path)
18
20
  markdown = Markdown.new(markdown_content)
19
21
 
@@ -21,17 +23,17 @@ module Docyard
21
23
 
22
24
  render(
23
25
  content: html_content,
24
- page_title: markdown.title || "Documentation",
25
- sidebar_html: sidebar_html
26
+ page_title: markdown.title || Constants::DEFAULT_SITE_TITLE,
27
+ sidebar_html: sidebar_html,
28
+ branding: branding
26
29
  )
27
30
  end
28
31
 
29
- def render(content:, page_title: "Documentation", sidebar_html: "")
32
+ def render(content:, page_title: Constants::DEFAULT_SITE_TITLE, sidebar_html: "", branding: {})
30
33
  template = File.read(layout_path)
31
34
 
32
- @content = content
33
- @page_title = page_title
34
- @sidebar_html = sidebar_html
35
+ assign_content_variables(content, page_title, sidebar_html)
36
+ assign_branding_variables(branding)
35
37
 
36
38
  ERB.new(template).result(binding)
37
39
  end
@@ -52,8 +54,39 @@ module Docyard
52
54
  ERB.new(template).result(binding)
53
55
  end
54
56
 
57
+ def render_partial(name, locals = {})
58
+ partial_path = File.join(PARTIALS_PATH, "#{name}.html.erb")
59
+ template = File.read(partial_path)
60
+
61
+ locals.each { |key, value| instance_variable_set("@#{key}", value) }
62
+
63
+ ERB.new(template).result(binding)
64
+ end
65
+
66
+ def asset_path(path)
67
+ return path if path.nil? || path.start_with?("http://", "https://")
68
+
69
+ "/#{path}"
70
+ end
71
+
55
72
  private
56
73
 
74
+ def assign_content_variables(content, page_title, sidebar_html)
75
+ @content = content
76
+ @page_title = page_title
77
+ @sidebar_html = sidebar_html
78
+ end
79
+
80
+ def assign_branding_variables(branding)
81
+ @site_title = branding[:site_title] || Constants::DEFAULT_SITE_TITLE
82
+ @site_description = branding[:site_description] || ""
83
+ @logo = branding[:logo] || Constants::DEFAULT_LOGO_PATH
84
+ @logo_dark = branding[:logo_dark]
85
+ @favicon = branding[:favicon] || Constants::DEFAULT_FAVICON_PATH
86
+ @display_logo = branding[:display_logo].nil? || branding[:display_logo]
87
+ @display_title = branding[:display_title].nil? || branding[:display_title]
88
+ end
89
+
57
90
  def strip_md_from_links(html)
58
91
  html.gsub(/href="([^"]+)\.md"/, 'href="\1"')
59
92
  end
@@ -4,22 +4,25 @@ require "webrick"
4
4
  require "stringio"
5
5
  require_relative "file_watcher"
6
6
  require_relative "rack_application"
7
+ require_relative "config"
7
8
 
8
9
  module Docyard
9
10
  class Server
10
11
  DEFAULT_PORT = 4200
11
12
  DEFAULT_HOST = "localhost"
12
13
 
13
- attr_reader :port, :host, :docs_path
14
+ attr_reader :port, :host, :docs_path, :config
14
15
 
15
16
  def initialize(port: DEFAULT_PORT, host: DEFAULT_HOST, docs_path: "docs")
16
17
  @port = port
17
18
  @host = host
18
19
  @docs_path = docs_path
20
+ @config = Config.load
19
21
  @file_watcher = FileWatcher.new(File.expand_path(docs_path))
20
22
  @app = RackApplication.new(
21
23
  docs_path: File.expand_path(docs_path),
22
- file_watcher: @file_watcher
24
+ file_watcher: @file_watcher,
25
+ config: @config
23
26
  )
24
27
  end
25
28
 
@@ -9,7 +9,7 @@ module Docyard
9
9
  class SidebarBuilder
10
10
  attr_reader :docs_path, :current_path, :config
11
11
 
12
- def initialize(docs_path:, current_path: "/", config: {})
12
+ def initialize(docs_path:, current_path: "/", config: nil)
13
13
  @docs_path = docs_path
14
14
  @current_path = current_path
15
15
  @config = config
@@ -43,8 +43,16 @@ module Docyard
43
43
 
44
44
  def renderer
45
45
  @renderer ||= Sidebar::Renderer.new(
46
- site_title: config[:site_title] || "Documentation"
46
+ site_title: extract_site_title
47
47
  )
48
48
  end
49
+
50
+ def extract_site_title
51
+ if config.is_a?(Hash)
52
+ config[:site_title] || "Documentation"
53
+ else
54
+ config&.site&.title || "Documentation"
55
+ end
56
+ end
49
57
  end
50
58
  end