docyard 0.9.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -1
  3. data/README.md +8 -253
  4. data/exe/docyard +6 -0
  5. data/lib/docyard/build/asset_bundler.rb +24 -2
  6. data/lib/docyard/build/error_page_generator.rb +33 -0
  7. data/lib/docyard/build/file_copier.rb +12 -5
  8. data/lib/docyard/build/file_writer.rb +19 -0
  9. data/lib/docyard/build/llms_txt_generator.rb +103 -0
  10. data/lib/docyard/build/root_fallback_generator.rb +66 -0
  11. data/lib/docyard/build/sitemap_generator.rb +1 -1
  12. data/lib/docyard/build/static_generator.rb +119 -81
  13. data/lib/docyard/builder.rb +6 -2
  14. data/lib/docyard/cli.rb +14 -4
  15. data/lib/docyard/components/processors/callout_processor.rb +1 -1
  16. data/lib/docyard/components/processors/code_block_extended_fence_postprocessor.rb +24 -0
  17. data/lib/docyard/components/processors/code_block_extended_fence_preprocessor.rb +44 -0
  18. data/lib/docyard/components/processors/code_block_options_preprocessor.rb +11 -1
  19. data/lib/docyard/components/processors/code_block_processor.rb +5 -24
  20. data/lib/docyard/components/processors/code_group_processor.rb +6 -22
  21. data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +1 -0
  22. data/lib/docyard/components/processors/file_tree_processor.rb +1 -2
  23. data/lib/docyard/components/processors/icon_processor.rb +8 -2
  24. data/lib/docyard/components/processors/include_processor.rb +10 -10
  25. data/lib/docyard/components/processors/video_embed_processor.rb +14 -3
  26. data/lib/docyard/components/support/code_block/feature_extractor.rb +3 -1
  27. data/lib/docyard/components/support/code_block/icon_detector.rb +5 -12
  28. data/lib/docyard/components/support/code_block/line_number_resolver.rb +30 -0
  29. data/lib/docyard/components/support/code_detector.rb +2 -12
  30. data/lib/docyard/components/support/code_group/html_builder.rb +2 -6
  31. data/lib/docyard/components/support/tabs/icon_detector.rb +6 -2
  32. data/lib/docyard/components/support/tabs/parser.rb +6 -23
  33. data/lib/docyard/config/analytics_resolver.rb +24 -0
  34. data/lib/docyard/config/branding_resolver.rb +58 -27
  35. data/lib/docyard/config/key_validator.rb +30 -0
  36. data/lib/docyard/config/logo_detector.rb +8 -8
  37. data/lib/docyard/config/schema.rb +39 -0
  38. data/lib/docyard/config/section.rb +21 -0
  39. data/lib/docyard/config/validation_helpers.rb +83 -0
  40. data/lib/docyard/config/validator.rb +45 -144
  41. data/lib/docyard/config/validators/navigation.rb +43 -0
  42. data/lib/docyard/config/validators/section.rb +114 -0
  43. data/lib/docyard/config.rb +46 -102
  44. data/lib/docyard/constants.rb +59 -0
  45. data/lib/docyard/{utils/errors.rb → errors.rb} +6 -0
  46. data/lib/docyard/initializer.rb +100 -49
  47. data/lib/docyard/navigation/breadcrumb_builder.rb +45 -6
  48. data/lib/docyard/navigation/page_navigation_builder.rb +65 -0
  49. data/lib/docyard/navigation/sidebar/auto_builder.rb +107 -0
  50. data/lib/docyard/navigation/sidebar/cache.rb +96 -0
  51. data/lib/docyard/navigation/sidebar/config_builder.rb +179 -0
  52. data/lib/docyard/navigation/sidebar/distributed_builder.rb +145 -0
  53. data/lib/docyard/navigation/sidebar/local_config_loader.rb +69 -3
  54. data/lib/docyard/navigation/sidebar/renderer.rb +12 -1
  55. data/lib/docyard/navigation/sidebar_builder.rb +43 -81
  56. data/lib/docyard/rendering/branding_variables.rb +65 -0
  57. data/lib/docyard/rendering/icon_helpers.rb +14 -1
  58. data/lib/docyard/rendering/icons/devicons.rb +63 -0
  59. data/lib/docyard/rendering/icons.rb +26 -27
  60. data/lib/docyard/rendering/markdown.rb +5 -23
  61. data/lib/docyard/rendering/og_helpers.rb +36 -0
  62. data/lib/docyard/rendering/renderer.rb +96 -61
  63. data/lib/docyard/rendering/template_resolver.rb +14 -0
  64. data/lib/docyard/routing/fallback_resolver.rb +3 -3
  65. data/lib/docyard/search/build_indexer.rb +2 -2
  66. data/lib/docyard/search/dev_indexer.rb +36 -28
  67. data/lib/docyard/search/pagefind_support.rb +1 -1
  68. data/lib/docyard/server/asset_handler.rb +39 -15
  69. data/lib/docyard/server/dev_server.rb +90 -55
  70. data/lib/docyard/server/file_watcher.rb +68 -18
  71. data/lib/docyard/server/pagefind_handler.rb +1 -1
  72. data/lib/docyard/server/preview_server.rb +29 -33
  73. data/lib/docyard/server/rack_application.rb +39 -71
  74. data/lib/docyard/server/router.rb +11 -7
  75. data/lib/docyard/server/sse_server.rb +157 -0
  76. data/lib/docyard/server/static_file_app.rb +42 -0
  77. data/lib/docyard/templates/assets/css/components/banner.css +31 -0
  78. data/lib/docyard/templates/assets/css/components/breadcrumbs.css +2 -1
  79. data/lib/docyard/templates/assets/css/components/callout.css +26 -6
  80. data/lib/docyard/templates/assets/css/components/code-block.css +4 -2
  81. data/lib/docyard/templates/assets/css/components/code-group.css +20 -7
  82. data/lib/docyard/templates/assets/css/components/feedback.css +126 -0
  83. data/lib/docyard/templates/assets/css/components/file-tree.css +5 -4
  84. data/lib/docyard/templates/assets/css/components/heading-anchor.css +2 -2
  85. data/lib/docyard/templates/assets/css/components/icon.css +5 -0
  86. data/lib/docyard/templates/assets/css/components/nav-menu.css +20 -4
  87. data/lib/docyard/templates/assets/css/components/navigation.css +25 -3
  88. data/lib/docyard/templates/assets/css/components/page-actions.css +131 -0
  89. data/lib/docyard/templates/assets/css/components/prev-next.css +14 -7
  90. data/lib/docyard/templates/assets/css/components/search.css +6 -10
  91. data/lib/docyard/templates/assets/css/components/tab-bar.css +9 -6
  92. data/lib/docyard/templates/assets/css/components/table-of-contents.css +63 -17
  93. data/lib/docyard/templates/assets/css/components/tabs.css +12 -4
  94. data/lib/docyard/templates/assets/css/components/theme-toggle.css +3 -1
  95. data/lib/docyard/templates/assets/css/landing.css +82 -13
  96. data/lib/docyard/templates/assets/css/layout.css +32 -16
  97. data/lib/docyard/templates/assets/css/markdown.css +22 -2
  98. data/lib/docyard/templates/assets/css/variables.css +14 -1
  99. data/lib/docyard/templates/assets/js/components/code-group.js +4 -1
  100. data/lib/docyard/templates/assets/js/components/copy-page.js +115 -0
  101. data/lib/docyard/templates/assets/js/components/feedback.js +66 -0
  102. data/lib/docyard/templates/assets/js/components/file-tree.js +5 -5
  103. data/lib/docyard/templates/assets/js/components/navigation.js +3 -3
  104. data/lib/docyard/templates/assets/js/components/search.js +3 -3
  105. data/lib/docyard/templates/assets/js/components/table-of-contents.js +12 -6
  106. data/lib/docyard/templates/assets/js/components/tabs.js +45 -22
  107. data/lib/docyard/templates/assets/js/components/tooltip.js +4 -4
  108. data/lib/docyard/templates/assets/js/hot-reload.js +44 -0
  109. data/lib/docyard/templates/errors/404.html.erb +125 -5
  110. data/lib/docyard/templates/errors/500.html.erb +184 -10
  111. data/lib/docyard/templates/errors/redirect.html.erb +12 -0
  112. data/lib/docyard/templates/init/_sidebar.yml +36 -0
  113. data/lib/docyard/templates/init/docyard.yml +36 -0
  114. data/lib/docyard/templates/init/pages/components.md +146 -0
  115. data/lib/docyard/templates/init/pages/getting-started.md +94 -0
  116. data/lib/docyard/templates/init/pages/index.md +22 -0
  117. data/lib/docyard/templates/layouts/default.html.erb +10 -0
  118. data/lib/docyard/templates/layouts/splash.html.erb +14 -1
  119. data/lib/docyard/templates/partials/_analytics.html.erb +24 -0
  120. data/lib/docyard/templates/partials/_banner.html.erb +1 -1
  121. data/lib/docyard/templates/partials/_code_block.html.erb +1 -1
  122. data/lib/docyard/templates/partials/_feedback.html.erb +14 -0
  123. data/lib/docyard/templates/partials/_footer.html.erb +1 -1
  124. data/lib/docyard/templates/partials/_head.html.erb +80 -5
  125. data/lib/docyard/templates/partials/_icon_library.html.erb +8 -0
  126. data/lib/docyard/templates/partials/_page_actions.html.erb +21 -0
  127. data/lib/docyard/templates/partials/_scripts.html.erb +6 -3
  128. data/lib/docyard/templates/partials/_tabs.html.erb +4 -1
  129. data/lib/docyard/utils/git_info.rb +157 -0
  130. data/lib/docyard/utils/hash_utils.rb +31 -0
  131. data/lib/docyard/utils/html_helpers.rb +8 -0
  132. data/lib/docyard/utils/logging.rb +44 -3
  133. data/lib/docyard/utils/path_resolver.rb +0 -10
  134. data/lib/docyard/utils/path_utils.rb +73 -0
  135. data/lib/docyard/version.rb +1 -1
  136. data/lib/docyard.rb +2 -2
  137. metadata +81 -47
  138. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  139. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  140. data/.github/pull_request_template.md +0 -14
  141. data/.github/workflows/ci.yml +0 -49
  142. data/.rubocop.yml +0 -42
  143. data/CODE_OF_CONDUCT.md +0 -132
  144. data/CONTRIBUTING.md +0 -55
  145. data/LICENSE.vscode-icons +0 -42
  146. data/Rakefile +0 -8
  147. data/lib/docyard/config/constants.rb +0 -31
  148. data/lib/docyard/navigation/sidebar/children_discoverer.rb +0 -51
  149. data/lib/docyard/navigation/sidebar/config_parser.rb +0 -208
  150. data/lib/docyard/navigation/sidebar/file_resolver.rb +0 -90
  151. data/lib/docyard/navigation/sidebar/file_system_scanner.rb +0 -78
  152. data/lib/docyard/navigation/sidebar/metadata_extractor.rb +0 -71
  153. data/lib/docyard/navigation/sidebar/metadata_reader.rb +0 -51
  154. data/lib/docyard/navigation/sidebar/path_prefixer.rb +0 -34
  155. data/lib/docyard/navigation/sidebar/sorter.rb +0 -21
  156. data/lib/docyard/navigation/sidebar/title_extractor.rb +0 -25
  157. data/lib/docyard/navigation/sidebar/tree_builder.rb +0 -140
  158. data/lib/docyard/rendering/icons/LICENSE.phosphor +0 -21
  159. data/lib/docyard/rendering/icons/file_types.rb +0 -79
  160. data/lib/docyard/rendering/icons/phosphor.rb +0 -93
  161. data/lib/docyard/rendering/language_mapping.rb +0 -52
  162. data/lib/docyard/templates/assets/js/reload.js +0 -98
  163. data/lib/docyard/templates/partials/_icon.html.erb +0 -1
  164. data/lib/docyard/templates/partials/_icon_file_extension.html.erb +0 -1
  165. data/sig/docyard.rbs +0 -4
