docyard 0.7.0 → 0.9.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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/CHANGELOG.md +43 -1
  4. data/lib/docyard/build/asset_bundler.rb +22 -7
  5. data/lib/docyard/build/file_copier.rb +49 -27
  6. data/lib/docyard/build/sitemap_generator.rb +6 -6
  7. data/lib/docyard/build/static_generator.rb +85 -12
  8. data/lib/docyard/builder.rb +6 -6
  9. data/lib/docyard/components/aliases.rb +12 -0
  10. data/lib/docyard/components/processors/abbreviation_processor.rb +72 -0
  11. data/lib/docyard/components/processors/accordion_processor.rb +81 -0
  12. data/lib/docyard/components/processors/badge_processor.rb +72 -0
  13. data/lib/docyard/components/processors/callout_processor.rb +8 -2
  14. data/lib/docyard/components/processors/cards_processor.rb +100 -0
  15. data/lib/docyard/components/processors/code_block_options_preprocessor.rb +23 -2
  16. data/lib/docyard/components/processors/code_block_processor.rb +6 -0
  17. data/lib/docyard/components/processors/code_group_processor.rb +198 -0
  18. data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +6 -1
  19. data/lib/docyard/components/processors/custom_anchor_processor.rb +42 -0
  20. data/lib/docyard/components/processors/file_tree_processor.rb +151 -0
  21. data/lib/docyard/components/processors/image_caption_processor.rb +96 -0
  22. data/lib/docyard/components/processors/include_processor.rb +86 -0
  23. data/lib/docyard/components/processors/steps_processor.rb +89 -0
  24. data/lib/docyard/components/processors/tabs_processor.rb +9 -1
  25. data/lib/docyard/components/processors/tooltip_processor.rb +57 -0
  26. data/lib/docyard/components/processors/video_embed_processor.rb +196 -0
  27. data/lib/docyard/components/support/code_group/html_builder.rb +122 -0
  28. data/lib/docyard/components/support/markdown_code_block_helper.rb +56 -0
  29. data/lib/docyard/config/branding_resolver.rb +121 -17
  30. data/lib/docyard/config/constants.rb +6 -4
  31. data/lib/docyard/config/logo_detector.rb +39 -0
  32. data/lib/docyard/config/validator.rb +122 -99
  33. data/lib/docyard/config.rb +40 -42
  34. data/lib/docyard/initializer.rb +15 -76
  35. data/lib/docyard/navigation/breadcrumb_builder.rb +133 -0
  36. data/lib/docyard/navigation/prev_next_builder.rb +4 -1
  37. data/lib/docyard/navigation/sidebar/children_discoverer.rb +51 -0
  38. data/lib/docyard/navigation/sidebar/config_parser.rb +136 -108
  39. data/lib/docyard/navigation/sidebar/file_resolver.rb +90 -0
  40. data/lib/docyard/navigation/sidebar/file_system_scanner.rb +2 -1
  41. data/lib/docyard/navigation/sidebar/item.rb +50 -7
  42. data/lib/docyard/navigation/sidebar/local_config_loader.rb +51 -0
  43. data/lib/docyard/navigation/sidebar/metadata_extractor.rb +71 -0
  44. data/lib/docyard/navigation/sidebar/metadata_reader.rb +51 -0
  45. data/lib/docyard/navigation/sidebar/path_prefixer.rb +34 -0
  46. data/lib/docyard/navigation/sidebar/renderer.rb +60 -38
  47. data/lib/docyard/navigation/sidebar/sorter.rb +21 -0
  48. data/lib/docyard/navigation/sidebar/tree_builder.rb +100 -26
  49. data/lib/docyard/navigation/sidebar/tree_filter.rb +55 -0
  50. data/lib/docyard/navigation/sidebar_builder.rb +105 -36
  51. data/lib/docyard/rendering/icon_helpers.rb +13 -0
  52. data/lib/docyard/rendering/icons/phosphor.rb +26 -1
  53. data/lib/docyard/rendering/markdown.rb +29 -1
  54. data/lib/docyard/rendering/renderer.rb +75 -34
  55. data/lib/docyard/rendering/template_resolver.rb +172 -0
  56. data/lib/docyard/routing/fallback_resolver.rb +92 -0
  57. data/lib/docyard/search/build_indexer.rb +1 -1
  58. data/lib/docyard/search/dev_indexer.rb +51 -6
  59. data/lib/docyard/search/pagefind_support.rb +2 -0
  60. data/lib/docyard/server/asset_handler.rb +25 -19
  61. data/lib/docyard/server/pagefind_handler.rb +63 -0
  62. data/lib/docyard/server/preview_server.rb +1 -1
  63. data/lib/docyard/server/rack_application.rb +81 -64
  64. data/lib/docyard/templates/assets/css/code.css +18 -51
  65. data/lib/docyard/templates/assets/css/components/abbreviation.css +86 -0
  66. data/lib/docyard/templates/assets/css/components/accordion.css +138 -0
  67. data/lib/docyard/templates/assets/css/components/badges.css +47 -0
  68. data/lib/docyard/templates/assets/css/components/banner.css +202 -0
  69. data/lib/docyard/templates/assets/css/components/breadcrumbs.css +143 -0
  70. data/lib/docyard/templates/assets/css/components/callout.css +67 -67
  71. data/lib/docyard/templates/assets/css/components/cards.css +100 -0
  72. data/lib/docyard/templates/assets/css/components/code-block.css +190 -282
  73. data/lib/docyard/templates/assets/css/components/code-group.css +281 -0
  74. data/lib/docyard/templates/assets/css/components/figure.css +22 -0
  75. data/lib/docyard/templates/assets/css/components/file-tree.css +124 -0
  76. data/lib/docyard/templates/assets/css/components/heading-anchor.css +36 -15
  77. data/lib/docyard/templates/assets/css/components/icon.css +0 -1
  78. data/lib/docyard/templates/assets/css/components/lightbox.css +65 -0
  79. data/lib/docyard/templates/assets/css/components/logo.css +0 -2
  80. data/lib/docyard/templates/assets/css/components/nav-menu.css +237 -0
  81. data/lib/docyard/templates/assets/css/components/navigation.css +193 -167
  82. data/lib/docyard/templates/assets/css/components/prev-next.css +68 -48
  83. data/lib/docyard/templates/assets/css/components/search.css +186 -174
  84. data/lib/docyard/templates/assets/css/components/steps.css +122 -0
  85. data/lib/docyard/templates/assets/css/components/tab-bar.css +163 -0
  86. data/lib/docyard/templates/assets/css/components/table-of-contents.css +127 -114
  87. data/lib/docyard/templates/assets/css/components/tabs.css +119 -160
  88. data/lib/docyard/templates/assets/css/components/theme-toggle.css +48 -44
  89. data/lib/docyard/templates/assets/css/components/tooltip.css +113 -0
  90. data/lib/docyard/templates/assets/css/components/video.css +41 -0
  91. data/lib/docyard/templates/assets/css/landing.css +815 -0
  92. data/lib/docyard/templates/assets/css/layout.css +489 -87
  93. data/lib/docyard/templates/assets/css/main.css +1 -3
  94. data/lib/docyard/templates/assets/css/markdown.css +113 -93
  95. data/lib/docyard/templates/assets/css/reset.css +0 -3
  96. data/lib/docyard/templates/assets/css/typography.css +43 -41
  97. data/lib/docyard/templates/assets/css/variables.css +268 -208
  98. data/lib/docyard/templates/assets/favicon.svg +7 -8
  99. data/lib/docyard/templates/assets/fonts/Inter-Variable.ttf +0 -0
  100. data/lib/docyard/templates/assets/js/components/abbreviation.js +85 -0
  101. data/lib/docyard/templates/assets/js/components/banner.js +81 -0
  102. data/lib/docyard/templates/assets/js/components/code-block.js +24 -42
  103. data/lib/docyard/templates/assets/js/components/code-group.js +283 -0
  104. data/lib/docyard/templates/assets/js/components/file-tree.js +39 -0
  105. data/lib/docyard/templates/assets/js/components/heading-anchor.js +26 -24
  106. data/lib/docyard/templates/assets/js/components/lightbox.js +72 -0
  107. data/lib/docyard/templates/assets/js/components/navigation.js +181 -70
  108. data/lib/docyard/templates/assets/js/components/search.js +0 -75
  109. data/lib/docyard/templates/assets/js/components/sidebar-toggle.js +29 -0
  110. data/lib/docyard/templates/assets/js/components/tab-navigation.js +145 -0
  111. data/lib/docyard/templates/assets/js/components/table-of-contents.js +153 -66
  112. data/lib/docyard/templates/assets/js/components/tabs.js +31 -69
  113. data/lib/docyard/templates/assets/js/components/tooltip.js +118 -0
  114. data/lib/docyard/templates/assets/js/theme.js +0 -3
  115. data/lib/docyard/templates/assets/logo-dark.svg +8 -2
  116. data/lib/docyard/templates/assets/logo.svg +7 -4
  117. data/lib/docyard/templates/config/docyard.yml.erb +37 -34
  118. data/lib/docyard/templates/errors/404.html.erb +1 -1
  119. data/lib/docyard/templates/errors/500.html.erb +1 -1
  120. data/lib/docyard/templates/layouts/default.html.erb +19 -67
  121. data/lib/docyard/templates/layouts/splash.html.erb +177 -0
  122. data/lib/docyard/templates/partials/_accordion.html.erb +9 -0
  123. data/lib/docyard/templates/partials/_banner.html.erb +27 -0
  124. data/lib/docyard/templates/partials/_breadcrumbs.html.erb +24 -0
  125. data/lib/docyard/templates/partials/_card.html.erb +23 -0
  126. data/lib/docyard/templates/partials/_code_block.html.erb +5 -3
  127. data/lib/docyard/templates/partials/_doc_footer.html.erb +25 -0
  128. data/lib/docyard/templates/partials/_features.html.erb +15 -0
  129. data/lib/docyard/templates/partials/_footer.html.erb +42 -0
  130. data/lib/docyard/templates/partials/_head.html.erb +22 -0
  131. data/lib/docyard/templates/partials/_header.html.erb +49 -0
  132. data/lib/docyard/templates/partials/_heading_anchor.html.erb +3 -1
  133. data/lib/docyard/templates/partials/_hero.html.erb +27 -0
  134. data/lib/docyard/templates/partials/_nav_group.html.erb +31 -11
  135. data/lib/docyard/templates/partials/_nav_leaf.html.erb +4 -1
  136. data/lib/docyard/templates/partials/_nav_menu.html.erb +42 -0
  137. data/lib/docyard/templates/partials/_nav_nested_section.html.erb +11 -0
  138. data/lib/docyard/templates/partials/_nav_section.html.erb +1 -1
  139. data/lib/docyard/templates/partials/_prev_next.html.erb +8 -2
  140. data/lib/docyard/templates/partials/_scripts.html.erb +7 -0
  141. data/lib/docyard/templates/partials/_search_modal.html.erb +2 -6
  142. data/lib/docyard/templates/partials/_search_trigger.html.erb +2 -6
  143. data/lib/docyard/templates/partials/_sidebar.html.erb +21 -4
  144. data/lib/docyard/templates/partials/_step.html.erb +14 -0
  145. data/lib/docyard/templates/partials/_tab_bar.html.erb +25 -0
  146. data/lib/docyard/templates/partials/_table_of_contents.html.erb +12 -12
  147. data/lib/docyard/templates/partials/_table_of_contents_toggle.html.erb +1 -3
  148. data/lib/docyard/templates/partials/_tabs.html.erb +2 -2
  149. data/lib/docyard/templates/partials/_theme_toggle.html.erb +2 -11
  150. data/lib/docyard/version.rb +1 -1
  151. metadata +70 -5
  152. data/lib/docyard/templates/markdown/getting-started/installation.md.erb +0 -77
  153. data/lib/docyard/templates/markdown/guides/configuration.md.erb +0 -202
  154. data/lib/docyard/templates/markdown/guides/markdown-features.md.erb +0 -247
  155. data/lib/docyard/templates/markdown/index.md.erb +0 -82
