docyard 0.0.1 → 0.2.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. data/.github/pull_request_template.md +14 -0
  5. data/.github/workflows/ci.yml +49 -0
  6. data/.rubocop.yml +38 -0
  7. data/CHANGELOG.md +52 -0
  8. data/CONTRIBUTING.md +55 -0
  9. data/README.md +174 -3
  10. data/lib/docyard/asset_handler.rb +64 -0
  11. data/lib/docyard/cli.rb +34 -0
  12. data/lib/docyard/constants.rb +23 -0
  13. data/lib/docyard/errors.rb +54 -0
  14. data/lib/docyard/file_watcher.rb +42 -0
  15. data/lib/docyard/initializer.rb +76 -0
  16. data/lib/docyard/logging.rb +43 -0
  17. data/lib/docyard/markdown.rb +61 -0
  18. data/lib/docyard/rack_application.rb +81 -0
  19. data/lib/docyard/renderer.rb +61 -0
  20. data/lib/docyard/router.rb +31 -0
  21. data/lib/docyard/routing/resolution_result.rb +31 -0
  22. data/lib/docyard/server.rb +91 -0
  23. data/lib/docyard/sidebar/file_system_scanner.rb +77 -0
  24. data/lib/docyard/sidebar/renderer.rb +110 -0
  25. data/lib/docyard/sidebar/title_extractor.rb +25 -0
  26. data/lib/docyard/sidebar/tree_builder.rb +59 -0
  27. data/lib/docyard/sidebar_builder.rb +50 -0
  28. data/lib/docyard/templates/assets/css/code.css +214 -0
  29. data/lib/docyard/templates/assets/css/components.css +258 -0
  30. data/lib/docyard/templates/assets/css/layout.css +273 -0
  31. data/lib/docyard/templates/assets/css/main.css +10 -0
  32. data/lib/docyard/templates/assets/css/markdown.css +199 -0
  33. data/lib/docyard/templates/assets/css/reset.css +59 -0
  34. data/lib/docyard/templates/assets/css/typography.css +97 -0
  35. data/lib/docyard/templates/assets/css/variables.css +114 -0
  36. data/lib/docyard/templates/assets/js/reload.js +98 -0
  37. data/lib/docyard/templates/assets/js/theme.js +193 -0
  38. data/lib/docyard/templates/errors/404.html.erb +16 -0
  39. data/lib/docyard/templates/errors/500.html.erb +25 -0
  40. data/lib/docyard/templates/layouts/default.html.erb +51 -0
  41. data/lib/docyard/templates/markdown/core-concepts/file-structure.md.erb +61 -0
  42. data/lib/docyard/templates/markdown/core-concepts/markdown.md.erb +90 -0
  43. data/lib/docyard/templates/markdown/getting-started/installation.md.erb +43 -0
  44. data/lib/docyard/templates/markdown/getting-started/introduction.md.erb +30 -0
  45. data/lib/docyard/templates/markdown/getting-started/quick-start.md.erb +56 -0
  46. data/lib/docyard/templates/markdown/index.md.erb +86 -0
  47. data/lib/docyard/templates/partials/_icons.html.erb +11 -0
  48. data/lib/docyard/templates/partials/_nav_group.html.erb +7 -0
  49. data/lib/docyard/templates/partials/_nav_item.html.erb +3 -0
  50. data/lib/docyard/templates/partials/_nav_leaf.html.erb +1 -0
  51. data/lib/docyard/templates/partials/_nav_list.html.erb +3 -0
  52. data/lib/docyard/templates/partials/_nav_section.html.erb +6 -0
  53. data/lib/docyard/templates/partials/_sidebar.html.erb +6 -0
  54. data/lib/docyard/templates/partials/_sidebar_footer.html.erb +11 -0
  55. data/lib/docyard/utils/path_resolver.rb +30 -0
  56. data/lib/docyard/utils/text_formatter.rb +22 -0
  57. data/lib/docyard/version.rb +1 -1
  58. data/lib/docyard.rb +20 -2
  59. metadata +169 -2