@@ -1,27 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "sidebar/file_system_scanner"
4
- require_relative "sidebar/title_extractor"
5
- require_relative "sidebar/tree_builder"
3
+ require_relative "sidebar/cache"
4
+ require_relative "sidebar/config_builder"
5
+ require_relative "sidebar/auto_builder"
6
+ require_relative "sidebar/distributed_builder"
6
7
  require_relative "sidebar/renderer"
7
- require_relative "sidebar/config_parser"
8
- require_relative "sidebar/local_config_loader"
9
- require_relative "sidebar/path_prefixer"
10
8
  require_relative "sidebar/tree_filter"
9
+ require_relative "sidebar/local_config_loader"
11
10
 
12
11
  module Docyard
13
12
  class SidebarBuilder
14
- attr_reader :docs_path, :current_path, :config, :header_ctas
13
+ attr_reader :docs_path, :current_path, :config, :header_ctas, :sidebar_cache
15
14
 
16
- def initialize(docs_path:, current_path: "/", config: nil, header_ctas: [])
15
+ def initialize(docs_path:, current_path: "/", config: nil, header_ctas: [], sidebar_cache: nil)
17
16
  @docs_path = docs_path
18
17
  @current_path = current_path
19
18
  @config = config
20
19
  @header_ctas = header_ctas