@@ -9,21 +9,22 @@ module Docyard
9
9
  end
10
10
 
11
11
  def validate!
12
- validate_site_section
12
+ validate_top_level
13
13
  validate_branding_section
14
+ validate_socials_section
15
+ validate_tabs_section
14
16
  validate_build_section
15
- validate_markdown_section
17
+ validate_search_section
18
+ validate_navigation_section
16
19
 
17
20
  raise ConfigError, format_errors if @errors.any?
18
21
  end
19
22
 
20
23
  private
21
24
 
22
- def validate_site_section
23
- site = @config["site"]
24
-
25
- validate_string(site["title"], "site.title")
26
- validate_string(site["description"], "site.description")
25
+ def validate_top_level
26
+ validate_string(@config["title"], "title")
27
+ validate_string(@config["description"], "description")
27
28
  end
28
29
 
29
30
  def validate_branding_section
@@ -31,127 +32,141 @@ module Docyard
31
32
  return unless branding
32
33
 
33
34
  validate_file_path_or_url(branding["logo"], "branding.logo")
34
- validate_file_path_or_url(branding["logo_dark"], "branding.logo_dark")
35
35
  validate_file_path_or_url(branding["favicon"], "branding.favicon")
36
+ validate_boolean(branding["credits"], "branding.credits") if branding.key?("credits")
37
+ end
38
+
39
+ def validate_socials_section
40
+ socials = @config["socials"]
41
+ return unless socials
42
+ return add_hash_error("socials") unless socials.is_a?(Hash)
43
+
44
+ socials.each { |platform, url| validate_url(url, "socials.#{platform}") unless platform == "custom" }
45
+ validate_custom_socials(socials["custom"]) if socials.key?("custom")
46
+ end
47
+
48
+ def validate_custom_socials(custom)
49
+ return if custom.nil?
50
+ return add_array_error("socials.custom") unless custom.is_a?(Array)
36
51
 
