docyard 0.9.0 → 1.0.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 (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +8 -253
  4. data/exe/docyard +6 -0
  5. data/lib/docyard/build/asset_bundler.rb +2 -2
  6. data/lib/docyard/build/file_copier.rb +12 -5
  7. data/lib/docyard/build/llms_txt_generator.rb +103 -0
  8. data/lib/docyard/build/sitemap_generator.rb +1 -1
  9. data/lib/docyard/build/static_generator.rb +115 -79
  10. data/lib/docyard/builder.rb +6 -2
  11. data/lib/docyard/cli.rb +14 -4
  12. data/lib/docyard/components/processors/callout_processor.rb +1 -1
  13. data/lib/docyard/components/processors/code_block_extended_fence_postprocessor.rb +24 -0
  14. data/lib/docyard/components/processors/code_block_extended_fence_preprocessor.rb +44 -0
  15. data/lib/docyard/components/processors/code_block_options_preprocessor.rb +11 -1
  16. data/lib/docyard/components/processors/code_block_processor.rb +5 -24
  17. data/lib/docyard/components/processors/code_group_processor.rb +6 -22
  18. data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +1 -0
  19. data/lib/docyard/components/processors/file_tree_processor.rb +1 -2
  20. data/lib/docyard/components/processors/icon_processor.rb +8 -2
  21. data/lib/docyard/components/processors/include_processor.rb +10 -10
  22. data/lib/docyard/components/processors/video_embed_processor.rb +14 -3
  23. data/lib/docyard/components/support/code_block/feature_extractor.rb +3 -1
  24. data/lib/docyard/components/support/code_block/icon_detector.rb +5 -12
  25. data/lib/docyard/components/support/code_block/line_number_resolver.rb +30 -0
  26. data/lib/docyard/components/support/code_detector.rb +2 -12
  27. data/lib/docyard/components/support/code_group/html_builder.rb +2 -6
  28. data/lib/docyard/components/support/tabs/icon_detector.rb +6 -2
  29. data/lib/docyard/components/support/tabs/parser.rb +6 -23
  30. data/lib/docyard/config/analytics_resolver.rb +24 -0
  31. data/lib/docyard/config/branding_resolver.rb +58 -27
  32. data/lib/docyard/config/key_validator.rb +30 -0
  33. data/lib/docyard/config/logo_detector.rb +8 -8
  34. data/lib/docyard/config/schema.rb +39 -0
  35. data/lib/docyard/config/section.rb +21 -0
  36. data/lib/docyard/config/validation_helpers.rb +83 -0
  37. data/lib/docyard/config/validator.rb +45 -144
  38. data/lib/docyard/config/validators/navigation.rb +43 -0
  39. data/lib/docyard/config/validators/section.rb +114 -0
  40. data/lib/docyard/config.rb +46 -102
  41. data/lib/docyard/constants.rb +59 -0
  42. data/lib/docyard/{utils/errors.rb → errors.rb} +6 -0
  43. data/lib/docyard/initializer.rb +100 -49
  44. data/lib/docyard/navigation/page_navigation_builder.rb +65 -0
  45. data/lib/docyard/navigation/sidebar/auto_builder.rb +107 -0
  46. data/lib/docyard/navigation/sidebar/cache.rb +96 -0
  47. data/lib/docyard/navigation/sidebar/config_builder.rb +179 -0
  48. data/lib/docyard/navigation/sidebar/distributed_builder.rb +145 -0
  49. data/lib/docyard/navigation/sidebar/local_config_loader.rb +69 -3
  50. data/lib/docyard/navigation/sidebar/renderer.rb +12 -1
  51. data/lib/docyard/navigation/sidebar_builder.rb +43 -81
  52. data/lib/docyard/rendering/branding_variables.rb +65 -0
  53. data/lib/docyard/rendering/icon_helpers.rb +14 -1
  54. data/lib/docyard/rendering/icons/devicons.rb +63 -0
  55. data/lib/docyard/rendering/icons.rb +26 -27
  56. data/lib/docyard/rendering/markdown.rb +5 -23
  57. data/lib/docyard/rendering/og_helpers.rb +36 -0
  58. data/lib/docyard/rendering/renderer.rb +87 -59
  59. data/lib/docyard/rendering/template_resolver.rb +14 -0
  60. data/lib/docyard/routing/fallback_resolver.rb +3 -3
  61. data/lib/docyard/search/build_indexer.rb +2 -2
  62. data/lib/docyard/search/dev_indexer.rb +36 -28
  63. data/lib/docyard/search/pagefind_support.rb +1 -1
  64. data/lib/docyard/server/asset_handler.rb +39 -15
  65. data/lib/docyard/server/dev_server.rb +90 -55
  66. data/lib/docyard/server/file_watcher.rb +68 -18
  67. data/lib/docyard/server/pagefind_handler.rb +1 -1
  68. data/lib/docyard/server/preview_server.rb +29 -33
  69. data/lib/docyard/server/rack_application.rb +38 -70
  70. data/lib/docyard/server/router.rb +11 -7
  71. data/lib/docyard/server/sse_server.rb +157 -0
  72. data/lib/docyard/server/static_file_app.rb +42 -0
  73. data/lib/docyard/templates/assets/css/components/banner.css +31 -0
  74. data/lib/docyard/templates/assets/css/components/breadcrumbs.css +2 -1
  75. data/lib/docyard/templates/assets/css/components/callout.css +26 -6
  76. data/lib/docyard/templates/assets/css/components/code-block.css +4 -2
  77. data/lib/docyard/templates/assets/css/components/code-group.css +20 -7
  78. data/lib/docyard/templates/assets/css/components/feedback.css +126 -0
  79. data/lib/docyard/templates/assets/css/components/file-tree.css +5 -4
  80. data/lib/docyard/templates/assets/css/components/icon.css +5 -0
  81. data/lib/docyard/templates/assets/css/components/nav-menu.css +20 -4
  82. data/lib/docyard/templates/assets/css/components/navigation.css +25 -3
  83. data/lib/docyard/templates/assets/css/components/page-actions.css +131 -0
  84. data/lib/docyard/templates/assets/css/components/prev-next.css +14 -7
  85. data/lib/docyard/templates/assets/css/components/search.css +6 -10
  86. data/lib/docyard/templates/assets/css/components/tab-bar.css +7 -4
  87. data/lib/docyard/templates/assets/css/components/table-of-contents.css +57 -11
  88. data/lib/docyard/templates/assets/css/components/tabs.css +12 -4
  89. data/lib/docyard/templates/assets/css/components/theme-toggle.css +3 -1
  90. data/lib/docyard/templates/assets/css/landing.css +82 -13
  91. data/lib/docyard/templates/assets/css/layout.css +17 -0
  92. data/lib/docyard/templates/assets/css/markdown.css +22 -2
  93. data/lib/docyard/templates/assets/css/variables.css +13 -1
  94. data/lib/docyard/templates/assets/js/components/code-group.js +4 -1
  95. data/lib/docyard/templates/assets/js/components/copy-page.js +115 -0
  96. data/lib/docyard/templates/assets/js/components/feedback.js +66 -0
  97. data/lib/docyard/templates/assets/js/components/file-tree.js +5 -5
  98. data/lib/docyard/templates/assets/js/components/navigation.js +3 -3
  99. data/lib/docyard/templates/assets/js/components/search.js +3 -3
  100. data/lib/docyard/templates/assets/js/components/table-of-contents.js +12 -6
  101. data/lib/docyard/templates/assets/js/components/tabs.js +45 -22
  102. data/lib/docyard/templates/assets/js/components/tooltip.js +4 -4
  103. data/lib/docyard/templates/assets/js/hot-reload.js +44 -0
  104. data/lib/docyard/templates/errors/404.html.erb +114 -5
  105. data/lib/docyard/templates/errors/500.html.erb +173 -10
  106. data/lib/docyard/templates/init/_sidebar.yml +36 -0
  107. data/lib/docyard/templates/init/docyard.yml +36 -0
  108. data/lib/docyard/templates/init/pages/components.md +146 -0
  109. data/lib/docyard/templates/init/pages/getting-started.md +94 -0
  110. data/lib/docyard/templates/init/pages/index.md +22 -0
  111. data/lib/docyard/templates/layouts/default.html.erb +10 -0
  112. data/lib/docyard/templates/layouts/splash.html.erb +14 -1
  113. data/lib/docyard/templates/partials/_analytics.html.erb +24 -0
  114. data/lib/docyard/templates/partials/_banner.html.erb +1 -1
  115. data/lib/docyard/templates/partials/_code_block.html.erb +1 -1
  116. data/lib/docyard/templates/partials/_feedback.html.erb +14 -0
  117. data/lib/docyard/templates/partials/_footer.html.erb +1 -1
  118. data/lib/docyard/templates/partials/_head.html.erb +79 -4
  119. data/lib/docyard/templates/partials/_icon_library.html.erb +8 -0
  120. data/lib/docyard/templates/partials/_page_actions.html.erb +21 -0
  121. data/lib/docyard/templates/partials/_scripts.html.erb +6 -3
  122. data/lib/docyard/templates/partials/_tabs.html.erb +4 -1
  123. data/lib/docyard/utils/git_info.rb +157 -0
  124. data/lib/docyard/utils/hash_utils.rb +31 -0
  125. data/lib/docyard/utils/html_helpers.rb +8 -0
  126. data/lib/docyard/utils/logging.rb +44 -3
  127. data/lib/docyard/utils/path_resolver.rb +0 -10
  128. data/lib/docyard/utils/path_utils.rb +73 -0
  129. data/lib/docyard/version.rb +1 -1
  130. data/lib/docyard.rb +2 -2
  131. metadata +77 -47
  132. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  133. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  134. data/.github/pull_request_template.md +0 -14
  135. data/.github/workflows/ci.yml +0 -49
  136. data/.rubocop.yml +0 -42
  137. data/CODE_OF_CONDUCT.md +0 -132
  138. data/CONTRIBUTING.md +0 -55
  139. data/LICENSE.vscode-icons +0 -42
  140. data/Rakefile +0 -8
  141. data/lib/docyard/config/constants.rb +0 -31
  142. data/lib/docyard/navigation/sidebar/children_discoverer.rb +0 -51
  143. data/lib/docyard/navigation/sidebar/config_parser.rb +0 -208
  144. data/lib/docyard/navigation/sidebar/file_resolver.rb +0 -90
  145. data/lib/docyard/navigation/sidebar/file_system_scanner.rb +0 -78
  146. data/lib/docyard/navigation/sidebar/metadata_extractor.rb +0 -71
  147. data/lib/docyard/navigation/sidebar/metadata_reader.rb +0 -51
  148. data/lib/docyard/navigation/sidebar/path_prefixer.rb +0 -34
  149. data/lib/docyard/navigation/sidebar/sorter.rb +0 -21
  150. data/lib/docyard/navigation/sidebar/title_extractor.rb +0 -25
  151. data/lib/docyard/navigation/sidebar/tree_builder.rb +0 -140
  152. data/lib/docyard/rendering/icons/LICENSE.phosphor +0 -21
  153. data/lib/docyard/rendering/icons/file_types.rb +0 -79
  154. data/lib/docyard/rendering/icons/phosphor.rb +0 -93
  155. data/lib/docyard/rendering/language_mapping.rb +0 -52
  156. data/lib/docyard/templates/assets/js/reload.js +0 -98
  157. data/lib/docyard/templates/partials/_icon.html.erb +0 -1
  158. data/lib/docyard/templates/partials/_icon_file_extension.html.erb +0 -1
  159. data/sig/docyard.rbs +0 -4
@@ -1,90 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "item"
4
-
5
- module Docyard
6
- module Sidebar
7
- class FileResolver
8
- attr_reader :docs_path, :current_path, :metadata_extractor
9
-
10
- def initialize(docs_path:, current_path:, metadata_extractor:)
11
- @docs_path = docs_path
12
- @current_path = current_path
13
- @metadata_extractor = metadata_extractor
14
- end
15
-
16
- def resolve(slug, base_path, options = {})
17
- context = build_context(slug.to_s, base_path, options || {})
18
- Item.new(**context)
19
- end
20
-
21
- def build_link_item(config)
22
- Item.new(
23
- text: config["text"] || config[:text],
24
- link: config["link"] || config[:link],
25
- path: config["link"] || config[:link],
26
- icon: config["icon"] || config[:icon],
27
- target: config["target"] || config[:target] || "_blank",
28
- type: :external,
29
- section: false
30
- )
31
- end
32
-
33
- def build_file_with_children(slug:, options:, base_path:, parsed_items:, depth: 1)
34
- common_opts = metadata_extractor.extract_common_options(options)
35
- file_path = File.join(docs_path, base_path, "#{slug}.md")
36
- url_path = Utils::PathResolver.to_url(File.join(base_path, slug))
37
- is_section = section_for_depth?(common_opts[:section], depth)
38
-
39
- Item.new(
40
- slug: slug,
41
- text: common_opts[:text] || metadata_extractor.extract_file_title(file_path, slug),
42
- path: is_section ? nil : url_path,
43
- icon: common_opts[:icon],
44
- collapsed: is_section ? false : common_opts[:collapsed],
45
- items: parsed_items,
46
- active: is_section ? false : current_path == url_path,
47
- type: is_section ? :section : :file,
48
- section: is_section
49
- )
50
- end
51
-
52
- private
53
-
54
- def section_for_depth?(explicit_section, depth)
55
- return explicit_section unless explicit_section.nil?
56
-
57
- depth == 1
58
- end
59
-
60
- def build_context(slug, base_path, options)
61
- paths = resolve_paths(slug, base_path, options)
62
- frontmatter = metadata_extractor.extract_frontmatter_metadata(paths[:file])
63
-
64
- build_context_hash(slug, paths, options, frontmatter)
65
- end
66
-
67
- def resolve_paths(slug, base_path, options)
68
- file_path = File.join(docs_path, base_path, "#{slug}.md")
69
- url_path = Utils::PathResolver.to_url(File.join(base_path, slug))
70
- final_path = options["link"] || options[:link] || url_path
71
-
72
- { file: file_path, final: final_path }
73
- end
74
-
75
- def build_context_hash(slug, paths, options, frontmatter)
76
- {
77
- slug: slug,
78
- text: metadata_extractor.resolve_item_text(slug, paths[:file], options, frontmatter[:text]),
79
- path: paths[:final],
80
- icon: metadata_extractor.resolve_item_icon(options, frontmatter[:icon]),
81
- badge: frontmatter[:badge],
82
- badge_type: frontmatter[:badge_type],
83
- active: current_path == paths[:final],
84
- type: :file,
85
- section: false
86
- }
87
- end
88
- end
89
- end
90
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- class FileSystemScanner
6
- attr_reader :docs_path
7
-
8
- def initialize(docs_path)
9
- @docs_path = docs_path
10
- end
11
-
12
- def scan
13
- return [] unless File.directory?(docs_path)
14
-
15
- scan_directory(docs_path, "")
16
- end
17
-
18
- private
19
-
20
- def scan_directory(base_path, relative_path)
21
- full_path = File.join(base_path, relative_path)
22
- return [] unless File.directory?(full_path)
23
-
24
- entries = sorted_entries(full_path, relative_path)
25
- entries.map { |entry| build_item(entry, base_path, relative_path, full_path) }.compact
26
- end
27
-
28
- def sorted_entries(full_path, relative_path)
29
- Dir.children(full_path)
30
- .reject { |entry| hidden_or_ignored?(entry, relative_path) }
31
- .sort_by { |entry| sort_key(entry) }
32
- end
33
-
34
- def build_item(entry, base_path, relative_path, full_path)
35
- entry_full_path = File.join(full_path, entry)
36
- entry_relative_path = build_relative_path(relative_path, entry)
37
-
38
- if File.directory?(entry_full_path)
39
- build_directory_item(entry, entry_relative_path, base_path)
40
- elsif entry.end_with?(".md")
41
- build_file_item(entry, entry_relative_path)
42
- end
43
- end
44
-
45
- def build_relative_path(relative_path, entry)
46
- relative_path.empty? ? entry : File.join(relative_path, entry)
47
- end
48
-
49
- def build_directory_item(entry, entry_relative_path, base_path)
50
- {
51
- type: :directory,
52
- name: entry,
53
- path: entry_relative_path,
54
- children: scan_directory(base_path, entry_relative_path)
55
- }
56
- end
57
-
58
- def build_file_item(entry, entry_relative_path)
59
- {
60
- type: :file,
61
- name: entry.delete_suffix(".md"),
62
- path: entry_relative_path
63
- }
64
- end
65
-
66
- def hidden_or_ignored?(entry, relative_path)
67
- entry.start_with?(".") ||
68
- entry.start_with?("_") ||
69
- (entry == "index.md" && relative_path.empty?) ||
70
- (entry == "public" && relative_path.empty?)
71
- end
72
-
73
- def sort_key(entry)
74
- entry.downcase
75
- end
76
- end
77
- end
78
- end
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- class MetadataExtractor
6
- attr_reader :docs_path, :title_extractor
7
-
8
- def initialize(docs_path:, title_extractor:)
9
- @docs_path = docs_path
10
- @title_extractor = title_extractor
11
- end
12
-
13
- def extract_index_metadata(file_path)
14
- return { sidebar_text: nil, icon: nil } unless File.file?(file_path)
15
-
16
- markdown = Markdown.new(File.read(file_path))
17
- {
18
- sidebar_text: markdown.sidebar_text,
19
- icon: markdown.sidebar_icon
20
- }
21
- rescue StandardError
22
- { sidebar_text: nil, icon: nil }
23
- end
24
-
25
- def extract_frontmatter_metadata(file_path)
26
- return { text: nil, icon: nil, badge: nil, badge_type: nil } unless File.exist?(file_path)
27
-
28
- markdown = Markdown.new(File.read(file_path))
29
- {
30
- text: markdown.sidebar_text || markdown.title,
31
- icon: markdown.sidebar_icon,
32
- badge: markdown.sidebar_badge,
33
- badge_type: markdown.sidebar_badge_type
34
- }
35
- end
36
-
37
- def extract_file_title(file_path, slug)
38
- File.exist?(file_path) ? title_extractor.extract(file_path) : Utils::TextFormatter.titleize(slug)
39
- end
40
-
41
- def extract_common_options(options)
42
- collapsed_value = options["collapsed"]
43
- collapsed_value = options[:collapsed] if collapsed_value.nil?
44
- collapsible_value = options["collapsible"]
45
- collapsible_value = options[:collapsible] if collapsible_value.nil?
46
- collapsible_value = true if !collapsed_value.nil? && collapsible_value.nil?
47
- {
48
- text: options["text"] || options[:text],
49
- icon: options["icon"] || options[:icon],
50
- collapsed: collapsed_value,
51
- section: section_from_collapsible(collapsible_value)
52
- }
53
- end
54
-
55
- def section_from_collapsible(collapsible_value)
56
- return nil if collapsible_value.nil?
57
-
58
- collapsible_value != true
59
- end
60
-
61
- def resolve_item_text(slug, file_path, options, frontmatter_text)
62
- text = options["text"] || options[:text] || frontmatter_text
63
- text || extract_file_title(file_path, slug)
64
- end
65
-
66
- def resolve_item_icon(options, frontmatter_icon)
67
- options["icon"] || options[:icon] || frontmatter_icon
68
- end
69
- end
70
- end
71
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- class MetadataReader
6
- def extract_file_metadata(file_path)
7
- return empty_file_metadata unless File.file?(file_path)
8
-
9
- content = File.read(file_path)
10
- markdown = Markdown.new(content)
11
- {
12
- title: markdown.sidebar_text || markdown.title,
13
- icon: markdown.sidebar_icon,
14
- collapsed: markdown.sidebar_collapsed,
15
- order: markdown.sidebar_order,
16
- badge: markdown.sidebar_badge,
17
- badge_type: markdown.sidebar_badge_type
18
- }
19
- rescue StandardError
20
- empty_file_metadata
21
- end
22
-
23
- def extract_index_metadata(file_path)
24
- return empty_index_metadata unless File.file?(file_path)
25
-
26
- content = File.read(file_path)
27
- markdown = Markdown.new(content)
28
- {
29
- sidebar_text: markdown.sidebar_text,
30
- icon: markdown.sidebar_icon,
31
- collapsed: markdown.sidebar_collapsed,
32
- order: markdown.sidebar_order,
33
- badge: markdown.sidebar_badge,
34
- badge_type: markdown.sidebar_badge_type
35
- }
36
- rescue StandardError
37
- empty_index_metadata
38
- end
39
-
40
- private
41
-
42
- def empty_file_metadata
43
- { title: nil, icon: nil, collapsed: nil, order: nil, badge: nil, badge_type: nil }
44
- end
45
-
46
- def empty_index_metadata
47
- { sidebar_text: nil, icon: nil, collapsed: nil, order: nil, badge: nil, badge_type: nil }
48
- end
49
- end
50
- end
51
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- class PathPrefixer
6
- def initialize(tree, prefix)
7
- @tree = tree
8
- @prefix = prefix
9
- end
10
-
11
- def prefix
12
- return @tree if @prefix.empty?
13
-
14
- @tree.map { |item| prefix_item(item) }
15
- end
16
-
17
- private
18
-
19
- def prefix_item(item)
20
- prefixed = item.dup
21
- prefixed[:path] = prefixed_path(prefixed[:path])
22
- prefixed[:children] = self.class.new(prefixed[:children], @prefix).prefix if prefixed[:children]&.any?
23
- prefixed
24
- end
25
-
26
- def prefixed_path(path)
27
- return path if path.nil? || path.start_with?("http")
28
-
29
- path_without_slash = path.sub(%r{^/}, "")
30
- path_without_slash.empty? ? @prefix : "#{@prefix}/#{path_without_slash}"
31
- end
32
- end
33
- end
34
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- module Sorter
6
- module_function
7
-
8
- def sort_by_order(items)
9
- items.sort_by do |item|
10
- order = item[:order]
11
- title = item[:title]&.downcase || ""
12
- if order.nil?
13
- [1, title]
14
- else
15
- [0, order, title]
16
- end
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docyard
4
- module Sidebar
5
- class TitleExtractor
6
- def extract(file_path)
7
- return titleize_filename(file_path) unless File.file?(file_path)
8
-
9
- content = File.read(file_path)
10
- markdown = Markdown.new(content)
11
- markdown.title || titleize_filename(file_path)
12
- rescue StandardError => e
13
- Docyard.logger.warn "Failed to extract title from #{file_path}: #{e.message}"
14
- titleize_filename(file_path)
15
- end
16
-
17
- private
18
-
19
- def titleize_filename(file_path)
20
- filename = File.basename(file_path, Constants::MARKDOWN_EXTENSION)
21
- Utils::TextFormatter.titleize(filename)
22
- end
23
- end
24
- end
25
- end
@@ -1,140 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "sorter"
4
- require_relative "local_config_loader"
5
- require_relative "config_parser"
6
- require_relative "metadata_reader"
7
-
8
- module Docyard
9
- module Sidebar
10
- class TreeBuilder
11
- attr_reader :docs_path, :current_path, :title_extractor, :metadata_reader
12
-
13
- def initialize(docs_path:, current_path:, title_extractor: TitleExtractor.new)
14
- @docs_path = docs_path
15
- @current_path = Utils::PathResolver.normalize(current_path)
16
- @title_extractor = title_extractor
17
- @metadata_reader = MetadataReader.new
18
- end
19
-
20
- def build(file_items)
21
- transform_items(file_items, "", depth: 1)
22
- end
23
-
24
- private
25
-
26
- def transform_items(items, relative_base, depth:)
27
- transformed = items.map do |item|
28
- if item[:type] == :directory
29
- transform_directory(item, relative_base, depth: depth)
30
- else
31
- transform_file(item, relative_base)
32
- end
33
- end
34
- Sorter.sort_by_order(transformed)
35
- end
36
-
37
- def transform_directory(item, relative_base, depth:)
38
- dir_path = File.join(relative_base, item[:name])
39
- dir_context = build_directory_context(dir_path)
40
- children = build_directory_children(item, dir_path, depth)
41
-
42
- if depth == 1
43
- build_section(item, children, dir_context)
44
- else
45
- build_collapsible_group(item, children, dir_context)
46
- end
47
- end
48
-
49
- def build_directory_children(item, dir_path, depth)
50
- full_dir_path = File.join(docs_path, dir_path)
51
- local_config = LocalConfigLoader.new(full_dir_path).load
52
-
53
- if local_config
54
- build_children_from_config(local_config, dir_path)
55
- else
56
- transform_items(item[:children], dir_path, depth: depth + 1)
57
- end
58
- end
59
-
60
- def build_children_from_config(config_items, base_path)
61
- full_base_path = File.join(docs_path, base_path)
62
- parser = ConfigParser.new(config_items, docs_path: full_base_path, current_path: current_path)
63
- parser.parse.map(&:to_h)
64
- end
65
-
66
- def build_directory_context(dir_path)
67
- index_file_path = File.join(docs_path, dir_path, "index.md")
68
- has_index = File.file?(index_file_path)
69
- { index_file_path: index_file_path, has_index: has_index,
70
- url_path: has_index ? Utils::PathResolver.to_url(dir_path) : nil }
71
- end
72
-
73
- def build_section(item, children, context)
74
- filtered_children = filter_index_from_children(children, context[:url_path])
75
- metadata = context[:has_index] ? metadata_reader.extract_index_metadata(context[:index_file_path]) : {}
76
-
77
- if context[:has_index]
78
- overview = build_overview_item(metadata, context[:url_path])
79
- filtered_children = [overview] + filtered_children
80
- end
81
-
82
- build_section_hash(item, filtered_children, metadata)
83
- end
84
-
85
- def build_section_hash(item, children, metadata)
86
- { title: Utils::TextFormatter.titleize(item[:name]), path: nil, icon: metadata[:icon],
87
- active: false, type: :directory, section: true,
88
- collapsed: false, has_index: false, order: metadata[:order], children: children }
89
- end
90
-
91
- def build_collapsible_group(item, children, context)
92
- filtered_children = filter_index_from_children(children, context[:url_path])
93
- metadata = context[:has_index] ? metadata_reader.extract_index_metadata(context[:index_file_path]) : {}
94
- is_active = context[:has_index] && current_path == context[:url_path]
95
-
96
- build_collapsible_hash(item, filtered_children, context, metadata, is_active)
97
- end
98
-
99
- def build_collapsible_hash(item, children, context, metadata, is_active)
100
- { title: Utils::TextFormatter.titleize(item[:name]), path: context[:url_path],
101
- icon: metadata[:icon], active: is_active, type: :directory, section: false,
102
- collapsed: collapsible_collapsed?(children, is_active), has_index: context[:has_index],
103
- order: metadata[:order], children: children }
104
- end
105
-
106
- def collapsible_collapsed?(children, is_active)
107
- return false if is_active || active_child?(children)
108
-
109
- true
110
- end
111
-
112
- def build_overview_item(metadata, url_path)
113
- { title: metadata[:sidebar_text] || "Overview", path: url_path,
114
- icon: metadata[:icon], active: current_path == url_path, type: :file, children: [] }
115
- end
116
-
117
- def filter_index_from_children(children, index_url_path)
118
- return children unless index_url_path
119
-
120
- children.reject { |child| child[:path] == index_url_path }
121
- end
122
-
123
- def active_child?(children)
124
- children.any? { |child| child[:active] || active_child?(child[:children] || []) }
125
- end
126
-
127
- def transform_file(item, relative_base)
128
- file_path = File.join(relative_base, "#{item[:name]}#{Constants::MARKDOWN_EXTENSION}")
129
- full_file_path = File.join(docs_path, file_path)
130
- url_path = Utils::PathResolver.to_url(file_path.delete_suffix(Constants::MARKDOWN_EXTENSION))
131
- metadata = metadata_reader.extract_file_metadata(full_file_path)
132
-
133
- { title: metadata[:title] || title_extractor.extract(full_file_path),
134
- path: url_path, icon: metadata[:icon], active: current_path == url_path,
135
- type: :file, order: metadata[:order], badge: metadata[:badge],
136
- badge_type: metadata[:badge_type], children: [] }
137
- end
138
- end
139
- end
140
- end
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2020-2023 Phosphor Icons
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.