20
+ @sidebar_cache = sidebar_cache
21
21
  end
22
22
 
23
23
  def tree
24
- @tree ||= build_scoped_tree
24
+ @tree ||= build_tree
25
25
  end
26
26
 
27
27
  def to_html
@@ -30,101 +30,63 @@ module Docyard
30
30
 
31
31
  private
32
32
 
33
- def build_scoped_tree
34
- active_tab = find_active_tab
35
- return build_tree_for_path(docs_path) unless active_tab
36
-
37
- build_tree_for_tab(active_tab)
38
- end
39
-
40
- def build_tree_for_tab(tab)
41
- tab_path = tab["href"]&.chomp("/")
42
- return build_tree_for_path(docs_path) if empty_tab_path?(tab_path)
33
+ def build_tree
34
+ return build_from_cache if sidebar_cache&.valid?
43
35
 
44
- scoped_docs_path = resolve_scoped_path(tab_path)
45
- build_scoped_or_filtered_tree(scoped_docs_path, tab_path)
36
+ build_without_cache
46
37
  end
47
38
 
48
- def empty_tab_path?(tab_path)
49
- tab_path.nil? || tab_path.empty? || tab_path == "/"
39
+ def build_from_cache
40
+ base_tree = sidebar_cache.get(current_path: current_path)
41
+ apply_tab_scoping(base_tree)
50
42
  end