37
- appearance = branding["appearance"] || {}
38
- validate_boolean(appearance["logo"], "branding.appearance.logo")
39
- validate_boolean(appearance["title"], "branding.appearance.title")
52
+ custom.each_with_index do |item, index|
53
+ validate_string(item["icon"], "socials.custom[#{index}].icon")
54
+ validate_url(item["href"], "socials.custom[#{index}].href")
55
+ end
56
+ end
57
+
58
+ def validate_tabs_section
59
+ tabs = @config["tabs"]
60
+ return unless tabs
61
+ return add_array_error("tabs") unless tabs.is_a?(Array)
62
+
63
+ tabs.each_with_index do |tab, index|
64
+ validate_string(tab["text"], "tabs[#{index}].text")
65
+ validate_string(tab["href"], "tabs[#{index}].href")
66
+ validate_boolean(tab["external"], "tabs[#{index}].external") if tab.key?("external")
67
+ end
40
68
  end
41
69
 
42
70
  def validate_build_section
43
71
  build = @config["build"]
72
+ return unless build
44
73
 
45
- validate_string(build["output_dir"], "build.output_dir")
46
- validate_no_slashes(build["output_dir"], "build.output_dir")
47
- validate_string(build["base_url"], "build.base_url")
48
- validate_starts_with_slash(build["base_url"], "build.base_url")
49
- validate_boolean(build["clean"], "build.clean")
74
+ validate_string(build["output"], "build.output")
75
+ validate_no_slashes(build["output"], "build.output")
76
+ validate_string(build["base"], "build.base")
77
+ validate_starts_with_slash(build["base"], "build.base")
50
78
  end
