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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51b2adfdb7f77c4366592cc3eea5a160fc42f85f17e44593e9bf192c3b470b48
4
- data.tar.gz: 439d82b762c7bc6f485ab627365522b7e202833fc841c6b8655fb28e1e9b3e55
3
+ metadata.gz: 9ea61eefa97dc9199d17929def1315fdf2ce71f75ddf4f03833b9291fa64ce75
4
+ data.tar.gz: '0538fae77efffb84a8aac4ba979510419ff2a8cd8eec1b5aaf9effc2950595d8'
5
5
  SHA512:
6
- metadata.gz: a66355efb4615ae4552aa8656e63078989cdec734f41670dc172936060954a065fefb1a2733e51494747ef0ab9a1dbee399b3c8f37daf2718e92ba03c315df9f
7
- data.tar.gz: be3cea82f91b60cc7e7c35be142b6c2ccd9be4323cbf1c5190987b6ba6894d4c8da38f55c141abda43211a5ac2b70d38283e1b340d62071ee5c22c87c9c35a51
6
+ metadata.gz: f184c67f1be8766cdd6339da6b25ccd8b7f1d0150ce3f6c0db0de8dd68c590b82e29f76a4ec1928c29151018d318225f2769cea58be9ad1dc2c721a2593663d5
7
+ data.tar.gz: 982f4bdcd4170dd013dd5ee10fb21db935eeafcb486372a45db7ae58a3f8d75e6c716555585695ea9527cc7fbd2bf0e43c5a7982894160ec6832c591c2db45a1
data/CHANGELOG.md CHANGED
@@ -7,6 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.0.1] - 2026-01-22
11
+
12
+ ### Added
13
+ - **Root Fallback Redirect** - Auto-redirect to first page when no index.md exists (#127)
14
+
15
+ ### Fixed
16
+ - Breadcrumb sections without index pages now link to first navigable child (#126)
17
+ - Error pages (404/500) now use custom branding colors (#125)
18
+ - Base URL now correctly applied to all assets including fonts and search (#124)
19
+ - Dev server no longer applies build.base config, always uses root path (#123)
20
+
21
+ ## [1.0.0] - 2026-01-22
22
+
23
+ ### Added
24
+ - **Custom Social Links** - Add custom social icons with Phosphor icons or inline SVG via `socials.custom` config (#119)
25
+ - **Branding Colors** - Customize primary color theme with `branding.color` supporting light/dark variants (#115)
26
+ - **Custom Hero Visuals** - Use custom images/components in landing page hero sections (#114)
27
+ - **Feedback Widget** - Collect user feedback on documentation pages (#107)
28
+ - **Phosphor Icons CDN** - Load icons from CDN for smaller bundle size (#106)
29
+ - **LLMs.txt Generation** - Auto-generate llms.txt files for AI indexing (#105)
30
+ - **Copy Page Button** - One-click copy entire page content for LLM context (#104)
31
+ - **Configurable Docs Path** - Customize source directory with `source` config option (#101)
32
+ - **Parallel Builds** - Multi-threaded site generation for faster builds (#89)
33
+ - **Analytics Integration** - Support for Google Analytics, Plausible, Fathom, and custom scripts (#77)
34
+ - **Git Integration** - "Edit on GitHub" links and "Last updated" timestamps from git history (#76)
35
+ - **Open Graph Meta Tags** - Auto-generated social sharing metadata (#75)
36
+ - **Custom Error Pages** - Styled 404 and error pages (#78)
37
+ - **GitHub Pages Workflow** - CI workflow for automated deployments (#117)
38
+
39
+ ### Changed
40
+ - **Domain Migration** - Project domain changed from docyard.org to docyard.dev (#118)
41
+ - **Init Command Revamp** - Improved scaffolding with better defaults (#109)
42
+ - **Puma Web Server** - Migrated to Puma for better performance (#84)
43
+ - **Search Optimization** - Improved search indexing and relevance (#86)
44
+ - **Sidebar Caching** - Faster rebuilds with sidebar state caching (#85)
45
+
46
+ ### Fixed
47
+ - Video embed dimension validation (#103)
48
+ - Mobile navigation issues (#102)
49
+ - Banner layout padding on tablet/mobile (#88)
50
+ - TOC dropdown behavior (#87)
51
+ - Flash of unstyled content (FOUC) prevention (#79)
52
+ - Security and error handling improvements (#99, #100)
53
+
54
+ ### Performance
55
+ - Parallel build support for multi-core systems (#89)
56
+ - Batch git operations for faster metadata lookup (#91)
57
+ - Dev server cache headers (#92)
58
+ - Passive event listeners for smoother scrolling (#93)
59
+ - Analytics domain preconnect hints (#83)
60
+ - CSS will-change hints for animations (#82)
61
+ - Font display swap for faster text rendering (#81)
62
+ - Deferred script loading (#80)
63
+
10
64
  ## [0.9.0] - 2026-01-15
11
65
 
12
66
  ### Added
@@ -162,7 +216,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
162
216
  - Initial gem structure
163
217
  - Project scaffolding
164
218
 
165
- [Unreleased]: https://github.com/sanifhimani/docyard/compare/v0.9.0...HEAD
219
+ [Unreleased]: https://github.com/sanifhimani/docyard/compare/v1.0.1...HEAD
220
+ [1.0.1]: https://github.com/sanifhimani/docyard/compare/v1.0.0...v1.0.1
221
+ [1.0.0]: https://github.com/sanifhimani/docyard/compare/v0.9.0...v1.0.0
166
222
  [0.9.0]: https://github.com/sanifhimani/docyard/compare/v0.8.0...v0.9.0
167
223
  [0.8.0]: https://github.com/sanifhimani/docyard/compare/v0.7.0...v0.8.0
168
224
  [0.7.0]: https://github.com/sanifhimani/docyard/compare/v0.6.0...v0.7.0
data/README.md CHANGED
@@ -3,272 +3,27 @@
3
3
  [![CI](https://github.com/sanifhimani/docyard/actions/workflows/ci.yml/badge.svg)](https://github.com/sanifhimani/docyard/actions/workflows/ci.yml)
4
4
  [![Gem Version](https://badge.fury.io/rb/docyard.svg)](https://badge.fury.io/rb/docyard)
5
5
 
6
- > Documentation generator for Ruby
7
-
8
- Build beautiful documentation sites with hot reload, dark mode, and powerful markdown components.
9
-
10
- ## Features
11
-
12
- ### Core
13
- - **Static site generation** - Build static sites with `docyard build`
14
- - **Hot reload** - Changes appear instantly while you write
15
- - **Dark mode** - Beautiful light/dark theme with system preference detection
16
- - **Configuration system** - Optional `docyard.yml` for site metadata, branding, and build settings
17
- - **Custom branding** - Logo and favicon with light/dark mode support
18
- - **Base URL support** - Deploy to subdirectories or custom paths
19
-
20
- ### Navigation
21
- - **Sidebar navigation** - Automatic sidebar with nested folders and collapsible sections
22
- - **Sidebar customization** - Custom ordering, icons, and external links via config
23
- - **Table of Contents** - Auto-generated TOC with heading anchors and smooth scrolling
24
- - **Previous/Next navigation** - Auto-detection from sidebar with frontmatter override support
25
- - **Active page highlighting** - Always know where you are
26
-
27
- ### Markdown
28
- - **GitHub Flavored Markdown** - Tables, task lists, strikethrough
29
- - **Syntax highlighting** - 100+ languages via Rouge with copy button
30
- - **Markdown components**:
31
- - **Callouts** - 5 types (note, tip, important, warning, danger) with GitHub alerts syntax
32
- - **Tabs** - Code blocks, package managers, and custom tabs with keyboard navigation
33
- - **Icons** - 24 Phosphor icons with `:icon:` syntax
34
- - **YAML frontmatter** - Add metadata to your pages
35
-
36
- ### Production
37
- - **Asset bundling** - Minified CSS/JS with content hashing for cache busting
38
- - **SEO** - Automatic sitemap.xml and robots.txt generation
39
- - **Preview server** - Test production builds locally before deploying
40
- - **Mobile responsive** - Looks great on all devices
6
+ Beautiful documentation sites from Markdown. Fast, simple, no configuration required.
41
7
 
42
8
  ## Quick Start
43
9
 
44
10
  ```bash
45
- # Install
46
11
  gem install docyard
47
-
48
- # Initialize
49
- docyard init
50
-
51
- # Start dev server
52
- docyard serve
53
- # → http://localhost:4200
54
-
55
- # Build for production
56
- docyard build
57
- ```
58
-
59
- Your site is ready to deploy! Upload the `dist/` folder to any static host.
60
-
61
- ## Installation
62
-
63
- Add to your Gemfile:
64
-
65
- ```ruby
66
- gem 'docyard'
67
- ```
68
-
69
- Or install directly:
70
-
71
- ```bash
72
- gem install docyard
73
- ```
74
-
75
- ## Usage
76
-
77
- ### Initialize a New Project
78
-
79
- ```bash
80
- docyard init
81
- ```
82
-
83
- This creates:
84
- ```
85
- docs/
86
- index.md # Home page
87
- getting-started/
88
- installation.md # Installation guide
89
- guides/
90
- markdown-features.md # Markdown features showcase
91
- configuration.md # Configuration guide
92
- docyard.yml # Optional configuration
93
- ```
94
-
95
- ### Commands
96
-
97
- ```bash
98
- # Development server with hot reload
12
+ docyard init my-docs
13
+ cd my-docs
99
14
  docyard serve
100
- docyard serve --port 3000 --host 0.0.0.0
101
-
102
- # Build for production
103
- docyard build
104
- docyard build --no-clean # Don't clean output directory
105
-
106
- # Preview production build
107
- docyard preview
108
- docyard preview --port 4001
109
- ```
110
-
111
- ### Writing Docs
112
-
113
- Create markdown files in the `docs/` directory:
114
-
115
- ```markdown
116
- ---
117
- title: Getting Started
118
- ---
119
-
120
- # Getting Started
121
-
122
- \`\`\`ruby
123
- class User
124
- def initialize(name)
125
- @name = name
126
- end
127
- end
128
- \`\`\`
129
- ```
130
-
131
- ### Frontmatter
132
-
133
- Add YAML frontmatter to customize page metadata:
134
-
135
- ```yaml
136
- ---
137
- title: My Page Title
138
- description: Page description
139
- ---
140
- ```
141
-
142
- Currently supported:
143
- - `title` - Page title (shown in `<title>` tag)
144
- - `prev` - Customize or disable previous link
145
- - `next` - Customize or disable next link
146
-
147
- ### Customizing Navigation
148
-
149
- Control previous/next links per page via frontmatter:
150
-
151
- ```yaml
152
- ---
153
- title: My Page
154
- prev: false # Disable previous link
155
- next:
156
- text: Custom Next Page
157
- link: /custom-path
158
- ---
159
- ```
160
-
161
- Configure labels globally in `docyard.yml`:
162
-
163
- ```yaml
164
- navigation:
165
- footer:
166
- enabled: true
167
- prev_text: "← Back"
168
- next_text: "Forward →"
169
- ```
170
-
171
- ### Linking Between Pages
172
-
173
- Write links with `.md` extension, they'll be automatically cleaned:
174
-
175
- ```markdown
176
- [Getting Started](./getting-started.md) → /getting-started
177
- [Guide](./guide/index.md) → /guide
178
- ```
179
-
180
- ### Using Icons
181
-
182
- Docyard includes 24 essential Phosphor icons that work out of the box. Just type `:icon-name:` in your markdown:
183
-
184
- ```markdown
185
- :check: Zero configuration
186
- :lightning: Hot reload
187
- :rocket-launch: Fast and lightweight
188
-
189
- Use different weights:
190
- :heart: → regular weight (default)
191
- :heart:bold: → bold weight
192
- :heart:fill: → filled version
193
- ```
194
-
195
- Available icons: `heart`, `check`, `x`, `warning`, `info`, `question`, `arrow-right`, `arrow-left`, `arrow-up`, `arrow-down`, `code`, `terminal`, `package`, `rocket-launch`, `star`, `lightning`, `moon-stars`, `sun`, `link-external`, `copy`, `github`, `file`, `terminal-window`, `warning-circle`.
196
-
197
- Weights: `regular` (default), `bold`, `fill`, `light`, `thin`, `duotone`
198
-
199
- Icons automatically match your text size and color.
200
-
201
- **Adding new icons:**
202
-
203
- 1. Get the SVG path from [phosphoricons.com](https://phosphoricons.com)
204
- 2. Add to `lib/docyard/icons/phosphor.rb` under the appropriate weight
205
- 3. Format: `"icon-name" => '<path d="..."/>',`
206
-
207
- ### Directory Structure
208
-
209
- ```
210
- docs/
211
- index.md # / (root)
212
- getting-started.md # /getting-started
213
- guide/
214
- index.md # /guide
215
- setup.md # /guide/setup
216
- advanced.md # /guide/advanced
217
- ```
218
-
219
- ## Architecture
220
-
221
- Clean separation of concerns:
222
-
223
- ```
224
- lib/docyard/
225
- cli.rb # Command-line interface (Thor)
226
- initializer.rb # Project scaffolding (init command)
227
- server.rb # Server lifecycle (WEBrick, signals)
228
- rack_application.rb # HTTP request handling (routing, rendering)
229
- router.rb # URL → file path mapping
230
- renderer.rb # Markdown → HTML conversion
231
- markdown.rb # Markdown parsing & frontmatter extraction
232
- file_watcher.rb # Live reload file monitoring
233
- asset_handler.rb # Static asset serving
234
- ```
235
-
236
- Each class has a single, clear responsibility. Easy to test, easy to extend.
237
-
238
- ## Development
239
-
240
- ```bash
241
- git clone https://github.com/sanifhimani/docyard.git
242
- cd docyard
243
- bundle install
244
-
245
- # Run tests
246
- bundle exec rspec
247
-
248
- # Run linter
249
- bundle exec rubocop
250
-
251
- # Use local version
252
- ./bin/docyard init
253
- ./bin/docyard serve
254
15
  ```
255
16
 
256
- ## Roadmap
17
+ Open http://localhost:4200 and start writing.
257
18
 
258
- **v0.5.0 - Just shipped:**
259
- - Table of Contents with heading anchors
260
- - Previous/Next page navigation with auto-detection
19
+ ## Documentation
261
20
 
262
- **Next up (v0.6.0+):**
263
- - Code block enhancements (line numbers, highlighting, diffs)
264
- - Search functionality (client-side with Cmd/K)
265
- - Details/collapsible blocks
266
- - More markdown extensions
21
+ Visit [docyard.dev](https://docyard.dev) for the full documentation.
267
22
 
268
23
  ## Contributing
269
24
 
270
- See [CONTRIBUTING.md](CONTRIBUTING.md)
25
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
271
26
 
272
27
  ## License
273
28
 
274
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
29
+ [MIT](LICENSE)
data/exe/docyard ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/docyard"
5
+
6
+ Docyard::CLI.start(ARGV)
@@ -17,7 +17,7 @@ module Docyard
17
17
  end
18
18
 
19
19
  def bundle
20
- puts "\nBundling assets..."
20
+ Docyard.logger.info("\nBundling assets...")
21
21
 
22
22
  css_hash = bundle_css
23
23
  js_hash = bundle_js
@@ -37,6 +37,7 @@ module Docyard
37
37
  minified = CSSminify.compress(css_content)
38
38
  minified = fix_calc_whitespace(minified)
39
39
  minified = fix_css_math_functions(minified)
40
+ minified = replace_css_asset_urls(minified)
40
41
  hash = generate_hash(minified)
41
42
 
42
43
  write_bundled_asset(minified, hash, "css")
@@ -58,6 +59,11 @@ module Docyard
58
59
  css.gsub(/\bmax\(0,/, "max(0px,").gsub(/\bmin\(0,/, "min(0px,)")
59
60
  end
60
61
 
62
+ def replace_css_asset_urls(css)
63
+ base_url = normalize_base_url(config.build.base)
64
+ css.gsub(%r{/_docyard/fonts/}, "#{base_url}_docyard/fonts/")
65
+ end
66
+
61
67
  def resolve_css_imports(css_content)
62
68
  css_content.gsub(/@import url\('([^']+)'\);/) do |match|
63
69
  import_file = Regexp.last_match(1)
@@ -86,6 +92,7 @@ module Docyard
86
92
  components_js = concatenate_component_js
87
93
  js_content = [theme_js, components_js].join("\n")
88
94
  minified = Terser.compile(js_content)
95
+ minified = replace_js_asset_urls(minified)
89
96
  hash = generate_hash(minified)
90
97
 
91
98
  write_bundled_asset(minified, hash, "js")
@@ -94,6 +101,12 @@ module Docyard
94
101
  hash
95
102
  end
96
103
 
104
+ def replace_js_asset_urls(js_content)
105
+ base_url = normalize_base_url(config.build.base)
106
+ js_content.gsub(%r{"/_docyard/pagefind/}, "\"#{base_url}_docyard/pagefind/")
107
+ .gsub(%r{baseUrl:\s*["']/["']}, "baseUrl:\"#{base_url}\"")
108
+ end
109
+
97
110
  def concatenate_component_js
98
111
  components_dir = File.join(ASSETS_PATH, "js", "components")
99
112
  return "" unless Dir.exist?(components_dir)
@@ -123,6 +136,15 @@ module Docyard
123
136
  .gsub(%r{/_docyard/js/theme\.js}, "#{base_url}_docyard/bundle.#{js_hash}.js")
124
137
  .gsub(%r{/_docyard/js/components\.js}, "")
125
138
  .gsub(%r{<script src="/_docyard/js/reload\.js"></script>}, "")
139
+ .then { |html| replace_content_image_paths(html, base_url) }
140
+ end
141
+
142
+ def replace_content_image_paths(content, base_url)
143
+ return content if base_url == "/"
144
+
145
+ content.gsub(%r{(<img[^>]*\ssrc=")/(?!_docyard/)([^"]*")}) do
146
+ "#{Regexp.last_match(1)}#{base_url}#{Regexp.last_match(2)}"
147
+ end
126
148
  end
127
149
 
128
150
  def write_bundled_asset(content, hash, extension)
@@ -147,7 +169,7 @@ module Docyard
147
169
  end
148
170
 
149
171
  def log(message)
150
- puts message
172
+ Docyard.logger.info(message)
151
173
  end
152
174
  end
153
175
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module Build
5
+ class ErrorPageGenerator
6
+ attr_reader :config, :docs_path, :renderer
7
+
8
+ def initialize(config:, docs_path:, renderer:)
9
+ @config = config
10
+ @docs_path = docs_path
11
+ @renderer = renderer
12
+ end
13
+
14
+ def generate
15
+ output_path = File.join(config.build.output, "404.html")
16
+ html_content = load_content
17
+
18
+ FileUtils.mkdir_p(File.dirname(output_path))
19
+ File.write(output_path, html_content)
20
+ end
21
+
22
+ private
23
+
24
+ def load_content
25
+ custom_error_page = File.join(docs_path, "404.html")
26
+ return File.read(custom_error_page) if File.exist?(custom_error_page)
27
+
28
+ branding = BrandingResolver.new(config).resolve
29
+ renderer.render_not_found(branding: branding)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -13,7 +13,7 @@ module Docyard
13
13
  end
14
14
 
15
15
  def copy
16
- puts "\nCopying static assets..."
16
+ Docyard.logger.info("\nCopying static assets...")
17
17
 
18
18
  count = 0
19
19
  count += copy_public_files
@@ -26,7 +26,7 @@ module Docyard
26
26
  private
27
27
 
28
28
  def copy_public_files
29
- public_dir = Constants::PUBLIC_DIR
29
+ public_dir = config.public_dir
30
30
  return 0 unless Dir.exist?(public_dir)
31
31
 
32
32
  files = find_files_in_dir(public_dir)
@@ -104,11 +104,18 @@ module Docyard
104
104
  %w[logo favicon].sum { |asset_key| copy_single_branding_asset(asset_key) }
105
105
  end
106
106
 
107
+ def branding_asset_path(asset_key)
108
+ case asset_key
109
+ when "logo" then config.branding.logo
110
+ when "favicon" then config.branding.favicon
111
+ end
112
+ end
113
+
107
114
  def copy_single_branding_asset(asset_key)
108
- asset_path = config.branding.send(asset_key)
115
+ asset_path = branding_asset_path(asset_key)
109
116
  return 0 if asset_path.nil? || asset_path.start_with?("http://", "https://")
110
117
 
111
- full_path = File.join("docs", asset_path)
118
+ full_path = File.join(config.source, asset_path)
112
119
  return 0 unless File.exist?(full_path)
113
120
 
114
121
  dest_path = File.join(config.build.output, asset_path)
@@ -120,7 +127,7 @@ module Docyard
120
127
  end
121
128
 
122
129
  def log(message)
123
- puts message
130
+ Docyard.logger.info(message)
124
131
  end
125
132
  end
126
133
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Docyard
4
+ module Build
5
+ module FileWriter
6
+ def safe_file_write(path)
7
+ yield
8
+ rescue Errno::EACCES => e
9
+ raise BuildError, "Permission denied writing to #{path}: #{e.message}"
10
+ rescue Errno::ENOSPC => e
11
+ raise BuildError, "Disk full, cannot write to #{path}: #{e.message}"
12
+ rescue Errno::EROFS => e
13
+ raise BuildError, "Read-only filesystem, cannot write to #{path}: #{e.message}"
14
+ rescue SystemCallError => e
15
+ raise BuildError, "Failed to write #{path}: #{e.message}"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../rendering/markdown"
4
+
5
+ module Docyard
6
+ module Build
7
+ class LlmsTxtGenerator
8
+ attr_reader :config
9
+
10
+ def initialize(config)
11
+ @config = config
12
+ end
13
+
14
+ def generate
15
+ pages = collect_pages.sort_by { |page| page[:path] }
16
+ generate_llms_txt(pages)
17
+ generate_llms_full_txt(pages)
18
+ end
19
+
20
+ private
21
+
22
+ def collect_pages
23
+ Dir.glob(File.join(config.source, "**", "*.md")).map { |f| build_page_data(f) }
24
+ end
25
+
26
+ def build_page_data(file_path)
27
+ content = File.read(file_path)
28
+ markdown = Markdown.new(content, config: config, file_path: file_path)
29
+
30
+ {
31
+ title: markdown.title || url_path_for(file_path),
32
+ description: markdown.description,
33
+ path: url_path_for(file_path),
34
+ content: strip_frontmatter(content)
35
+ }
36
+ end
37
+
38
+ def url_path_for(file_path)
39
+ relative = file_path.delete_prefix("#{config.source}/").delete_suffix(".md")
40
+ relative == "index" ? "/" : "/#{relative}"
41
+ end
42
+
43
+ def strip_frontmatter(content)
44
+ return content unless content.start_with?("---")
45
+
46
+ parts = content.split(/^---\s*$/, 3)
47
+ parts.length >= 3 ? parts[2].strip : content
48
+ end
49
+
50
+ def generate_llms_txt(pages)
51
+ output_path = File.join(config.build.output, "llms.txt")
52
+ File.write(output_path, build_llms_txt_content(pages))
53
+ Docyard.logger.info("[✓] Generated llms.txt (#{pages.size} pages)")
54
+ end
55
+
56
+ def build_llms_txt_content(pages)
57
+ lines = header_lines
58
+ lines << "## Docs"
59
+ lines << ""
60
+ lines.concat(pages.map { |page| format_page_link(page) })
61
+ lines.join("\n")
62
+ end
63
+
64
+ def format_page_link(page)
65
+ line = "- [#{page[:title]}](#{page_url(page[:path])})"
66
+ line += ": #{page[:description]}" if page[:description]&.length&.positive?
67
+ line
68
+ end
69
+
70
+ def generate_llms_full_txt(pages)
71
+ output_path = File.join(config.build.output, "llms-full.txt")
72
+ File.write(output_path, build_llms_full_txt_content(pages))
73
+ Docyard.logger.info("[✓] Generated llms-full.txt")
74
+ end
75
+
76
+ def build_llms_full_txt_content(pages)
77
+ lines = header_lines
78
+ lines << "This file contains the complete documentation content."
79
+ lines << ""
80
+ pages.each { |page| lines.concat(format_page_content(page)) }
81
+ lines.join("\n")
82
+ end
83
+
84
+ def format_page_content(page)
85
+ ["---", "", "## #{page[:title]}", "", "URL: #{page_url(page[:path])}", "", page[:content], ""]
86
+ end
87
+
88
+ def header_lines
89
+ lines = ["# #{config.title}", ""]
90
+ return lines unless config.description&.length&.positive?
91
+
92
+ lines << "> #{config.description}"
93
+ lines << ""
94
+ lines
95
+ end
96
+
97
+ def page_url(path)
98
+ base = config.url || config.build.base
99
+ "#{base.chomp('/')}#{path}"
100
+ end
101
+ end
102
+ end
103
+ end