51
43
 
52
- def resolve_scoped_path(tab_path)
53
- tab_folder = tab_path.sub(%r{^/}, "")
54
- File.join(docs_path, tab_folder)
44
+ def build_without_cache
45
+ base_tree = build_tree_for_mode
46
+ apply_tab_scoping(base_tree)
55
47
  end
56
48
 
57
- def build_scoped_or_filtered_tree(scoped_docs_path, tab_path)
58
- if scoped_sidebar_available?(scoped_docs_path)
59
- build_tree_for_path(scoped_docs_path, base_url_prefix: tab_path)
49
+ def build_tree_for_mode
50
+ case sidebar_mode
51
+ when "auto"
52
+ Sidebar::AutoBuilder.new(docs_path, current_path: current_path).build
53
+ when "distributed"
54
+ Sidebar::DistributedBuilder.new(docs_path, current_path: current_path).build
60
55
  else
61
- Sidebar::TreeFilter.new(build_tree_for_path(docs_path), tab_path).filter
56
+ build_config_tree
62
57
  end
63
58
  end
64
59
 
65
- def scoped_sidebar_available?(path)
66
- File.directory?(path) && Sidebar::LocalConfigLoader.new(path).config_file_exists?
67
- end
60
+ def build_config_tree
61
+ config_items = Sidebar::LocalConfigLoader.new(docs_path).load
62
+ return [] unless config_items
68
63
 
69
- def build_tree_for_path(path, base_url_prefix: "")
70
- config_items = Sidebar::LocalConfigLoader.new(path).load
71
- tree = build_tree(config_items, path, base_url_prefix)
72
- maybe_prepend_overview(tree, path, base_url_prefix)
64
+ Sidebar::ConfigBuilder.new(config_items, current_path: current_path).build
73
65
  end
74
66
 
75
- def build_tree(config_items, path, base_url_prefix)
76
- if config_items&.any?
77
- build_tree_from_config(config_items, path, base_url_prefix)
78
- else
79
- build_tree_from_filesystem(path, base_url_prefix)
80
- end
67
+ def sidebar_mode
68
+ config&.sidebar || "config"
81
69
  end