51
79
 
52
- def validate_markdown_section
53
- markdown = @config["markdown"]
54
- return unless markdown
80
+ def validate_search_section
81
+ search = @config["search"]
82
+ return unless search
55
83
 
56
- validate_boolean(markdown["lineNumbers"], "markdown.lineNumbers") if markdown.key?("lineNumbers")
84
+ validate_boolean(search["enabled"], "search.enabled") if search.key?("enabled")
85
+ validate_string(search["placeholder"], "search.placeholder") if search.key?("placeholder")
86
+ validate_array(search["exclude"], "search.exclude") if search.key?("exclude")
57
87
  end
58
88
 
59
- def validate_string(value, field_name)
60
- return if value.nil?
61
- return if value.is_a?(String)
89
+ def validate_navigation_section
90
+ cta = @config.dig("navigation", "cta")
91
+ return if cta.nil?
92
+ return add_array_error("navigation.cta") unless cta.is_a?(Array)
62
93
 
63
- add_error(
64
- field: field_name,
65
- error: "must be a string",
66
- got: value.class.name,
67
- fix: "Change to a string value"
68
- )
94
+ validate_cta_max_count(cta)
95
+ validate_cta_items(cta)
69
96
  end
70
97
 
71
- def validate_boolean(value, field_name)
72
- return if [true, false].include?(value)
73
-
74
- add_error(
75
- field: field_name,
76
- error: "must be true or false",
77
- got: value.inspect,
78
- fix: "Change to true or false"
79
- )
98
+ def validate_cta_items(cta)
99
+ cta.each_with_index do |item, idx|
100
+ validate_string(item["text"], "navigation.cta[#{idx}].text")
101
+ validate_string(item["href"], "navigation.cta[#{idx}].href")
102
+ validate_cta_variant(item["variant"], idx) if item.key?("variant")
103
+ validate_boolean(item["external"], "navigation.cta[#{idx}].external") if item.key?("external")
104
+ end
80
105
  end