@@ -0,0 +1,114 @@
1
+ /* Design Tokens */
2
+
3
+ :root {
4
+ /* Colors - Light Theme */
5
+ --color-bg: #ffffff;
6
+ --color-bg-secondary: #f9fafb;
7
+ --color-bg-tertiary: #f3f4f6;
8
+ --color-sidebar-bg: #f8fafc;
9
+ --color-text: #0d1117;
10
+ --color-text-secondary: #57606a;
11
+ --color-text-tertiary: #8c959f;
12
+ --color-border: #e4e7eb;
13
+ --color-border-secondary: #f3f4f6;
14
+ --color-primary: #2563eb;
15
+ --color-primary-hover: #1d4ed8;
16
+ --color-primary-light: #eff6ff;
17
+ --color-link: #0969da;
18
+ --color-link-hover: #0550ae;
19
+
20
+ /* Code colors */
21
+ --color-code-bg: #f4f4f5;
22
+ --color-code-text: #18181b;
23
+ --color-code-border: transparent;
24
+
25
+ /* Semantic colors */
26
+ --color-success: #116329;
27
+ --color-success-bg: #dafbe1;
28
+ --color-warning: #7d4e00;
29
+ --color-warning-bg: #fff8c5;
30
+ --color-error: #cf222e;
31
+ --color-error-bg: #ffebe9;
32
+ --color-info: #0969da;
33
+ --color-info-bg: #ddf4ff;
34
+
35
+ /* Typography */
36
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
37
+ --font-mono: ui-monospace, 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Menlo, Consolas, 'Courier New', monospace;
38
+
39
+ /* Font sizes */
40
+ --font-size-xs: 0.75rem; /* 12px */
41
+ --font-size-sm: 0.875rem; /* 14px */
42
+ --font-size-base: 1rem; /* 16px */
43
+ --font-size-lg: 1.125rem; /* 18px */
44
+ --font-size-xl: 1.25rem; /* 20px */
45
+ --font-size-2xl: 1.5rem; /* 24px */
46
+ --font-size-3xl: 1.875rem; /* 30px */
47
+ --font-size-4xl: 2.25rem; /* 36px */
48
+
49
+ /* Font weights */
50
+ --font-weight-normal: 400;
51
+ --font-weight-medium: 500;
52
+ --font-weight-semibold: 600;
53
+ --font-weight-bold: 700;
54
+
55
+ /* Line heights */
56
+ --line-height-tight: 1.25;
57
+ --line-height-normal: 1.5;
58
+ --line-height-relaxed: 1.75;
59
+
60
+ /* Spacing scale */
61
+ --space-1: 0.25rem; /* 4px */
62
+ --space-2: 0.5rem; /* 8px */
63
+ --space-3: 0.75rem; /* 12px */
64
+ --space-4: 1rem; /* 16px */
65
+ --space-5: 1.25rem; /* 20px */
66
+ --space-6: 1.5rem; /* 24px */
67
+ --space-8: 2rem; /* 32px */
68
+ --space-10: 2.5rem; /* 40px */
69
+ --space-12: 3rem; /* 48px */
70
+ --space-16: 4rem; /* 64px */
71
+ --space-20: 5rem; /* 80px */
72
+
73
+ /* Layout */
74
+ --sidebar-width: 288px;
75
+ --header-height: 64px;
76
+ --content-max-width: 800px;
77
+ --layout-max-width: 1440px;
78
+
79
+ /* Border radius */
80
+ --radius-sm: 0.25rem; /* 4px */
81
+ --radius-md: 0.375rem; /* 6px */
82
+ --radius-lg: 0.5rem; /* 8px */
83
+ --radius-xl: 0.75rem; /* 12px */
84
+
85
+ /* Shadows */
86
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
87
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
88
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
89
+
90
+ /* Transitions */
91
+ --transition-fast: 200ms cubic-bezier(0.4, 0, 0.2, 1);
92
+ --transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1);
93
+ --transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1);
94
+
95
+ /* Z-index scale */
96
+ --z-base: 0;
97
+ --z-header: 20;
98
+ --z-secondary-header: 25;
99
+ --z-overlay: 28;
100
+ --z-sidebar: 30;
101
+ --z-dropdown: 40;
102
+ --z-modal: 50;
103
+ --z-tooltip: 60;
104
+ }
105
+
106
+ /* Mobile breakpoint adjustments */
107
+ @media (max-width: 1024px) {
108
+ :root {
109
+ --sidebar-width: 100%;
110
+ --header-height: 56px;
111
+ --font-size-3xl: 1.5rem; /* Smaller on mobile */
112
+ --font-size-4xl: 1.875rem; /* Smaller on mobile */
113
+ }
114
+ }
@@ -0,0 +1,98 @@
1
+ (function () {
2
+ let lastCheck = Date.now() / 1000;
3
+ let basePollInterval = 500;
4
+ let currentPollInterval = basePollInterval;
5
+ let isReloading = false;
6
+ let consecutiveFailures = 0;
7
+ let serverWasDown = false;
8
+ let timeoutId = null;
9
+
10
+ async function fetchWithTimeout(resource, options = {}) {
11
+ const { timeout = 5000 } = options;
12
+
13
+ const controller = new AbortController();
14
+ const id = setTimeout(() => controller.abort(), timeout);
15
+
16
+ try {
17
+ const response = await fetch(resource, {
18
+ ...options,
19
+ signal: controller.signal
20
+ });
21
+ clearTimeout(id);
22
+ return response;
23
+ } catch (error) {
24
+ clearTimeout(id);
25
+ throw error;
26
+ }
27
+ }
28
+
29
+ async function checkForChanges() {
30
+ if (isReloading) return;
31
+
32
+ try {
33
+ const response = await fetchWithTimeout(`/_docyard/reload?since=${lastCheck}`, { timeout: 3000 });
34
+ const data = await response.json();
35
+ lastCheck = data.timestamp;
36
+
37
+ if (consecutiveFailures > 0) {
38
+ consecutiveFailures = 0;
39
+ currentPollInterval = basePollInterval;
40
+ if (serverWasDown) {
41
+ console.log('[Docyard] Server reconnected');
42
+ serverWasDown = false;
43
+ }
44
+ }
45
+
46
+ if (data.reload && !isReloading) {
47
+ isReloading = true;
48
+ console.log("[Docyard] Changes detected, hot reloading...");
49
+
50
+ try {
51
+ const resp = await fetchWithTimeout(window.location.href, { timeout: 5000 });
52
+ const html = await resp.text();
53
+ const parser = new DOMParser();
54
+ const newDoc = parser.parseFromString(html, 'text/html');
55
+
56
+ const oldMain = document.querySelector('main');
57
+ const newMain = newDoc.querySelector('main');
58
+
59
+ if (oldMain && newMain) {
60
+ oldMain.innerHTML = newMain.innerHTML;
61
+ console.log('[Docyard] Content updated via hot reload');
62
+ } else {
63
+ console.log('[Docyard] Layout changed, full reload required');
64
+ window.location.reload();
65
+ return;
66
+ }
67
+
68
+ isReloading = false;
69
+ } catch (error) {
70
+ console.error('[Docyard] Hot reload failed:', error);
71
+ console.log('[Docyard] Falling back to full reload');
72
+ window.location.reload();
73
+ }
74
+ }
75
+ } catch (error) {
76
+ consecutiveFailures++;
77
+
78
+ if (consecutiveFailures === 1) {
79
+ console.log('[Docyard] Server disconnected - live reload paused');
80
+ serverWasDown = true;
81
+ }
82
+
83
+ if (consecutiveFailures >= 3) {
84
+ console.log('[Docyard] Stopped polling. Refresh page when server restarts.');
85
+ return;
86
+ }
87
+
88
+ currentPollInterval = Math.min(basePollInterval * Math.pow(2, consecutiveFailures - 1), 5000);
89
+
90
+ isReloading = false;
91
+ }
92
+
93
+ timeoutId = setTimeout(checkForChanges, currentPollInterval);
94
+ }
95
+
96
+ checkForChanges();
97
+ console.log("[Docyard] Hot reload initialized");
98
+ })();
@@ -0,0 +1,193 @@
1
+ // Docyard Theme JavaScript
2
+
3
+ (function() {
4
+ 'use strict';
5
+
6
+ function initMobileMenu() {
7
+ const toggle = document.querySelector('.secondary-header-menu');
8
+ const sidebar = document.querySelector('.sidebar');
9
+ const overlay = document.querySelector('.mobile-overlay');
10
+
11
+ if (!toggle || !sidebar || !overlay) return;
12
+
13
+ function openMenu() {
14
+ sidebar.classList.add('is-open');
15
+ overlay.classList.add('is-visible');
16
+ toggle.setAttribute('aria-expanded', 'true');
17
+ document.body.style.overflow = 'hidden';
18
+ }
19
+
20
+ function closeMenu() {
21
+ sidebar.classList.remove('is-open');
22
+ overlay.classList.remove('is-visible');
23
+ toggle.setAttribute('aria-expanded', 'false');
24
+ document.body.style.overflow = '';
25
+ }
26
+
27
+ function toggleMenu() {
28
+ if (sidebar.classList.contains('is-open')) {
29
+ closeMenu();
30
+ } else {
31
+ openMenu();
32
+ }
33
+ }
34
+
35
+ toggle.addEventListener('click', toggleMenu);
36
+ overlay.addEventListener('click', closeMenu);
37
+
38
+ document.addEventListener('keydown', function(e) {
39
+ if (e.key === 'Escape' && sidebar.classList.contains('is-open')) {
40
+ closeMenu();
41
+ }
42
+ });
43
+
44
+ sidebar.querySelectorAll('a').forEach(function(link) {
45
+ link.addEventListener('click', closeMenu);
46
+ });
47
+ }
48
+
49
+ function initAccordion() {
50
+ const toggles = document.querySelectorAll('.nav-group-toggle');
51
+
52
+ toggles.forEach(function(toggle) {
53
+ toggle.addEventListener('click', function() {
54
+ const expanded = toggle.getAttribute('aria-expanded') === 'true';
55
+ const children = toggle.nextElementSibling;
56
+
57
+ if (!children || !children.classList.contains('nav-group-children')) {
58
+ return;
59
+ }
60
+
61
+ toggle.setAttribute('aria-expanded', !expanded);
62
+ children.classList.toggle('collapsed');
63
+
64
+ if (expanded) {
65
+ children.style.maxHeight = '0';
66
+ } else {
67
+ children.style.maxHeight = children.scrollHeight + 'px';
68
+ }
69
+ });
70
+ });
71
+
72
+ document.querySelectorAll('.nav-group-children.collapsed').forEach(function(el) {
73
+ el.style.maxHeight = '0';
74
+ });
75
+ }
76
+
77
+ function initSidebarScroll() {
78
+ const scrollContainer = document.querySelector('.sidebar nav');
79
+ if (!scrollContainer) return;
80
+
81
+ const STORAGE_KEY = 'docyard_sidebar_scroll';
82
+ const savedPosition = sessionStorage.getItem(STORAGE_KEY);
83
+
84
+ if (savedPosition) {
85
+ const position = parseInt(savedPosition, 10);
86
+ scrollContainer.scrollTop = position;
87
+
88
+ setTimeout(function() {
89
+ scrollContainer.scrollTop = position;
90
+ }, 100);
91
+ } else {
92
+ const activeLink = scrollContainer.querySelector('a.active');
93
+ if (activeLink) {
94
+ setTimeout(function() {
95
+ activeLink.scrollIntoView({
96
+ behavior: 'instant',
97
+ block: 'center'
98
+ });
99
+ }, 50);
100
+ }
101
+ }
102
+
103
+ scrollContainer.querySelectorAll('a').forEach(function(link) {
104
+ link.addEventListener('click', function() {
105
+ sessionStorage.setItem(STORAGE_KEY, scrollContainer.scrollTop);
106
+ });
107
+ });
108
+
109
+ let scrollTimeout;
110
+ scrollContainer.addEventListener('scroll', function() {
111
+ clearTimeout(scrollTimeout);
112
+ scrollTimeout = setTimeout(function() {
113
+ sessionStorage.setItem(STORAGE_KEY, scrollContainer.scrollTop);
114
+ }, 150);
115
+ });
116
+
117
+ const logo = document.querySelector('.header-logo');
118
+ if (logo) {
119
+ logo.addEventListener('click', function() {
120
+ sessionStorage.removeItem(STORAGE_KEY);
121
+ scrollContainer.scrollTop = 0;
122
+ });
123
+ }
124
+ }
125
+
126
+ function initScrollBehavior() {
127
+ const header = document.querySelector('.header');
128
+ const secondaryHeader = document.querySelector('.secondary-header');
129
+
130
+ if (!header || !secondaryHeader) return;
131
+
132
+ let lastScrollTop = 0;
133
+ let ticking = false;
134
+
135
+ function isMobile() {
136
+ return window.innerWidth <= 1024;
137
+ }
138
+
139
+ function updateHeaders() {
140
+ if (!isMobile()) {
141
+ header.classList.remove('hide-on-scroll');
142
+ secondaryHeader.classList.remove('shift-up');
143
+ ticking = false;
144
+ return;
145
+ }
146
+
147
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
148
+
149
+ if (scrollTop > lastScrollTop && scrollTop > 100) {
150
+ header.classList.add('hide-on-scroll');
151
+ secondaryHeader.classList.add('shift-up');
152
+ } else if (scrollTop < lastScrollTop) {
153
+ header.classList.remove('hide-on-scroll');
154
+ secondaryHeader.classList.remove('shift-up');
155
+ }
156
+
157
+ lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
158
+ ticking = false;
159
+ }
160
+
161
+ window.addEventListener('scroll', function() {
162
+ if (!ticking) {
163
+ window.requestAnimationFrame(updateHeaders);
164
+ ticking = true;
165
+ }
166
+ });
167
+
168
+ window.addEventListener('resize', function() {
169
+ if (!isMobile()) {
170
+ header.classList.remove('hide-on-scroll');
171
+ secondaryHeader.classList.remove('shift-up');
172
+ }
173
+ });
174
+ }
175
+
176
+ if ('scrollRestoration' in history) {
177
+ history.scrollRestoration = 'manual';
178
+ }
179
+
180
+ if (document.readyState === 'loading') {
181
+ document.addEventListener('DOMContentLoaded', function() {
182
+ initMobileMenu();
183
+ initAccordion();
184
+ initSidebarScroll();
185
+ initScrollBehavior();
186
+ });
187
+ } else {
188
+ initMobileMenu();
189
+ initAccordion();
190
+ initSidebarScroll();
191
+ initScrollBehavior();
192
+ }
193
+ })();
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>404 - Page Not Found</title>
7
+ <link rel="stylesheet" href="/assets/css/main.css">
8
+ </head>
9
+ <body>
10
+ <main>
11
+ <h1>404 - Page Not Found</h1>
12
+ <p>The page you're looking for doesn't exist.</p>
13
+ <p><a href="/">Go back home</a></p>
14
+ </main>
15
+ </body>
16
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>500 - Server Error</title>
7
+ <link rel="stylesheet" href="/assets/css/main.css">
8
+ </head>
9
+ <body>
10
+ <main>
11
+ <h1>500 - Internal Server Error</h1>
12
+ <p>Something went wrong.</p>
13
+
14
+ <% if @error_message %>
15
+ <h3>Error Details:</h3>
16
+ <pre><%= @error_message %></pre>
17
+ <% end %>
18
+
19
+ <% if @backtrace %>
20
+ <h3>Backtrace:</h3>
21
+ <pre><%= @backtrace %></pre>
22
+ <% end %>
23
+ </main>
24
+ </body>
25
+ </html>
@@ -0,0 +1,51 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="description" content="<%= @page_description || 'Documentation' %>">
7
+ <title><%= @page_title || 'Documentation' %></title>
8
+ <link rel="stylesheet" href="/assets/css/main.css">
9
+ </head>
10
+ <body>
11
+ <!-- Skip to main content for accessibility -->
12
+ <a href="#main-content" class="skip-link">Skip to main content</a>
13
+
14
+ <!-- Primary Header -->
15
+ <header class="header">
16
+ <div class="header-content">
17
+ <a href="/" class="header-logo">
18
+ <span class="header-title"><%= @site_title || 'Documentation' %></span>
19
+ </a>
20
+ </div>
21
+ </header>
22
+
23
+ <!-- Secondary Header (Mobile Navigation) -->
24
+ <div class="secondary-header">
25
+ <button class="secondary-header-menu" aria-label="Toggle navigation menu" aria-expanded="false">
26
+ <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" width="18" height="18">
27
+ <path d="M32,64a8,8,0,0,1,8-8H216a8,8,0,0,1,0,16H40A8,8,0,0,1,32,64Zm8,48H168a8,8,0,0,0,0-16H40a8,8,0,0,0,0,16Zm176,24H40a8,8,0,0,0,0,16H216a8,8,0,0,0,0-16Zm-48,40H40a8,8,0,0,0,0,16H168a8,8,0,0,0,0-16Z"></path>
28
+ </svg>
29
+ <span>Menu</span>
30
+ </button>
31
+ </div>
32
+
33
+ <!-- Mobile overlay -->
34
+ <div class="mobile-overlay" aria-hidden="true"></div>
35
+
36
+ <div class="layout">
37
+ <!-- Sidebar navigation -->
38
+ <%= @sidebar_html %>
39
+
40
+ <!-- Main content area -->
41
+ <div class="layout-main">
42
+ <main id="main-content" class="content">
43
+ <%= @content %>
44
+ </main>
45
+ </div>
46
+ </div>
47
+
48
+ <script src="/assets/js/theme.js"></script>
49
+ <script src="/assets/js/reload.js"></script>
50
+ </body>
51
+ </html>
@@ -0,0 +1,61 @@
1
+ # File Structure
2
+
3
+ Understanding how Docyard organizes your documentation.
4
+
5
+ ## Basic Structure
6
+
7
+ ```bash
8
+ docs/
9
+ ├── index.md # Home page
10
+ ├── getting-started/ # Section folder
11
+ │ ├── introduction.md
12
+ │ └── quick-start.md
13
+ └── guides/ # Another section
14
+ └── tutorial.md
15
+ ```
16
+
17
+ ## Navigation Rules
18
+
19
+ - **Folders become sections** in the sidebar navigation
20
+ - **Files become pages** within those sections
21
+ - Files are sorted alphabetically
22
+ - `index.md` in the root is your home page
23
+
24
+ ## File Naming
25
+
26
+ File names are automatically converted to page titles:
27
+
28
+ - `quick-start.md` → "Quick Start"
29
+ - `api-reference.md` → "Api Reference"
30
+ - `getting_started.md` → "Getting Started"
31
+
32
+ ## Custom Titles
33
+
34
+ Override the auto-generated title using an H1 heading:
35
+
36
+ ```markdown
37
+ # Custom Page Title
38
+
39
+ Your content here...
40
+ ```
41
+
42
+ ## Hidden Files
43
+
44
+ Files and folders starting with `_` or `.` are ignored:
45
+
46
+ - `_draft.md` - Not included
47
+ - `.notes.md` - Not included
48
+ - `published.md` - Included
49
+
50
+ ## Nested Sections
51
+
52
+ You can nest folders to create sub-sections:
53
+
54
+ ```bash
55
+ docs/
56
+ └── api/
57
+ ├── classes/
58
+ │ └── generator.md
59
+ └── modules/
60
+ └── utils.md
61
+ ```
@@ -0,0 +1,90 @@
1
+ # Markdown Support
2
+
3
+ Docyard supports GitHub Flavored Markdown with syntax highlighting.
4
+
5
+ ## Headings
6
+
7
+ ```markdown
8
+ # H1 Heading
9
+ ## H2 Heading
10
+ ### H3 Heading
11
+ ```
12
+
13
+
14
+ ## Text Formatting
15
+
16
+ **Bold text** with `**bold**`
17
+ *Italic text* with `*italic*`
18
+ ~~Strikethrough~~ with `~~strikethrough~~`
19
+
20
+ ## Lists
21
+
22
+ Unordered lists:
23
+
24
+ - Item 1
25
+ - Item 2
26
+ - Nested item
27
+ - Another nested
28
+
29
+ Ordered lists:
30
+
31
+ 1. First step
32
+ 2. Second step
33
+ 3. Third step
34
+
35
+ ## Code
36
+
37
+ Inline code: `const x = 42`
38
+
39
+ Code blocks with syntax highlighting:
40
+
41
+ ```ruby
42
+ class DocumentationGenerator
43
+ def initialize(title)
44
+ @title = title
45
+ end
46
+
47
+ def build
48
+ puts "Building #{@title}..."
49
+ end
50
+ end
51
+ ```
52
+
53
+ ```javascript
54
+ function generateDocs(title) {
55
+ console.log(`Building ${title}...`);
56
+ return { success: true };
57
+ }
58
+ ```
59
+
60
+
61
+ ## Tables
62
+
63
+ | Feature | Status | Priority |
64
+ |---------|--------|----------|
65
+ | Sidebar | Done | High |
66
+ | Search | Planned | Medium |
67
+ | Dark Mode | Planned | High |
68
+
69
+ ## Links
70
+
71
+ [External link](https://github.com)
72
+ [Internal link](file-structure)
73
+ [Link to section](../getting-started/installation)
74
+
75
+ ## Blockquotes
76
+
77
+ > This is a blockquote. Use it to highlight important information or callouts.
78
+
79
+ ## Images
80
+
81
+ ```markdown
82
+ ![Alt text](/path/to/image.png)
83
+ ```
84
+
85
+
86
+ ## Task Lists
87
+
88
+ - [x] Completed task
89
+ - [ ] Incomplete task
90
+ - [ ] Another task
@@ -0,0 +1,43 @@
1
+ # Installation
2
+
3
+ Learn how to install Docyard on your system.
4
+
5
+ ## Prerequisites
6
+
7
+ - Ruby 3.2 or higher
8
+ - Bundler (recommended)
9
+
10
+ ## Install via RubyGems
11
+
12
+ ```bash
13
+ gem install docyard
14
+ ```
15
+
16
+
17
+ ## Install via Bundler
18
+
19
+ Add to your Gemfile:
20
+
21
+ ```ruby
22
+ gem 'docyard'
23
+ ```
24
+
25
+
26
+ Then run:
27
+
28
+ ```bash
29
+ bundle install
30
+ ```
31
+
32
+ ## Verify Installation
33
+
34
+ ```bash
35
+ docyard --version
36
+ ```
37
+
38
+
39
+ You should see the version number printed to your terminal.
40
+
41
+ ## Next Steps
42
+
43
+ Now that Docyard is installed, head over to the [Quick Start Guide](quick-start) to create your first documentation site.