82
70
 
83
- def maybe_prepend_overview(tree, path, base_url_prefix)
84
- return tree if skip_overview?(tree, path, base_url_prefix)
71
+ def apply_tab_scoping(base_tree)
72
+ return base_tree if sidebar_mode == "auto"
73
+ return base_tree unless tabs_configured?
85
74
 
86
- [build_overview_item(base_url_prefix)] + tree
87
- end
88
-
89
- def skip_overview?(tree, path, base_url_prefix)
90
- base_url_prefix.empty? ||
91
- tree.first&.dig(:section) ||
92
- !File.file?(File.join(path, "index.md")) ||
93
- tree.any? { |item| item[:path] == base_url_prefix }
94
- end
95
-
96
- def build_overview_item(base_url_prefix)
97
- {
98
- title: "Overview", path: base_url_prefix, icon: nil,
99
- active: current_path == base_url_prefix, type: :file,
100
- collapsed: false, collapsible: false, target: "_self",
101
- has_index: false, section: false, children: []
102
- }
103
- end
104
-
105
- def build_tree_from_config(items, path, base_url_prefix)
106
- tree = Sidebar::ConfigParser.new(
107
- items, docs_path: path, current_path: current_path_relative_to(base_url_prefix)
108
- ).parse.map(&:to_h)
75
+ active_tab = find_active_tab
76
+ return base_tree unless active_tab
109
77
 
110
- Sidebar::PathPrefixer.new(tree, base_url_prefix).prefix
78
+ filter_tree_for_tab(base_tree, active_tab)
111
79
  end
112
80
 
113
- def build_tree_from_filesystem(path, base_url_prefix)
114
- file_items = Sidebar::FileSystemScanner.new(path).scan
115
- tree = Sidebar::TreeBuilder.new(
116
- docs_path: path, current_path: current_path_relative_to(base_url_prefix)
117
- ).build(file_items)
81
+ def filter_tree_for_tab(base_tree, tab)
82
+ tab_path = tab["href"]&.chomp("/")
83
+ return base_tree if empty_tab_path?(tab_path)
118
84
 
119
- Sidebar::PathPrefixer.new(tree, base_url_prefix).prefix
85
+ Sidebar::TreeFilter.new(base_tree, tab_path).filter
120
86
  end
121
87
 
122
- def current_path_relative_to(prefix)
123
- return current_path if prefix.empty?
124
- return current_path unless current_path.start_with?(prefix)
125
-
126
- relative = current_path.sub(prefix, "")
127
- relative.empty? ? "/" : relative
88
+ def empty_tab_path?(tab_path)
89
+ tab_path.nil? || tab_path.empty? || tab_path == "/"
128
90
  end
129
91
 
130
92
  def renderer
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module BrandingVariables
5
+ private
6
+
7
+ def assign_branding_variables(branding, current_path = "/")
8
+ assign_site_branding(branding)
9
+ assign_search_options(branding)
10
+ assign_credits_and_social(branding)
11
+ assign_tabs(branding, current_path)
12
+ assign_analytics(branding)
13
+ end
14
+
15
+ def assign_site_branding(branding)
16
+ @site_title = branding[:site_title] || Constants::DEFAULT_SITE_TITLE
17
+ @site_description = branding[:site_description] || ""
18
+ @logo = branding[:logo] || Constants::DEFAULT_LOGO_PATH
19
+ @logo_dark = branding[:logo_dark]
20
+ @favicon = branding[:favicon] || Constants::DEFAULT_FAVICON_PATH
21
+ @has_custom_logo = branding[:has_custom_logo] || false
22
+ @primary_color = branding[:primary_color]
23
+ end
24
+
25
+ def assign_search_options(branding)
26
+ @search_enabled = branding[:search_enabled].nil? || branding[:search_enabled]
27
+ @search_placeholder = branding[:search_placeholder] || "Search documentation..."
28
+ end
29
+
30
+ def assign_credits_and_social(branding)
31
+ @credits = branding[:credits] != false
32
+ @copyright = branding[:copyright]
33
+ @social = branding[:social] || []
34
+ @header_ctas = branding[:header_ctas] || []
35
+ @announcement = branding[:announcement]
36
+ end
37
+
38
+ def assign_analytics(branding)
39
+ @has_analytics = branding[:has_analytics] || false
40
+ @analytics_google = branding[:analytics_google]
41
+ @analytics_plausible = branding[:analytics_plausible]
42
+ @analytics_fathom = branding[:analytics_fathom]
43
+ @analytics_script = branding[:analytics_script]
44
+ end
45
+
46
+ def assign_tabs(branding, current_path)
47
+ tabs = branding[:tabs] || []
48
+ @tabs = tabs.map { |tab| tab.merge(active: tab_active?(tab[:href], current_path)) }
49
+ @has_tabs = branding[:has_tabs] || false
50
+ @current_path = current_path
51
+ end
52
+
53
+ def tab_active?(tab_href, current_path)
54
+ return false if tab_href.nil? || current_path.nil?
55
+ return false if tab_href.start_with?("http://", "https://")
56
+
57
+ normalized_tab = tab_href.chomp("/")
58
+ normalized_current = current_path.chomp("/")
59
+
60
+ return true if normalized_tab == normalized_current
61
+
62
+ current_path.start_with?("#{normalized_tab}/")
63
+ end
64
+ end
65
+ end
@@ -2,12 +2,25 @@
2
2
 