81
106
 
82
- def validate_file_path(value, field_name)
83
- return if value.nil?
84
- return add_file_path_type_error(value, field_name) unless value.is_a?(String)
107
+ def validate_cta_max_count(cta)
108
+ return if cta.length <= 2
85
109
 
86
- file_path = if File.absolute_path?(value)
87
- value
88
- else
89
- File.join("docs", value)
90
- end
110
+ add_error(field: "navigation.cta", error: "maximum 2 CTAs allowed",
111
+ got: "#{cta.length} items", fix: "Remove extra CTA items to have at most 2")
112
+ end
91
113
 
92
- return if File.exist?(file_path)
114
+ def validate_cta_variant(variant, idx)
115
+ return if variant.nil? || %w[primary secondary].include?(variant)
93
116
 
94
- add_file_not_found_error(value, field_name)
117
+ add_error(field: "navigation.cta[#{idx}].variant", error: "must be 'primary' or 'secondary'",
118
+ got: variant, fix: "Change to 'primary' or 'secondary'")
95
119
  end
96
120
 
97
- def add_file_path_type_error(value, field_name)
98
- add_error(
99
- field: field_name,
100
- error: "must be a file path (string)",
101
- got: value.class.name,
102
- fix: "Change to a string file path"
103
- )
121
+ def validate_string(value, field_name)
122
+ return if value.nil? || value.is_a?(String)
123
+
124
+ add_error(field: field_name, error: "must be a string", got: value.class.name, fix: "Change to a string value")
104
125
  end
105
126
 
106
- def add_file_not_found_error(value, field_name)
107
- add_error(
108
- field: field_name,
109
- error: "file not found",
110
- got: value,
111
- fix: "Place the file in docs/ directory and use a relative path (e.g., 'assets/logo.svg')"
112
- )
127
+ def validate_boolean(value, field_name)
128
+ return if [true, false].include?(value)
129
+
130
+ add_error(field: field_name, error: "must be true or false", got: value.inspect, fix: "Change to true or false")
113
131
  end
114
132
 
115
- def validate_no_slashes(value, field_name)
116
- return if value.nil?
117
- return unless value.is_a?(String)
118
- return unless value.include?("/") || value.include?("\\")
133
+ def validate_url(value, field_name)
134
+ return if value.nil? || value.is_a?(String)
119
135
 
120
- add_error(
121
- field: field_name,
122
- error: "cannot contain slashes",
123
- got: value,
124
- fix: "Use a simple directory name like 'dist' or '_site'"
125
- )
136
+ add_error(field: field_name, error: "must be a URL string",
137
+ got: value.class.name, fix: "Change to a URL string")
126
138
  end
127
139
 
128
- def validate_starts_with_slash(value, field_name)
129
- return if value.nil?
130
- return if value.start_with?("/")
140
+ def validate_array(value, field_name)
141
+ return if value.nil? || value.is_a?(Array)
131
142
 
