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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +31 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/ci.yml +49 -0
- data/.rubocop.yml +38 -0
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +55 -0
- data/README.md +174 -3
- data/lib/docyard/asset_handler.rb +64 -0
- data/lib/docyard/cli.rb +34 -0
- data/lib/docyard/constants.rb +23 -0
- data/lib/docyard/errors.rb +54 -0
- data/lib/docyard/file_watcher.rb +42 -0
- data/lib/docyard/initializer.rb +76 -0
- data/lib/docyard/logging.rb +43 -0
- data/lib/docyard/markdown.rb +61 -0
- data/lib/docyard/rack_application.rb +81 -0
- data/lib/docyard/renderer.rb +61 -0
- data/lib/docyard/router.rb +31 -0
- data/lib/docyard/routing/resolution_result.rb +31 -0
- data/lib/docyard/server.rb +91 -0
- data/lib/docyard/sidebar/file_system_scanner.rb +77 -0
- data/lib/docyard/sidebar/renderer.rb +110 -0
- data/lib/docyard/sidebar/title_extractor.rb +25 -0
- data/lib/docyard/sidebar/tree_builder.rb +59 -0
- data/lib/docyard/sidebar_builder.rb +50 -0
- data/lib/docyard/templates/assets/css/code.css +214 -0
- data/lib/docyard/templates/assets/css/components.css +258 -0
- data/lib/docyard/templates/assets/css/layout.css +273 -0
- data/lib/docyard/templates/assets/css/main.css +10 -0
- data/lib/docyard/templates/assets/css/markdown.css +199 -0
- data/lib/docyard/templates/assets/css/reset.css +59 -0
- data/lib/docyard/templates/assets/css/typography.css +97 -0
- data/lib/docyard/templates/assets/css/variables.css +114 -0
- data/lib/docyard/templates/assets/js/reload.js +98 -0
- data/lib/docyard/templates/assets/js/theme.js +193 -0
- data/lib/docyard/templates/errors/404.html.erb +16 -0
- data/lib/docyard/templates/errors/500.html.erb +25 -0
- data/lib/docyard/templates/layouts/default.html.erb +51 -0
- data/lib/docyard/templates/markdown/core-concepts/file-structure.md.erb +61 -0
- data/lib/docyard/templates/markdown/core-concepts/markdown.md.erb +90 -0
- data/lib/docyard/templates/markdown/getting-started/installation.md.erb +43 -0
- data/lib/docyard/templates/markdown/getting-started/introduction.md.erb +30 -0
- data/lib/docyard/templates/markdown/getting-started/quick-start.md.erb +56 -0
- data/lib/docyard/templates/markdown/index.md.erb +86 -0
- data/lib/docyard/templates/partials/_icons.html.erb +11 -0
- data/lib/docyard/templates/partials/_nav_group.html.erb +7 -0
- data/lib/docyard/templates/partials/_nav_item.html.erb +3 -0
- data/lib/docyard/templates/partials/_nav_leaf.html.erb +1 -0
- data/lib/docyard/templates/partials/_nav_list.html.erb +3 -0
- data/lib/docyard/templates/partials/_nav_section.html.erb +6 -0
- data/lib/docyard/templates/partials/_sidebar.html.erb +6 -0
- data/lib/docyard/templates/partials/_sidebar_footer.html.erb +11 -0
- data/lib/docyard/utils/path_resolver.rb +30 -0
- data/lib/docyard/utils/text_formatter.rb +22 -0
- data/lib/docyard/version.rb +1 -1
- data/lib/docyard.rb +20 -2
- 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
|
+

|
|
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.
|