3
3
  module Docyard
4
4
  module IconHelpers
5
+ VALID_WEIGHTS = %w[regular bold fill light thin duotone].freeze
6
+
5
7
  def icon(name, weight = "regular")
6
- Icons.render(name.to_s.tr("_", "-"), weight) || ""
8
+ name = name.to_s
9
+ return name if name.strip.start_with?("<svg")
10
+
11
+ name = name.tr("_", "-")
12
+ weight = weight.to_s
13
+ weight = "regular" unless VALID_WEIGHTS.include?(weight)
14
+ weight_class = weight == "regular" ? "ph" : "ph-#{weight}"
15
+ %(<i class="#{weight_class} ph-#{name}" aria-hidden="true"></i>)
7
16
  end
8
17
 
9
18
  def icon_file_extension(extension)
10
19
  Icons.render_file_extension(extension) || ""
11
20
  end
21
+
22
+ def icon_for_language(language)
23
+ Icons.render_for_language(language) || ""
24
+ end
12
25
  end
13
26
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module Icons
5
+ module Devicons
6
+ MAP = {
7
+ "bash" => "devicon-bash-plain", "bun" => "devicon-bun-plain colored",
8
+ "c" => "devicon-c-plain colored", "cargo" => "devicon-rust-original colored",
9
+ "clj" => "devicon-clojure-plain colored", "clojure" => "devicon-clojure-plain colored",
10
+ "coffee" => "devicon-coffeescript-original colored", "coffeescript" => "devicon-coffeescript-original colored",
11
+ "composer" => "devicon-composer-plain colored", "cpp" => "devicon-cplusplus-plain colored",
12
+ "cs" => "devicon-csharp-plain colored", "csharp" => "devicon-csharp-plain colored",
13
+ "css" => "devicon-css3-plain colored", "dart" => "devicon-dart-plain colored",
14
+ "docker" => "devicon-docker-plain colored", "dockerfile" => "devicon-docker-plain colored",
15
+ "elixir" => "devicon-elixir-plain colored", "elm" => "devicon-elm-plain colored",
16
+ "erb" => "devicon-ruby-plain colored", "erl" => "devicon-erlang-plain colored",
17
+ "erlang" => "devicon-erlang-plain colored", "ex" => "devicon-elixir-plain colored",
18
+ "gem" => "devicon-ruby-plain colored", "go" => "devicon-go-plain colored",
19
+ "golang" => "devicon-go-plain colored", "gql" => "devicon-graphql-plain colored",
20
+ "gradle" => "devicon-gradle-original colored", "graphql" => "devicon-graphql-plain colored",
21
+ "groovy" => "devicon-groovy-plain colored", "h" => "devicon-c-plain colored",
22
+ "haml" => "devicon-ruby-plain colored", "haskell" => "devicon-haskell-plain colored",
23
+ "homebrew" => "devicon-homebrew-plain colored", "hs" => "devicon-haskell-plain colored",
24
+ "htm" => "devicon-html5-plain colored", "html" => "devicon-html5-plain colored",
25
+ "html5" => "devicon-html5-plain colored", "java" => "devicon-java-plain colored",
26
+ "javascript" => "devicon-javascript-plain colored", "js" => "devicon-javascript-plain colored",
27
+ "json" => "devicon-json-plain colored", "jsx" => "devicon-react-original colored",
28
+ "kotlin" => "devicon-kotlin-plain colored", "kt" => "devicon-kotlin-plain colored",
29
+ "less" => "devicon-less-plain-wordmark colored", "lua" => "devicon-lua-plain colored",
30
+ "markdown" => "devicon-markdown-original", "maven" => "devicon-maven-plain colored",
31
+ "md" => "devicon-markdown-original", "mysql" => "devicon-mysql-plain colored",
32
+ "nginx" => "devicon-nginx-original colored", "nim" => "devicon-nim-plain colored",
33
+ "nix" => "devicon-nixos-plain colored", "npm" => "devicon-npm-original-wordmark colored",
34
+ "nuget" => "devicon-nuget-original colored", "objc" => "devicon-objectivec-plain colored",
35
+ "objectivec" => "devicon-objectivec-plain colored", "ocaml" => "devicon-ocaml-plain colored",
36
+ "pacman" => "devicon-archlinux-plain colored", "perl" => "devicon-perl-plain colored",
37
+ "pgsql" => "devicon-postgresql-plain colored", "php" => "devicon-php-plain colored",
38
+ "pip" => "devicon-python-plain colored", "pl" => "devicon-perl-plain colored",
39
+ "pnpm" => "devicon-pnpm-plain colored", "postgres" => "devicon-postgresql-plain colored",
40
+ "postgresql" => "devicon-postgresql-plain colored", "py" => "devicon-python-plain colored",
41
+ "python" => "devicon-python-plain colored", "r" => "devicon-r-plain colored",
42
+ "rb" => "devicon-ruby-plain colored", "rs" => "devicon-rust-original colored",
43
+ "ruby" => "devicon-ruby-plain colored", "rust" => "devicon-rust-original colored",
44
+ "sass" => "devicon-sass-original colored", "scala" => "devicon-scala-plain colored",
45
+ "scss" => "devicon-sass-original colored", "sql" => "devicon-azuresqldatabase-plain colored",
46
+ "svelte" => "devicon-svelte-plain colored", "swift" => "devicon-swift-plain colored",
47
+ "terraform" => "devicon-terraform-plain colored", "tf" => "devicon-terraform-plain colored",
48
+ "ts" => "devicon-typescript-plain colored", "tsx" => "devicon-react-original colored",
49
+ "typescript" => "devicon-typescript-plain colored", "vim" => "devicon-vim-plain colored",
50
+ "vue" => "devicon-vuejs-plain colored", "xml" => "devicon-xml-plain colored",
51
+ "yaml" => "devicon-yaml-plain colored", "yml" => "devicon-yaml-plain colored",
52
+ "yarn" => "devicon-yarn-plain colored", "zig" => "devicon-zig-plain colored"
53
+ }.freeze
54
+
55
+ HIGHLIGHT_ALIASES = {
56
+ "apt" => "bash", "bun" => "bash", "cargo" => "bash", "composer" => "bash",
57
+ "gem" => "bash", "gradle" => "bash", "homebrew" => "bash", "maven" => "bash",
58
+ "npm" => "bash", "nuget" => "bash", "pacman" => "bash", "pip" => "bash",
59
+ "pnpm" => "bash", "yarn" => "bash"
60
+ }.freeze
61
+ end
62
+ end
63
+ end
@@ -1,40 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "icons/phosphor"
4
- require_relative "icons/file_types"
5
- require_relative "renderer"
3
+ require_relative "icons/devicons"
6
4
 