132
- add_error(
133
- field: field_name,
134
- error: "must start with /",
135
- got: value,
136
- fix: "Change to '/#{value}'"
137
- )
143
+ add_array_error(field_name)
138
144
  end
139
145
 
140
146
  def validate_file_path_or_url(value, field_name)
141
147
  return if value.nil?
142
- return add_file_path_type_error(value, field_name) unless value.is_a?(String)
143
-
148
+ return add_type_error(field_name, "file path or URL (string)", value.class.name) unless value.is_a?(String)
144
149
  return if url?(value)
145
150
 
146
- file_path = if File.absolute_path?(value)
147
- value
148
- else
149
- File.join("docs", value)
150
- end
151
-
151
+ file_path = File.absolute_path?(value) ? value : File.join("docs/public", value)
152
152
  return if File.exist?(file_path)
153
153
 
154
- add_file_not_found_error(value, field_name)
154
+ add_error(field: field_name, error: "file not found", got: value,
155
+ fix: "Place the file in docs/public/ directory (e.g., 'logo.svg' for docs/public/logo.svg)")
156
+ end
157
+
158
+ def validate_no_slashes(value, field_name)
159
+ return if value.nil? || !value.is_a?(String)
160
+ return unless value.include?("/") || value.include?("\\")
161
+
162
+ add_error(field: field_name, error: "cannot contain slashes", got: value,
163
+ fix: "Use a simple directory name like 'dist' or '_site'")
164
+ end
165
+
166
+ def validate_starts_with_slash(value, field_name)
167
+ return if value.nil? || value.start_with?("/")
168
+
169
+ add_error(field: field_name, error: "must start with /", got: value, fix: "Change to '/#{value}'")
155
170
  end
156
171
 
157
172
  def url?(value)
@@ -162,17 +177,25 @@ module Docyard
162
177
  @errors << error_data
163
178
  end
164
179
 
165
- def format_errors
166
- message = "Error in docyard.yml:\n\n"
180
+ def add_type_error(field, expected, got)
181
+ add_error(field: field, error: "must be a #{expected}", got: got, fix: "Change to a #{expected}")
182
+ end
167
183
 
168
- @errors.each do |err|
169
- message += " Field: #{err[:field]}\n"
170
- message += " Error: #{err[:error]}\n"
171
- message += " Got: #{err[:got]}\n"
172
- message += " Fix: #{err[:fix]}\n\n"
173
- end
184
+ def add_hash_error(field)
185
+ add_error(field: field, error: "must be a hash", got: @config[field].class.name,
186
+ fix: "Change to a hash with platform names as keys and URLs as values")
187
+ end
174
188
 
175
- message.chomp
189
+ def add_array_error(field)
190
+ value = field.split(".").reduce(@config) { |h, k| h&.[](k) }
191
+ add_error(field: field, error: "must be an array", got: value.class.name, fix: "Change to an array")
192
+ end
193
+
194
+ def format_errors
195
+ errors_text = @errors.map do |err|
196
+ " Field: #{err[:field]}\n Error: #{err[:error]}\n Got: #{err[:got]}\n Fix: #{err[:fix]}"
197
+ end.join("\n\n")
198
+ "Error in docyard.yml:\n\n#{errors_text}"
176
199
  end
177
200
  end
178
201
  end
@@ -7,42 +7,30 @@ require_relative "config/constants"
7
7
  module Docyard
8
8
  class Config
