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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +43 -1
- data/lib/docyard/build/asset_bundler.rb +22 -7
- data/lib/docyard/build/file_copier.rb +49 -27
- data/lib/docyard/build/sitemap_generator.rb +6 -6
- data/lib/docyard/build/static_generator.rb +85 -12
- data/lib/docyard/builder.rb +6 -6
- data/lib/docyard/components/aliases.rb +12 -0
- data/lib/docyard/components/processors/abbreviation_processor.rb +72 -0
- data/lib/docyard/components/processors/accordion_processor.rb +81 -0
- data/lib/docyard/components/processors/badge_processor.rb +72 -0
- data/lib/docyard/components/processors/callout_processor.rb +8 -2
- data/lib/docyard/components/processors/cards_processor.rb +100 -0
- data/lib/docyard/components/processors/code_block_options_preprocessor.rb +23 -2
- data/lib/docyard/components/processors/code_block_processor.rb +6 -0
- data/lib/docyard/components/processors/code_group_processor.rb +198 -0
- data/lib/docyard/components/processors/code_snippet_import_preprocessor.rb +6 -1
- data/lib/docyard/components/processors/custom_anchor_processor.rb +42 -0
- data/lib/docyard/components/processors/file_tree_processor.rb +151 -0
- data/lib/docyard/components/processors/image_caption_processor.rb +96 -0
- data/lib/docyard/components/processors/include_processor.rb +86 -0
- data/lib/docyard/components/processors/steps_processor.rb +89 -0
- data/lib/docyard/components/processors/tabs_processor.rb +9 -1
- data/lib/docyard/components/processors/tooltip_processor.rb +57 -0
- data/lib/docyard/components/processors/video_embed_processor.rb +196 -0
- data/lib/docyard/components/support/code_group/html_builder.rb +122 -0
- data/lib/docyard/components/support/markdown_code_block_helper.rb +56 -0
- data/lib/docyard/config/branding_resolver.rb +121 -17
- data/lib/docyard/config/constants.rb +6 -4
- data/lib/docyard/config/logo_detector.rb +39 -0
- data/lib/docyard/config/validator.rb +122 -99
- data/lib/docyard/config.rb +40 -42
- data/lib/docyard/initializer.rb +15 -76
- data/lib/docyard/navigation/breadcrumb_builder.rb +133 -0
- data/lib/docyard/navigation/prev_next_builder.rb +4 -1
- data/lib/docyard/navigation/sidebar/children_discoverer.rb +51 -0
- data/lib/docyard/navigation/sidebar/config_parser.rb +136 -108
- data/lib/docyard/navigation/sidebar/file_resolver.rb +90 -0
- data/lib/docyard/navigation/sidebar/file_system_scanner.rb +2 -1
- data/lib/docyard/navigation/sidebar/item.rb +50 -7
- data/lib/docyard/navigation/sidebar/local_config_loader.rb +51 -0
- data/lib/docyard/navigation/sidebar/metadata_extractor.rb +71 -0
- data/lib/docyard/navigation/sidebar/metadata_reader.rb +51 -0
- data/lib/docyard/navigation/sidebar/path_prefixer.rb +34 -0
- data/lib/docyard/navigation/sidebar/renderer.rb +60 -38
- data/lib/docyard/navigation/sidebar/sorter.rb +21 -0
- data/lib/docyard/navigation/sidebar/tree_builder.rb +100 -26
- data/lib/docyard/navigation/sidebar/tree_filter.rb +55 -0
- data/lib/docyard/navigation/sidebar_builder.rb +105 -36
- data/lib/docyard/rendering/icon_helpers.rb +13 -0
- data/lib/docyard/rendering/icons/phosphor.rb +26 -1
- data/lib/docyard/rendering/markdown.rb +29 -1
- data/lib/docyard/rendering/renderer.rb +75 -34
- data/lib/docyard/rendering/template_resolver.rb +172 -0
- data/lib/docyard/routing/fallback_resolver.rb +92 -0
- data/lib/docyard/search/build_indexer.rb +1 -1
- data/lib/docyard/search/dev_indexer.rb +51 -6
- data/lib/docyard/search/pagefind_support.rb +2 -0
- data/lib/docyard/server/asset_handler.rb +25 -19
- data/lib/docyard/server/pagefind_handler.rb +63 -0
- data/lib/docyard/server/preview_server.rb +1 -1
- data/lib/docyard/server/rack_application.rb +81 -64
- data/lib/docyard/templates/assets/css/code.css +18 -51
- data/lib/docyard/templates/assets/css/components/abbreviation.css +86 -0
- data/lib/docyard/templates/assets/css/components/accordion.css +138 -0
- data/lib/docyard/templates/assets/css/components/badges.css +47 -0
- data/lib/docyard/templates/assets/css/components/banner.css +202 -0
- data/lib/docyard/templates/assets/css/components/breadcrumbs.css +143 -0
- data/lib/docyard/templates/assets/css/components/callout.css +67 -67
- data/lib/docyard/templates/assets/css/components/cards.css +100 -0
- data/lib/docyard/templates/assets/css/components/code-block.css +190 -282
- data/lib/docyard/templates/assets/css/components/code-group.css +281 -0
- data/lib/docyard/templates/assets/css/components/figure.css +22 -0
- data/lib/docyard/templates/assets/css/components/file-tree.css +124 -0
- data/lib/docyard/templates/assets/css/components/heading-anchor.css +36 -15
- data/lib/docyard/templates/assets/css/components/icon.css +0 -1
- data/lib/docyard/templates/assets/css/components/lightbox.css +65 -0
- data/lib/docyard/templates/assets/css/components/logo.css +0 -2
- data/lib/docyard/templates/assets/css/components/nav-menu.css +237 -0
- data/lib/docyard/templates/assets/css/components/navigation.css +193 -167
- data/lib/docyard/templates/assets/css/components/prev-next.css +68 -48
- data/lib/docyard/templates/assets/css/components/search.css +186 -174
- data/lib/docyard/templates/assets/css/components/steps.css +122 -0
- data/lib/docyard/templates/assets/css/components/tab-bar.css +163 -0
- data/lib/docyard/templates/assets/css/components/table-of-contents.css +127 -114
- data/lib/docyard/templates/assets/css/components/tabs.css +119 -160
- data/lib/docyard/templates/assets/css/components/theme-toggle.css +48 -44
- data/lib/docyard/templates/assets/css/components/tooltip.css +113 -0
- data/lib/docyard/templates/assets/css/components/video.css +41 -0
- data/lib/docyard/templates/assets/css/landing.css +815 -0
- data/lib/docyard/templates/assets/css/layout.css +489 -87
- data/lib/docyard/templates/assets/css/main.css +1 -3
- data/lib/docyard/templates/assets/css/markdown.css +113 -93
- data/lib/docyard/templates/assets/css/reset.css +0 -3
- data/lib/docyard/templates/assets/css/typography.css +43 -41
- data/lib/docyard/templates/assets/css/variables.css +268 -208
- data/lib/docyard/templates/assets/favicon.svg +7 -8
- data/lib/docyard/templates/assets/fonts/Inter-Variable.ttf +0 -0
- data/lib/docyard/templates/assets/js/components/abbreviation.js +85 -0
- data/lib/docyard/templates/assets/js/components/banner.js +81 -0
- data/lib/docyard/templates/assets/js/components/code-block.js +24 -42
- data/lib/docyard/templates/assets/js/components/code-group.js +283 -0
- data/lib/docyard/templates/assets/js/components/file-tree.js +39 -0
- data/lib/docyard/templates/assets/js/components/heading-anchor.js +26 -24
- data/lib/docyard/templates/assets/js/components/lightbox.js +72 -0
- data/lib/docyard/templates/assets/js/components/navigation.js +181 -70
- data/lib/docyard/templates/assets/js/components/search.js +0 -75
- data/lib/docyard/templates/assets/js/components/sidebar-toggle.js +29 -0
- data/lib/docyard/templates/assets/js/components/tab-navigation.js +145 -0
- data/lib/docyard/templates/assets/js/components/table-of-contents.js +153 -66
- data/lib/docyard/templates/assets/js/components/tabs.js +31 -69
- data/lib/docyard/templates/assets/js/components/tooltip.js +118 -0
- data/lib/docyard/templates/assets/js/theme.js +0 -3
- data/lib/docyard/templates/assets/logo-dark.svg +8 -2
- data/lib/docyard/templates/assets/logo.svg +7 -4
- data/lib/docyard/templates/config/docyard.yml.erb +37 -34
- data/lib/docyard/templates/errors/404.html.erb +1 -1
- data/lib/docyard/templates/errors/500.html.erb +1 -1
- data/lib/docyard/templates/layouts/default.html.erb +19 -67
- data/lib/docyard/templates/layouts/splash.html.erb +177 -0
- data/lib/docyard/templates/partials/_accordion.html.erb +9 -0
- data/lib/docyard/templates/partials/_banner.html.erb +27 -0
- data/lib/docyard/templates/partials/_breadcrumbs.html.erb +24 -0
- data/lib/docyard/templates/partials/_card.html.erb +23 -0
- data/lib/docyard/templates/partials/_code_block.html.erb +5 -3
- data/lib/docyard/templates/partials/_doc_footer.html.erb +25 -0
- data/lib/docyard/templates/partials/_features.html.erb +15 -0
- data/lib/docyard/templates/partials/_footer.html.erb +42 -0
- data/lib/docyard/templates/partials/_head.html.erb +22 -0
- data/lib/docyard/templates/partials/_header.html.erb +49 -0
- data/lib/docyard/templates/partials/_heading_anchor.html.erb +3 -1
- data/lib/docyard/templates/partials/_hero.html.erb +27 -0
- data/lib/docyard/templates/partials/_nav_group.html.erb +31 -11
- data/lib/docyard/templates/partials/_nav_leaf.html.erb +4 -1
- data/lib/docyard/templates/partials/_nav_menu.html.erb +42 -0
- data/lib/docyard/templates/partials/_nav_nested_section.html.erb +11 -0
- data/lib/docyard/templates/partials/_nav_section.html.erb +1 -1
- data/lib/docyard/templates/partials/_prev_next.html.erb +8 -2
- data/lib/docyard/templates/partials/_scripts.html.erb +7 -0
- data/lib/docyard/templates/partials/_search_modal.html.erb +2 -6
- data/lib/docyard/templates/partials/_search_trigger.html.erb +2 -6
- data/lib/docyard/templates/partials/_sidebar.html.erb +21 -4
- data/lib/docyard/templates/partials/_step.html.erb +14 -0
- data/lib/docyard/templates/partials/_tab_bar.html.erb +25 -0
- data/lib/docyard/templates/partials/_table_of_contents.html.erb +12 -12
- data/lib/docyard/templates/partials/_table_of_contents_toggle.html.erb +1 -3
- data/lib/docyard/templates/partials/_tabs.html.erb +2 -2
- data/lib/docyard/templates/partials/_theme_toggle.html.erb +2 -11
- data/lib/docyard/version.rb +1 -1
- metadata +70 -5
- data/lib/docyard/templates/markdown/getting-started/installation.md.erb +0 -77
- data/lib/docyard/templates/markdown/guides/configuration.md.erb +0 -202
- data/lib/docyard/templates/markdown/guides/markdown-features.md.erb +0 -247
- data/lib/docyard/templates/markdown/index.md.erb +0 -82
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51b2adfdb7f77c4366592cc3eea5a160fc42f85f17e44593e9bf192c3b470b48
|
|
4
|
+
data.tar.gz: 439d82b762c7bc6f485ab627365522b7e202833fc841c6b8655fb28e1e9b3e55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a66355efb4615ae4552aa8656e63078989cdec734f41670dc172936060954a065fefb1a2733e51494747ef0ab9a1dbee399b3c8f37daf2718e92ba03c315df9f
|
|
7
|
+
data.tar.gz: be3cea82f91b60cc7e7c35be142b6c2ccd9be4323cbf1c5190987b6ba6894d4c8da38f55c141abda43211a5ac2b70d38283e1b340d62071ee5c22c87c9c35a51
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.0] - 2026-01-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Accordions** - Collapsible content sections with `:::details{title="..."}` syntax (#62)
|
|
14
|
+
- **Steps** - Numbered step-by-step instructions with `:::steps` syntax and vertical connector lines (#63)
|
|
15
|
+
- **Cards** - Grid of linked content blocks with `:::cards` and `::card{title="" icon="" href=""}` syntax (#64)
|
|
16
|
+
- **Badges** - Inline status indicators with `:badge[text]{type="success|warning|danger"}` syntax (#65)
|
|
17
|
+
- **Sidebar Badges** - Navigation labels via frontmatter `sidebar.badge` and `sidebar.badge_type` (#66)
|
|
18
|
+
- **Announcement Banner** - Dismissible top banner with optional action button via config (#56)
|
|
19
|
+
- **Markdown Inclusion** - Include content from other files with `<!--@include: ./file.md-->` syntax (#57)
|
|
20
|
+
- **Custom Anchor IDs** - Override auto-generated heading IDs with `## Heading {#custom-id}` syntax (#58)
|
|
21
|
+
- **Image Captions** - Figure elements with captions using `{caption="..."}` syntax (#59)
|
|
22
|
+
- **Video Embeds** - YouTube and Vimeo embedding with `::youtube[ID]` and `::vimeo[ID]` syntax (#60)
|
|
23
|
+
- **File Tree** - Display directory structures with icons using `filetree` code blocks (#67)
|
|
24
|
+
- **Tooltips** - Inline hover definitions with `:tooltip[term]{description="..."}` syntax (#68)
|
|
25
|
+
- **Abbreviations** - Auto-expanding terms with `*[TERM]: Definition` syntax (#68)
|
|
26
|
+
- **Code Groups** - Tabbed code blocks with `:::code-group` syntax, syncs selection across page (#70)
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- **Copy Button Overlap** - Repositioned copy button to prevent overlapping code content in non-titled blocks (#71)
|
|
30
|
+
- **Code Fence Protection** - Preprocessors now skip content inside fenced code blocks, allowing documentation to show raw syntax examples (#72)
|
|
31
|
+
|
|
32
|
+
## [0.8.0] - 2026-01-13
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- **Landing Pages** - Hero sections, feature grids, and custom footer layouts for documentation homepages (#45)
|
|
36
|
+
- **Tab Navigation** - Top-level navigation tabs for organizing documentation into sections like Guide, API, Components (#52)
|
|
37
|
+
- **Header CTAs** - Configurable call-to-action buttons in the header with primary/secondary variants (#51)
|
|
38
|
+
- **Breadcrumbs** - Path navigation with auto-truncation for deep nesting and configurable via `navigation.breadcrumbs` (#54)
|
|
39
|
+
- **Doc Page Footer** - Social icons, "Built with Docyard" attribution, and copyright text in TOC column (#55)
|
|
40
|
+
- **Auto-detect Branding** - Automatic logo and favicon detection from `docs/public/` directory (#49)
|
|
41
|
+
- **Social Icon Mapping** - 16 social platform icons with automatic platform-to-icon mapping (#55)
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
- **Sidebar Overhaul** - Per-section `_sidebar.yml` files, improved collapsible behavior, and better active state handling (#50, #53)
|
|
45
|
+
- **Config Schema** - Reorganized configuration with `branding`, `navigation`, and `socials` sections (#48)
|
|
46
|
+
- **Sidebar Convention** - Section-based sidebar configuration in `docs/<section>/_sidebar.yml` (#47)
|
|
47
|
+
- **UI Refresh** - Updated typography, spacing, and visual consistency across components (#44)
|
|
48
|
+
- **Logo Update** - New logo with cyan accent and dark mode support (#46)
|
|
49
|
+
|
|
10
50
|
## [0.7.0] - 2026-01-01
|
|
11
51
|
|
|
12
52
|
### Added
|
|
@@ -122,7 +162,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
122
162
|
- Initial gem structure
|
|
123
163
|
- Project scaffolding
|
|
124
164
|
|
|
125
|
-
[Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.
|
|
165
|
+
[Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.9.0...HEAD
|
|
166
|
+
[0.9.0]: https://github.com/sanifhimani/docyard/compare/v0.8.0...v0.9.0
|
|
167
|
+
[0.8.0]: https://github.com/sanifhimani/docyard/compare/v0.7.0...v0.8.0
|
|
126
168
|
[0.7.0]: https://github.com/sanifhimani/docyard/compare/v0.6.0...v0.7.0
|
|
127
169
|
[0.6.0]: https://github.com/sanifhimani/docyard/compare/v0.5.0...v0.6.0
|
|
128
170
|
[0.5.0]: https://github.com/sanifhimani/docyard/compare/v0.4.0...v0.5.0
|
|
@@ -35,6 +35,8 @@ module Docyard
|
|
|
35
35
|
main_css = File.read(File.join(ASSETS_PATH, "css", "main.css"))
|
|
36
36
|
css_content = resolve_css_imports(main_css)
|
|
37
37
|
minified = CSSminify.compress(css_content)
|
|
38
|
+
minified = fix_calc_whitespace(minified)
|
|
39
|
+
minified = fix_css_math_functions(minified)
|
|
38
40
|
hash = generate_hash(minified)
|
|
39
41
|
|
|
40
42
|
write_bundled_asset(minified, hash, "css")
|
|
@@ -43,6 +45,19 @@ module Docyard
|
|
|
43
45
|
hash
|
|
44
46
|
end
|
|
45
47
|
|
|
48
|
+
def fix_calc_whitespace(css)
|
|
49
|
+
css
|
|
50
|
+
.gsub(/\)\+(?!\s)/, ") + ")
|
|
51
|
+
.gsub(/\)-(?![-\s])/, ") - ")
|
|
52
|
+
.gsub(/(\d[a-z]*)\+(?=[\w(])/, '\1 + ')
|
|
53
|
+
.gsub(/([lch])\+(?=[\d.])/, '\1 + ')
|
|
54
|
+
.gsub(/([lch])-(?=[\d.])/, '\1 - ')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def fix_css_math_functions(css)
|
|
58
|
+
css.gsub(/\bmax\(0,/, "max(0px,").gsub(/\bmin\(0,/, "min(0px,)")
|
|
59
|
+
end
|
|
60
|
+
|
|
46
61
|
def resolve_css_imports(css_content)
|
|
47
62
|
css_content.gsub(/@import url\('([^']+)'\);/) do |match|
|
|
48
63
|
import_file = Regexp.last_match(1)
|
|
@@ -92,8 +107,8 @@ module Docyard
|
|
|
92
107
|
end
|
|
93
108
|
|
|
94
109
|
def update_html_references(css_hash, js_hash)
|
|
95
|
-
html_files = Dir.glob(File.join(config.build.
|
|
96
|
-
base_url = normalize_base_url(config.build.
|
|
110
|
+
html_files = Dir.glob(File.join(config.build.output, "**", "*.html"))
|
|
111
|
+
base_url = normalize_base_url(config.build.base)
|
|
97
112
|
|
|
98
113
|
html_files.each do |file|
|
|
99
114
|
content = replace_asset_references(File.read(file), css_hash, js_hash, base_url)
|
|
@@ -104,15 +119,15 @@ module Docyard
|
|
|
104
119
|
end
|
|
105
120
|
|
|
106
121
|
def replace_asset_references(content, css_hash, js_hash, base_url)
|
|
107
|
-
content.gsub(%r{/
|
|
108
|
-
.gsub(%r{/
|
|
109
|
-
.gsub(%r{/
|
|
110
|
-
.gsub(%r{<script src="/
|
|
122
|
+
content.gsub(%r{/_docyard/css/main\.css}, "#{base_url}_docyard/bundle.#{css_hash}.css")
|
|
123
|
+
.gsub(%r{/_docyard/js/theme\.js}, "#{base_url}_docyard/bundle.#{js_hash}.js")
|
|
124
|
+
.gsub(%r{/_docyard/js/components\.js}, "")
|
|
125
|
+
.gsub(%r{<script src="/_docyard/js/reload\.js"></script>}, "")
|
|
111
126
|
end
|
|
112
127
|
|
|
113
128
|
def write_bundled_asset(content, hash, extension)
|
|
114
129
|
filename = "bundle.#{hash}.#{extension}"
|
|
115
|
-
output_path = File.join(config.build.
|
|
130
|
+
output_path = File.join(config.build.output, "_docyard", filename)
|
|
116
131
|
FileUtils.mkdir_p(File.dirname(output_path))
|
|
117
132
|
File.write(output_path, content)
|
|
118
133
|
end
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
module Docyard
|
|
4
4
|
module Build
|
|
5
5
|
class FileCopier
|
|
6
|
+
DOCYARD_OUTPUT_DIR = "_docyard"
|
|
7
|
+
|
|
6
8
|
attr_reader :config, :verbose
|
|
7
9
|
|
|
8
10
|
def initialize(config, verbose: false)
|
|
@@ -14,7 +16,7 @@ module Docyard
|
|
|
14
16
|
puts "\nCopying static assets..."
|
|
15
17
|
|
|
16
18
|
count = 0
|
|
17
|
-
count +=
|
|
19
|
+
count += copy_public_files
|
|
18
20
|
count += copy_branding_assets
|
|
19
21
|
|
|
20
22
|
log "[✓] Copied #{count} static files"
|
|
@@ -23,25 +25,22 @@ module Docyard
|
|
|
23
25
|
|
|
24
26
|
private
|
|
25
27
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
return 0 unless Dir.exist?(
|
|
29
|
-
|
|
30
|
-
output_assets_dir = File.join(config.build.output_dir, "assets")
|
|
31
|
-
FileUtils.mkdir_p(output_assets_dir)
|
|
28
|
+
def copy_public_files
|
|
29
|
+
public_dir = Constants::PUBLIC_DIR
|
|
30
|
+
return 0 unless Dir.exist?(public_dir)
|
|
32
31
|
|
|
33
|
-
files =
|
|
34
|
-
files.each { |file|
|
|
32
|
+
files = find_files_in_dir(public_dir)
|
|
33
|
+
files.each { |file| copy_single_file(file, "#{public_dir}/", config.build.output) }
|
|
35
34
|
|
|
36
|
-
log "[✓] Copied #{files.size}
|
|
35
|
+
log "[✓] Copied #{files.size} public files from #{public_dir}/" if files.any?
|
|
37
36
|
files.size
|
|
38
37
|
end
|
|
39
38
|
|
|
40
|
-
def
|
|
41
|
-
Dir.glob(File.join(
|
|
39
|
+
def find_files_in_dir(dir)
|
|
40
|
+
Dir.glob(File.join(dir, "**", "*")).select { |f| File.file?(f) }
|
|
42
41
|
end
|
|
43
42
|
|
|
44
|
-
def
|
|
43
|
+
def copy_single_file(file, prefix, output_dir)
|
|
45
44
|
relative_path = file.delete_prefix(prefix)
|
|
46
45
|
dest_path = File.join(output_dir, relative_path)
|
|
47
46
|
|
|
@@ -60,26 +59,49 @@ module Docyard
|
|
|
60
59
|
end
|
|
61
60
|
|
|
62
61
|
def copy_default_branding_assets
|
|
63
|
-
templates_assets =
|
|
64
|
-
count =
|
|
62
|
+
templates_assets = templates_assets_path
|
|
63
|
+
count = copy_branding_files(templates_assets)
|
|
64
|
+
count + copy_fonts(templates_assets)
|
|
65
|
+
end
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
def templates_assets_path
|
|
68
|
+
File.join(__dir__, "..", "templates", "assets")
|
|
69
|
+
end
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
def copy_branding_files(templates_assets)
|
|
72
|
+
branding_files = %w[logo.svg logo-dark.svg favicon.svg]
|
|
73
|
+
branding_files.sum { |asset_file| copy_asset_to_docyard(templates_assets, asset_file, "default branding") }
|
|
74
|
+
end
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
def copy_asset_to_docyard(source_dir, filename, label)
|
|
77
|
+
source_path = File.join(source_dir, filename)
|
|
78
|
+
return 0 unless File.exist?(source_path)
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
dest_path = File.join(config.build.output, DOCYARD_OUTPUT_DIR, filename)
|
|
81
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
82
|
+
FileUtils.cp(source_path, dest_path)
|
|
83
|
+
log " Copied #{label}: #{filename}" if verbose
|
|
84
|
+
1
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def copy_fonts(templates_assets)
|
|
88
|
+
fonts_dir = File.join(templates_assets, "fonts")
|
|
89
|
+
return 0 unless Dir.exist?(fonts_dir)
|
|
90
|
+
|
|
91
|
+
font_files = Dir.glob(File.join(fonts_dir, "*")).select { |f| File.file?(f) }
|
|
92
|
+
font_files.sum { |font_file| copy_single_font(font_file) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def copy_single_font(font_file)
|
|
96
|
+
dest_path = File.join(config.build.output, DOCYARD_OUTPUT_DIR, "fonts", File.basename(font_file))
|
|
97
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
98
|
+
FileUtils.cp(font_file, dest_path)
|
|
99
|
+
log " Copied font: #{File.basename(font_file)}" if verbose
|
|
100
|
+
1
|
|
79
101
|
end
|
|
80
102
|
|
|
81
103
|
def copy_user_branding_assets
|
|
82
|
-
%w[logo
|
|
104
|
+
%w[logo favicon].sum { |asset_key| copy_single_branding_asset(asset_key) }
|
|
83
105
|
end
|
|
84
106
|
|
|
85
107
|
def copy_single_branding_asset(asset_key)
|
|
@@ -89,7 +111,7 @@ module Docyard
|
|
|
89
111
|
full_path = File.join("docs", asset_path)
|
|
90
112
|
return 0 unless File.exist?(full_path)
|
|
91
113
|
|
|
92
|
-
dest_path = File.join(config.build.
|
|
114
|
+
dest_path = File.join(config.build.output, asset_path)
|
|
93
115
|
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
94
116
|
FileUtils.cp(full_path, dest_path)
|
|
95
117
|
|
|
@@ -15,7 +15,7 @@ module Docyard
|
|
|
15
15
|
urls = collect_urls
|
|
16
16
|
sitemap_content = build_sitemap(urls)
|
|
17
17
|
|
|
18
|
-
output_path = File.join(config.build.
|
|
18
|
+
output_path = File.join(config.build.output, "sitemap.xml")
|
|
19
19
|
File.write(output_path, sitemap_content)
|
|
20
20
|
|
|
21
21
|
puts "[✓] Generated sitemap.xml (#{urls.size} URLs)"
|
|
@@ -24,10 +24,10 @@ module Docyard
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
def collect_urls
|
|
27
|
-
html_files = Dir.glob(File.join(config.build.
|
|
27
|
+
html_files = Dir.glob(File.join(config.build.output, "**", "index.html"))
|
|
28
28
|
|
|
29
29
|
html_files.map do |file|
|
|
30
|
-
relative_path = file.delete_prefix(config.build.
|
|
30
|
+
relative_path = file.delete_prefix(config.build.output).delete_suffix("/index.html")
|
|
31
31
|
url_path = relative_path.empty? ? "/" : relative_path
|
|
32
32
|
lastmod = File.mtime(file).utc.iso8601
|
|
33
33
|
|
|
@@ -36,15 +36,15 @@ module Docyard
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def build_sitemap(urls)
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
base = config.build.base
|
|
40
|
+
base = base.chop if base.end_with?("/")
|
|
41
41
|
|
|
42
42
|
xml = ['<?xml version="1.0" encoding="UTF-8"?>']
|
|
43
43
|
xml << '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
|
|
44
44
|
|
|
45
45
|
urls.each do |url|
|
|
46
46
|
xml << " <url>"
|
|
47
|
-
xml << " <loc>#{
|
|
47
|
+
xml << " <loc>#{base}#{url[:loc]}</loc>"
|
|
48
48
|
xml << " <lastmod>#{url[:lastmod]}</lastmod>"
|
|
49
49
|
xml << " </url>"
|
|
50
50
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "tty-progressbar"
|
|
4
|
+
require_relative "../rendering/template_resolver"
|
|
5
|
+
require_relative "../navigation/prev_next_builder"
|
|
6
|
+
require_relative "../navigation/breadcrumb_builder"
|
|
4
7
|
|
|
5
8
|
module Docyard
|
|
6
9
|
module Build
|
|
@@ -10,10 +13,12 @@ module Docyard
|
|
|
10
13
|
def initialize(config, verbose: false)
|
|
11
14
|
@config = config
|
|
12
15
|
@verbose = verbose
|
|
13
|
-
@renderer = Renderer.new(base_url: config.build.
|
|
16
|
+
@renderer = Renderer.new(base_url: config.build.base, config: config)
|
|
14
17
|
end
|
|
15
18
|
|
|
16
19
|
def generate
|
|
20
|
+
copy_custom_landing_page if custom_landing_page?
|
|
21
|
+
|
|
17
22
|
markdown_files = collect_markdown_files
|
|
18
23
|
puts "\n[✓] Found #{markdown_files.size} markdown files"
|
|
19
24
|
|
|
@@ -33,24 +38,72 @@ module Docyard
|
|
|
33
38
|
|
|
34
39
|
private
|
|
35
40
|
|
|
41
|
+
def custom_landing_page?
|
|
42
|
+
File.file?("docs/index.html")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def copy_custom_landing_page
|
|
46
|
+
output_path = File.join(config.build.output, "index.html")
|
|
47
|
+
FileUtils.mkdir_p(File.dirname(output_path))
|
|
48
|
+
FileUtils.cp("docs/index.html", output_path)
|
|
49
|
+
log "[✓] Copied custom landing page (index.html)"
|
|
50
|
+
end
|
|
51
|
+
|
|
36
52
|
def collect_markdown_files
|
|
37
|
-
Dir.glob(File.join("docs", "**", "*.md"))
|
|
53
|
+
files = Dir.glob(File.join("docs", "**", "*.md"))
|
|
54
|
+
files.reject! { |f| f == "docs/index.md" } if custom_landing_page?
|
|
55
|
+
files
|
|
38
56
|
end
|
|
39
57
|
|
|
40
58
|
def generate_page(markdown_file_path)
|
|
41
59
|
output_path = determine_output_path(markdown_file_path)
|
|
42
60
|
current_path = determine_current_path(markdown_file_path)
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
html_content =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
html_content = render_markdown_file(markdown_file_path, current_path)
|
|
63
|
+
html_content = apply_search_exclusion(html_content, current_path)
|
|
64
|
+
write_output(output_path, html_content)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def apply_search_exclusion(html_content, current_path)
|
|
68
|
+
return html_content unless excluded_from_search?(current_path)
|
|
69
|
+
|
|
70
|
+
html_content.gsub("data-pagefind-body", "data-pagefind-ignore")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def excluded_from_search?(path)
|
|
74
|
+
exclude_patterns = config.search.exclude || []
|
|
75
|
+
exclude_patterns.any? do |pattern|
|
|
76
|
+
next false unless pattern.start_with?("/")
|
|
77
|
+
|
|
78
|
+
File.fnmatch(pattern, path, File::FNM_PATHNAME)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def render_markdown_file(markdown_file_path, current_path)
|
|
83
|
+
markdown = Markdown.new(File.read(markdown_file_path))
|
|
84
|
+
template_resolver = TemplateResolver.new(markdown.frontmatter, config.data)
|
|
85
|
+
branding = branding_options
|
|
50
86
|
|
|
87
|
+
navigation = build_navigation_html(template_resolver, current_path, markdown, branding[:header_ctas])
|
|
88
|
+
renderer.render_file(markdown_file_path, **navigation, branding: branding,
|
|
89
|
+
template_options: template_resolver.to_options,
|
|
90
|
+
current_path: current_path)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build_navigation_html(template_resolver, current_path, markdown, header_ctas)
|
|
94
|
+
return { sidebar_html: "", prev_next_html: "", breadcrumbs: nil } unless template_resolver.show_sidebar?
|
|
95
|
+
|
|
96
|
+
sidebar_builder = build_sidebar_instance(current_path, header_ctas)
|
|
97
|
+
{
|
|
98
|
+
sidebar_html: sidebar_builder.to_html,
|
|
99
|
+
prev_next_html: build_prev_next(sidebar_builder, current_path, markdown),
|
|
100
|
+
breadcrumbs: build_breadcrumbs(sidebar_builder.tree, current_path)
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def write_output(output_path, html_content)
|
|
51
105
|
FileUtils.mkdir_p(File.dirname(output_path))
|
|
52
106
|
File.write(output_path, html_content)
|
|
53
|
-
|
|
54
107
|
log "Generated: #{output_path}" if verbose
|
|
55
108
|
end
|
|
56
109
|
|
|
@@ -59,7 +112,7 @@ module Docyard
|
|
|
59
112
|
base_name = File.basename(relative_path, ".md")
|
|
60
113
|
dir_name = File.dirname(relative_path)
|
|
61
114
|
|
|
62
|
-
output_dir = config.build.
|
|
115
|
+
output_dir = config.build.output
|
|
63
116
|
|
|
64
117
|
if base_name == "index"
|
|
65
118
|
File.join(output_dir, dir_name, "index.html")
|
|
@@ -80,14 +133,34 @@ module Docyard
|
|
|
80
133
|
end
|
|
81
134
|
end
|
|
82
135
|
|
|
83
|
-
def
|
|
136
|
+
def build_sidebar_instance(current_path, header_ctas = [])
|
|
84
137
|
SidebarBuilder.new(
|
|
85
138
|
docs_path: "docs",
|
|
86
139
|
current_path: current_path,
|
|
87
|
-
config: config
|
|
140
|
+
config: config,
|
|
141
|
+
header_ctas: header_ctas
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def build_prev_next(sidebar_builder, current_path, markdown)
|
|
146
|
+
PrevNextBuilder.new(
|
|
147
|
+
sidebar_tree: sidebar_builder.tree,
|
|
148
|
+
current_path: current_path,
|
|
149
|
+
frontmatter: markdown.frontmatter,
|
|
150
|
+
config: {}
|
|
88
151
|
).to_html
|
|
89
152
|
end
|
|
90
153
|
|
|
154
|
+
def build_breadcrumbs(sidebar_tree, current_path)
|
|
155
|
+
return nil unless breadcrumbs_enabled?
|
|
156
|
+
|
|
157
|
+
BreadcrumbBuilder.new(sidebar_tree: sidebar_tree, current_path: current_path)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def breadcrumbs_enabled?
|
|
161
|
+
config.navigation.breadcrumbs != false
|
|
162
|
+
end
|
|
163
|
+
|
|
91
164
|
def branding_options
|
|
92
165
|
BrandingResolver.new(config).resolve
|
|
93
166
|
end
|
data/lib/docyard/builder.rb
CHANGED
|
@@ -35,7 +35,7 @@ module Docyard
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
37
|
def prepare_output_directory
|
|
38
|
-
output_dir = config.build.
|
|
38
|
+
output_dir = config.build.output
|
|
39
39
|
|
|
40
40
|
if clean && Dir.exist?(output_dir)
|
|
41
41
|
log "[✓] Cleaning #{output_dir}/ directory"
|
|
@@ -68,7 +68,7 @@ module Docyard
|
|
|
68
68
|
sitemap_gen = Build::SitemapGenerator.new(config)
|
|
69
69
|
sitemap_gen.generate
|
|
70
70
|
|
|
71
|
-
File.write(File.join(config.build.
|
|
71
|
+
File.write(File.join(config.build.output, "robots.txt"), robots_txt_content)
|
|
72
72
|
log "[+] Generated robots.txt"
|
|
73
73
|
end
|
|
74
74
|
|
|
@@ -78,14 +78,14 @@ module Docyard
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def robots_txt_content
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
base = config.build.base
|
|
82
|
+
base = "#{base}/" unless base.end_with?("/")
|
|
83
83
|
|
|
84
84
|
<<~ROBOTS
|
|
85
85
|
User-agent: *
|
|
86
86
|
Allow: /
|
|
87
87
|
|
|
88
|
-
Sitemap: #{
|
|
88
|
+
Sitemap: #{base}sitemap.xml
|
|
89
89
|
ROBOTS
|
|
90
90
|
end
|
|
91
91
|
|
|
@@ -94,7 +94,7 @@ module Docyard
|
|
|
94
94
|
|
|
95
95
|
puts "\n#{'=' * 50}"
|
|
96
96
|
puts "Build complete in #{format('%.2f', elapsed)}s"
|
|
97
|
-
puts "Output: #{config.build.
|
|
97
|
+
puts "Output: #{config.build.output}/"
|
|
98
98
|
|
|
99
99
|
summary = "#{pages} pages, #{bundles} bundles, #{assets} static files"
|
|
100
100
|
summary += ", #{indexed} pages indexed" if indexed.positive?
|
|
@@ -2,17 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
module Docyard
|
|
4
4
|
module Components
|
|
5
|
+
AbbreviationProcessor = Processors::AbbreviationProcessor
|
|
6
|
+
AccordionProcessor = Processors::AccordionProcessor
|
|
7
|
+
BadgeProcessor = Processors::BadgeProcessor
|
|
8
|
+
StepsProcessor = Processors::StepsProcessor
|
|
9
|
+
CardsProcessor = Processors::CardsProcessor
|
|
5
10
|
CalloutProcessor = Processors::CalloutProcessor
|
|
6
11
|
CodeBlockProcessor = Processors::CodeBlockProcessor
|
|
12
|
+
CodeGroupProcessor = Processors::CodeGroupProcessor
|
|
7
13
|
CodeBlockDiffPreprocessor = Processors::CodeBlockDiffPreprocessor
|
|
8
14
|
CodeBlockFocusPreprocessor = Processors::CodeBlockFocusPreprocessor
|
|
9
15
|
CodeBlockOptionsPreprocessor = Processors::CodeBlockOptionsPreprocessor
|
|
10
16
|
CodeSnippetImportPreprocessor = Processors::CodeSnippetImportPreprocessor
|
|
17
|
+
CustomAnchorProcessor = Processors::CustomAnchorProcessor
|
|
18
|
+
ImageCaptionProcessor = Processors::ImageCaptionProcessor
|
|
19
|
+
IncludeProcessor = Processors::IncludeProcessor
|
|
20
|
+
VideoEmbedProcessor = Processors::VideoEmbedProcessor
|
|
21
|
+
FileTreeProcessor = Processors::FileTreeProcessor
|
|
11
22
|
HeadingAnchorProcessor = Processors::HeadingAnchorProcessor
|
|
12
23
|
IconProcessor = Processors::IconProcessor
|
|
13
24
|
TableOfContentsProcessor = Processors::TableOfContentsProcessor
|
|
14
25
|
TableWrapperProcessor = Processors::TableWrapperProcessor
|
|
15
26
|
TabsProcessor = Processors::TabsProcessor
|
|
27
|
+
TooltipProcessor = Processors::TooltipProcessor
|
|
16
28
|
|
|
17
29
|
CodeDetector = Support::CodeDetector
|
|
18
30
|
IconDetector = Support::Tabs::IconDetector
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../base_processor"
|
|
4
|
+
require_relative "../support/markdown_code_block_helper"
|
|
5
|
+
|
|
6
|
+
module Docyard
|
|
7
|
+
module Components
|
|
8
|
+
module Processors
|
|
9
|
+
class AbbreviationProcessor < BaseProcessor
|
|
10
|
+
include Support::MarkdownCodeBlockHelper
|
|
11
|
+
|
|
12
|
+
DEFINITION_PATTERN = /^\*\[([^\]]+)\]:\s*(.+)$/
|
|
13
|
+
self.priority = 5
|
|
14
|
+
|
|
15
|
+
def preprocess(content)
|
|
16
|
+
abbreviations = extract_abbreviations_outside_code_blocks(content)
|
|
17
|
+
return content if abbreviations.empty?
|
|
18
|
+
|
|
19
|
+
process_outside_code_blocks(content) do |segment|
|
|
20
|
+
segment = remove_definitions(segment)
|
|
21
|
+
apply_abbreviations(segment, abbreviations)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def extract_abbreviations_outside_code_blocks(content)
|
|
28
|
+
abbreviations = {}
|
|
29
|
+
process_outside_code_blocks(content) do |segment|
|
|
30
|
+
segment.scan(DEFINITION_PATTERN) do |term, definition|
|
|
31
|
+
abbreviations[term] = definition.strip
|
|
32
|
+
end
|
|
33
|
+
segment
|
|
34
|
+
end
|
|
35
|
+
abbreviations
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def remove_definitions(content)
|
|
39
|
+
content.gsub(/^[ \t]*\*\[([^\]]+)\]:\s*.+$\n?/, "")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def apply_abbreviations(content, abbreviations)
|
|
43
|
+
abbreviations.each do |term, definition|
|
|
44
|
+
pattern = build_term_pattern(term)
|
|
45
|
+
content = content.gsub(pattern) do |match|
|
|
46
|
+
build_abbr_tag(match, definition)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
content
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def build_term_pattern(term)
|
|
53
|
+
escaped = Regexp.escape(term)
|
|
54
|
+
/(?<![<\w])#{escaped}(?![>\w])/
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def build_abbr_tag(term, definition)
|
|
58
|
+
escaped_definition = escape_html(definition)
|
|
59
|
+
%(<abbr class="docyard-abbr" data-definition="#{escaped_definition}">#{term}</abbr>)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def escape_html(text)
|
|
63
|
+
text.to_s
|
|
64
|
+
.gsub("&", "&")
|
|
65
|
+
.gsub("<", "<")
|
|
66
|
+
.gsub(">", ">")
|
|
67
|
+
.gsub('"', """)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|