7
5
  module Docyard
8
6
  module Icons
9
- LIBRARIES = {
10
- phosphor: PHOSPHOR
11
- }.freeze
7
+ VALID_WEIGHTS = %w[regular bold fill light thin duotone].freeze
12
8
 
13
9
  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
- )
10
+ name = name.to_s.tr("_", "-")
11
+ weight = weight.to_s
12
+ weight = "regular" unless VALID_WEIGHTS.include?(weight)
13
+ weight_class = weight == "regular" ? "ph" : "ph-#{weight}"
14
+ %(<i class="#{weight_class} ph-#{name}" aria-hidden="true"></i>)
15
+ end
16
+
17
+ def self.render_for_language(language)
18
+ devicon_class = Devicons::MAP[language.to_s.downcase]
19
+ return %(<i class="#{devicon_class}" aria-hidden="true"></i>) if devicon_class
20
+
21
+ ""
23
22
  end
24
23
 
25
24
  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
25
+ devicon_class = Devicons::MAP[extension.to_s.downcase]
26
+ return %(<i class="#{devicon_class}" aria-hidden="true"></i>) if devicon_class
27
+
28
+ ""
29
+ end
30
+
31
+ def self.highlight_language(language)
32
+ Devicons::HIGHLIGHT_ALIASES[language.to_s.downcase] || language
33
+ end
34
+
35
+ def self.devicon?(language)
36
+ Devicons::MAP.key?(language.to_s.downcase)
38
37
  end