9
9
  DEFAULT_CONFIG = {
10
- "site" => {
11
- "title" => Constants::DEFAULT_SITE_TITLE,
12
- "description" => ""
13
- },
10
+ "title" => Constants::DEFAULT_SITE_TITLE,
11
+ "description" => "",
14
12
  "branding" => {
15
13
  "logo" => nil,
16
- "logo_dark" => nil,
17
14
  "favicon" => nil,
18
- "appearance" => {
19
- "logo" => true,
20
- "title" => true
21
- }
15
+ "credits" => true,
16
+ "copyright" => nil
22
17
  },
18
+ "socials" => {},
19
+ "tabs" => [],
23
20
  "build" => {
24
- "output_dir" => "dist",
25
- "base_url" => "/",
26
- "clean" => true
27
- },
28
- "sidebar" => {
29
- "items" => []
30
- },
31
- "navigation" => {
32
- "footer" => {
33
- "enabled" => true,
34
- "prev_text" => "Previous",
35
- "next_text" => "Next"
36
- }
37
- },
38
- "markdown" => {
39
- "lineNumbers" => false
21
+ "output" => "dist",
22
+ "base" => "/"
40
23
  },
41
24
  "search" => {
42
25
  "enabled" => true,
43
- "placeholder" => "Search documentation...",
26
+ "placeholder" => "Search...",
44
27
  "exclude" => []
45
- }
28
+ },
29
+ "navigation" => {
30
+ "cta" => [],
31
+ "breadcrumbs" => true
32
+ },
33
+ "announcement" => nil
46
34
  }.freeze
47
35
 
48
36
  attr_reader :data, :file_path
@@ -62,32 +50,40 @@ module Docyard
62
50
  File.exist?(file_path)
63
51
  end
64
52
 
65
- def site
66
- @site ||= ConfigSection.new(data["site"])
53
+ def title
54
+ data["title"]
55
+ end
56
+
57
+ def description
58
+ data["description"]
67
59
  end
68
60
 
69
61
  def branding
70
62
  @branding ||= ConfigSection.new(data["branding"])
71
63
  end
72
64
 
65
+ def socials
66
+ data["socials"]
67
+ end
68
+
69
+ def tabs
70
+ data["tabs"]
71
+ end
72
+
73
73
  def build
74
74
  @build ||= ConfigSection.new(data["build"])
75
75
  end
76
76
 
77
- def sidebar
78
- @sidebar ||= ConfigSection.new(data["sidebar"])
77
+ def search
78
+ @search ||= ConfigSection.new(data["search"])
79
79
  end
80
80
 
81
81
  def navigation
82
82
  @navigation ||= ConfigSection.new(data["navigation"])
83
83
  end
84
84
 
85
- def markdown
86
- @markdown ||= ConfigSection.new(data["markdown"])
87
- end
88
-
89
- def search
90
- @search ||= ConfigSection.new(data["search"])
85
+ def announcement
86
+ @announcement ||= data["announcement"] ? ConfigSection.new(data["announcement"]) : nil
91
87
  end
92
88
 
93
89
  private
@@ -122,7 +118,13 @@ module Docyard
122
118
  end
123
119
 
124
120
  def deep_dup(hash)
125
- hash.transform_values { |value| value.is_a?(Hash) ? deep_dup(value) : value }
121
+ hash.transform_values do |value|
122
+ case value
123
+ when Hash then deep_dup(value)
124
+ when Array then value.map { |v| v.is_a?(Hash) ? deep_dup(v) : v }
125
+ else value
126
+ end
127
+ end
126
128
  end
127
129
 
128
130
  def build_yaml_error_message(error)
@@ -143,10 +145,6 @@ module Docyard
143
145
  @data = data || {}
144
146
  end
145
147
 
146
- def appearance
147
- @data["appearance"]
148
- end
149
-
150
148
  def method_missing(method, *args)
151
149
  return @data[method.to_s] if args.empty?
152
150
 
@@ -5,16 +5,8 @@ require "fileutils"
5
5
  module Docyard
6
6
  class Initializer
7
7
  DOCS_DIR = "docs"
8
- TEMPLATE_DIR = File.join(__dir__, "templates", "markdown")
9
8
  CONFIG_TEMPLATE_DIR = File.join(__dir__, "templates", "config")
10
9
 
11
- TEMPLATES = {
12
- "index.md" => "index.md.erb",
13
- "getting-started/installation.md" => "getting-started/installation.md.erb",
14
- "guides/markdown-features.md" => "guides/markdown-features.md.erb",
15
- "guides/configuration.md" => "guides/configuration.md.erb"
16
- }.freeze
17
-
18
10
  def initialize(path = ".")
19
11
  @path = path
20
12
  @docs_path = File.join(@path, DOCS_DIR)
@@ -39,23 +31,22 @@ module Docyard
39
31
 