39
38
  end
40
39
  end
@@ -19,6 +19,8 @@ require_relative "../components/processors/include_processor"
19
19
  require_relative "../components/processors/code_block_options_preprocessor"
20
20
  require_relative "../components/processors/code_block_diff_preprocessor"
21
21
  require_relative "../components/processors/code_block_focus_preprocessor"
22
+ require_relative "../components/processors/code_block_extended_fence_preprocessor"
23
+ require_relative "../components/processors/code_block_extended_fence_postprocessor"
22
24
  require_relative "../components/processors/table_wrapper_processor"
23
25
  require_relative "../components/processors/heading_anchor_processor"
24
26
  require_relative "../components/processors/custom_anchor_processor"
@@ -63,28 +65,8 @@ module Docyard
63
65
  frontmatter["description"]
64
66
  end
65
67
 
66
- def sidebar_icon
67
- frontmatter.dig("sidebar", "icon")
68
- end
69
-
70
- def sidebar_text
71
- frontmatter.dig("sidebar", "text")
72
- end
73
-
74
- def sidebar_collapsed
75
- frontmatter.dig("sidebar", "collapsed")
76
- end
77
-
78
- def sidebar_order
79
- frontmatter.dig("sidebar", "order")
80
- end
81
-
82
- def sidebar_badge
83
- frontmatter.dig("sidebar", "badge")
84
- end
85
-
86
- def sidebar_badge_type
87
- frontmatter.dig("sidebar", "badge_type")
68
+ def og_image
69
+ frontmatter["og_image"]
88
70
  end
89
71
 
90
72
  def toc
@@ -109,7 +91,7 @@ module Docyard
109
91
  def render_html
110
92
  @context[:config] = config&.data
111
93
  @context[:current_file] = @file_path
112
- @context[:docs_root] = "docs"
94
+ @context[:docs_root] = config&.source || "docs"
113
95
 
114
96
  preprocessed_content = Components::Registry.run_preprocessors(content, @context)
115
97
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module OgHelpers
5
+ def assign_og_variables(branding, page_description, page_og_image, current_path)
6
+ site_url = branding[:site_url]
7
+ @og_enabled = !site_url.nil? && !site_url.empty?
8
+ return unless @og_enabled
9
+
10
+ @og_url = build_canonical_url(site_url, current_path)
11
+ @og_description = page_description || @site_description
12
+ @og_image = build_og_image_url(site_url, page_og_image || branding[:og_image])
13
+ @og_twitter = branding[:twitter]
14
+ end
15
+
16
+ private
17
+
18
+ def build_canonical_url(site_url, current_path)
19
+ base = site_url.chomp("/")
20
+ path = current_path.start_with?("/") ? current_path : "/#{current_path}"
21
+ "#{base}#{path}"
22
+ end
23
+
24
+ def build_og_image_url(site_url, og_image)
25
+ return nil if og_image.nil?
26
+
27
+ if og_image.start_with?("http://", "https://")
28
+ og_image
29
+ else
30
+ base = site_url.chomp("/")
31
+ path = og_image.start_with?("/") ? og_image : "/#{og_image}"
32
+ "#{base}#{path}"
33
+ end
34
+ end
35
+ end
36
+ end