40
32
  def create_structure
41
33
  FileUtils.mkdir_p(@docs_path)
42
-
43
- TEMPLATES.each do |output_name, template_name|
44
- copy_template(template_name, output_name)
45
- end
46
-
34
+ create_index_file
47
35
  create_example_config
48
36
  end
49
37
 
50
- def copy_template(template_name, output_name)
51
- template_path = File.join(TEMPLATE_DIR, template_name)
52
- output_path = File.join(@docs_path, output_name)
38
+ def create_index_file
39
+ index_path = File.join(@docs_path, "index.md")
40
+ content = <<~MARKDOWN
41
+ ---
42
+ title: Welcome
43
+ ---
53
44
 
54
- output_dir = File.dirname(output_path)
55
- FileUtils.mkdir_p(output_dir) unless File.directory?(output_dir)
45
+ # Welcome to Your Documentation
56
46
 
57
- content = File.read(template_path)
58
- File.write(output_path, content)
47
+ Start writing your documentation here.
48
+ MARKDOWN
49
+ File.write(index_path, content)
59
50
  end
60
51
 
61
52
  def create_example_config
@@ -81,16 +72,16 @@ module Docyard
81
72
 
82
73
  def print_banner
83
74
  puts ""
84
- puts "┌─────────────────────────────────────────────────────────────┐"
85
- puts "│ ✓ Docyard initialized successfully │"
86
- puts "└─────────────────────────────────────────────────────────────┘"
75
+ puts "Docyard initialized successfully"
87
76
  puts ""
88
77
  end
89
78
 
90
79
  def print_created_files
91
80
  puts "Created files:"
92
81
  puts ""
93
- print_file_tree
82
+ puts " docs/"
83
+ puts " index.md"
84
+ puts " docyard.yml"
94
85
  puts ""
95
86
  end
96
87
 
@@ -99,62 +90,10 @@ module Docyard
99
90
  puts ""
100
91
  puts " Start development server:"
101
92
  puts " docyard serve"
102
- puts " → http://localhost:4200"
103
93
  puts ""
104
94
  puts " Build for production:"
105
95
  puts " docyard build"
106
96
  puts ""
107
- puts " Preview production build:"
108
- puts " docyard preview"
109
- puts ""
110
- end
111
-
112
- def print_file_tree
113
- puts " ├── docs/"
114
-
115
- grouped_files = TEMPLATES.keys.group_by { |file| File.dirname(file) }
116
- sorted_dirs = grouped_files.keys.sort
117
-
118
- sorted_dirs.each_with_index do |dir, dir_idx|
119
- print_directory_group(dir, grouped_files[dir], dir_idx == sorted_dirs.length - 1)
120
- end
121
-
122
- puts " └── docyard.yml"
123
- end
124
-
125
- def print_directory_group(dir, files, is_last_dir)
126
- sorted_files = files.sort
127
-
128
- if dir == "."
129
- print_root_files(sorted_files, is_last_dir)
130
- else
131
- print_subdirectory(dir, sorted_files, is_last_dir)
132
- end
133
- end
134
-
135
- def print_root_files(files, is_last_dir)
136
- files.each_with_index do |file, idx|
137
- is_last = idx == files.length - 1 && is_last_dir
138
- prefix = is_last ? " │ └──" : " │ ├──"
139
- puts "#{prefix} #{file}"
140
- end
141
- end
142
-
143
- def print_subdirectory(dir, files, is_last_dir)
144
- dir_prefix = is_last_dir ? " │ └──" : " │ ├──"
145
- puts "#{dir_prefix} #{dir}/"
146
-
147
- files.each_with_index do |file, idx|
148
- print_subdirectory_file(file, idx, files.length, is_last_dir)
149
- end
150
- end
151
-
152
- def print_subdirectory_file(file, idx, total, is_last_dir)
153
- is_last_file = idx == total - 1
154
- file_prefix = is_last_dir ? " │ " : " │ │ "
155
- file_prefix += is_last_file ? "└──" : "├──"
156
- basename = File.basename(file)
157
- puts "#{file_prefix} #{basename}"
158
97
  end
159
98
  end
160
99
  end