@knowcode/doc-builder 1.9.30 → 1.10.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.
- package/lib/core-builder.js +164 -174
- package/lib/emoji-mapper.js +27 -12
- package/package.json +12 -2
- package/.claude/settings.local.json +0 -56
- package/CACHE-BUSTING-GUIDE.md +0 -82
- package/CLAUDE.md +0 -86
- package/CONTRIBUTING.md +0 -148
- package/GITHUB_SETUP.md +0 -203
- package/RELEASE-NOTES-1.7.5.md +0 -64
- package/Screenshot 2025-07-22 at 19.51.21.png +0 -0
- package/Screenshot 2025-07-26 at 17.06.49.png +0 -0
- package/add-user-clive.sql +0 -35
- package/add-user-lindsay-fixed.sql +0 -85
- package/add-user-lindsay.sql +0 -68
- package/add-user-pmorgan.sql +0 -35
- package/add-user-robbie.sql +0 -35
- package/add-wru-users.sql +0 -105
- package/debug-login.sql +0 -30
- package/doc-builder.config.js +0 -126
- package/doc-builder.config.js.backup.1753793768283 +0 -47
- package/doc-builder.config.js.backup.1753803964423 +0 -114
- package/doc-builder.config.js.backup.1753945707032 +0 -115
- package/doc-builder.config.js.backup.1754059241330 +0 -115
- package/doc-builder.config.js.backup.1754119567787 +0 -123
- package/doc-builder.config.js.backup.1754120048862 +0 -124
- package/doc-builder.config.js.backup.1754120529913 +0 -124
- package/doc-builder.config.js.backup.1754218469785 +0 -124
- package/doc-builder.config.js.backup.1754384764054 +0 -124
- package/doc-builder.config.js.backup.1754567425847 +0 -124
- package/doc-builder.config.js.backup.1754568137859 +0 -126
- package/doc-builder.config.js.backup.1754569388252 +0 -126
- package/doc-builder.config.js.backup.1754576694123 +0 -126
- package/doc-builder.config.js.backup.1755031374829 +0 -126
- package/doc-builder.config.js.backup.1755034500990 +0 -126
- package/doc-builder.config.js.backup.1755034809236 +0 -126
- package/grant-access.sql +0 -15
- package/html/11.png +0 -0
- package/html/404.html +0 -115
- package/html/README.html +0 -522
- package/html/Screenshot 2025-08-12 at 21.35.07.png +0 -0
- package/html/about-doc-builder.html +0 -491
- package/html/auth.js +0 -157
- package/html/claude-workflow-guide.html +0 -525
- package/html/css/notion-style.css +0 -2502
- package/html/documentation-index.html +0 -471
- package/html/guides/authentication-default-change.html +0 -370
- package/html/guides/authentication-guide.html +0 -509
- package/html/guides/cache-control-anti-pattern.html +0 -361
- package/html/guides/claude-workflow-guide.html +0 -1074
- package/html/guides/configuration-guide.html +0 -472
- package/html/guides/document-standards.html +0 -518
- package/html/guides/documentation-standards.html +0 -694
- package/html/guides/html-embedding-guide.html +0 -461
- package/html/guides/image-modal-guide.html +0 -515
- package/html/guides/next-steps-walkthrough.html +0 -638
- package/html/guides/phosphor-icons-guide.html +0 -584
- package/html/guides/private-directory-authentication-troubleshooting.html +0 -555
- package/html/guides/private-directory-authentication.html +0 -541
- package/html/guides/public-site-deployment.html +0 -431
- package/html/guides/search-engine-verification-guide.html +0 -542
- package/html/guides/seo-guide.html +0 -661
- package/html/guides/seo-optimization-guide.html +0 -887
- package/html/guides/supabase-auth-implementation-plan.html +0 -543
- package/html/guides/supabase-auth-integration-plan.html +0 -671
- package/html/guides/supabase-auth-setup-guide.html +0 -498
- package/html/guides/supabase-authentication-complete-guide.html +0 -866
- package/html/guides/troubleshooting-guide.html +0 -633
- package/html/guides/vercel-deployment-auth-setup.html +0 -337
- package/html/guides/windows-setup-guide.html +0 -859
- package/html/image-modal-test.html +0 -318
- package/html/index.html +0 -522
- package/html/js/auth.js +0 -157
- package/html/js/main.js +0 -1754
- package/html/launch/README.html +0 -297
- package/html/launch/bubble-plugin-specification.html +0 -933
- package/html/launch/go-to-market-strategy.html +0 -663
- package/html/launch/launch-announcements.html +0 -593
- package/html/login.html +0 -102
- package/html/logout.html +0 -18
- package/html/private/cache-control-anti-pattern.html +0 -429
- package/html/private/launch/README.html +0 -371
- package/html/private/launch/auth-cleanup-summary.html +0 -361
- package/html/private/launch/bubble-plugin-specification.html +0 -1007
- package/html/private/launch/go-to-market-strategy.html +0 -737
- package/html/private/launch/launch-announcements.html +0 -667
- package/html/private/launch/vercel-deployment-auth-setup.html +0 -417
- package/html/private/next-steps-walkthrough.html +0 -679
- package/html/private/supabase-auth-implementation-completed.html +0 -454
- package/html/private/supabase-auth-implementation-plan.html +0 -594
- package/html/private/supabase-auth-integration-plan.html +0 -704
- package/html/private/supabase-auth-setup-guide.html +0 -555
- package/html/private/test-private-doc.html +0 -302
- package/html/private/user-management-tooling.html +0 -601
- package/html/prompts/Screenshot 2025-08-02 at 08.49.55.png +0 -0
- package/html/prompts/beautiful-documentation-design.html +0 -784
- package/html/prompts/markdown-document-standards.html +0 -422
- package/html/prompts/project-rename-strategy-sasha-publish.html +0 -530
- package/html/robots.txt +0 -9
- package/html/sitemap.xml +0 -357
- package/html/test-questions/how-does-it-work%3F.html +0 -294
- package/html/test-questions/step-1%3A%20getting-started.html +0 -289
- package/html/test-questions/what-is-the-purpose.html +0 -293
- package/html/test-status.html +0 -281
- package/html/vercel-cli-setup-guide.html +0 -495
- package/html/vercel-first-time-setup-guide.html +0 -454
- package/html/vercel.json +0 -29
- package/html-static/11.png +0 -0
- package/html-static/404.html +0 -115
- package/html-static/README.html +0 -609
- package/html-static/Screenshot 2025-08-12 at 21.35.07.png +0 -0
- package/html-static/about-doc-builder.html +0 -578
- package/html-static/css/notion-style.css +0 -2502
- package/html-static/documentation-index.html +0 -558
- package/html-static/guides/authentication-default-change.html +0 -457
- package/html-static/guides/authentication-guide.html +0 -596
- package/html-static/guides/claude-workflow-guide.html +0 -1161
- package/html-static/guides/configuration-guide.html +0 -559
- package/html-static/guides/documentation-standards.html +0 -781
- package/html-static/guides/html-embedding-guide.html +0 -548
- package/html-static/guides/image-modal-guide.html +0 -602
- package/html-static/guides/phosphor-icons-guide.html +0 -671
- package/html-static/guides/private-directory-authentication-troubleshooting.html +0 -642
- package/html-static/guides/private-directory-authentication.html +0 -628
- package/html-static/guides/public-site-deployment.html +0 -518
- package/html-static/guides/search-engine-verification-guide.html +0 -629
- package/html-static/guides/seo-guide.html +0 -748
- package/html-static/guides/seo-optimization-guide.html +0 -974
- package/html-static/guides/supabase-authentication-complete-guide.html +0 -953
- package/html-static/guides/troubleshooting-guide.html +0 -720
- package/html-static/guides/windows-setup-guide.html +0 -946
- package/html-static/image-modal-test.html +0 -405
- package/html-static/index.html +0 -609
- package/html-static/js/main.js +0 -1754
- package/html-static/prompts/Screenshot 2025-08-02 at 08.49.55.png +0 -0
- package/html-static/prompts/beautiful-documentation-design.html +0 -871
- package/html-static/prompts/markdown-document-standards.html +0 -509
- package/html-static/prompts/project-rename-strategy-sasha-publish.html +0 -617
- package/html-static/robots.txt +0 -5
- package/html-static/sitemap.xml +0 -195
- package/html-static/test-questions/how-does-it-work%3F.html +0 -381
- package/html-static/test-questions/step-1%3A%20getting-started.html +0 -376
- package/html-static/test-questions/what-is-the-purpose.html +0 -380
- package/html-static/vercel-cli-setup-guide.html +0 -582
- package/html-static/vercel-first-time-setup-guide.html +0 -541
- package/manage-users.sql +0 -191
- package/migrate-to-domain-auth.sql +0 -47
- package/package/CACHE-BUSTING-GUIDE.md +0 -82
- package/package/CHANGELOG.md +0 -902
- package/package/README.md +0 -248
- package/package/assets/css/notion-style.css +0 -2211
- package/package/assets/js/auth.js +0 -67
- package/package/assets/js/main.js +0 -1565
- package/package/cli.js +0 -764
- package/package/index.js +0 -38
- package/package/knowcode-doc-builder-1.3.15.tgz +0 -0
- package/package/lib/builder.js +0 -32
- package/package/lib/config.js +0 -278
- package/package/lib/core-builder.js +0 -957
- package/package/lib/deploy.js +0 -497
- package/package/lib/dev-server.js +0 -96
- package/package/package.json +0 -34
- package/package/scripts/npx-runner.js +0 -27
- package/package/scripts/setup.js +0 -56
- package/package/test-cache-bust.sh +0 -43
- package/public-config.js +0 -22
- package/public-html/404.html +0 -115
- package/public-html/README.html +0 -149
- package/public-html/css/notion-style.css +0 -2036
- package/public-html/index.html +0 -149
- package/public-html/js/auth.js +0 -67
- package/public-html/js/main.js +0 -1485
- package/quick-test-commands.md +0 -40
- package/recordings/Screenshot 2025-07-24 at 18.22.01.png +0 -0
- package/recordings/mh-ls-22jul.txt +0 -2305
- package/screenshot.png +0 -0
- package/scripts/Screenshot 2025-07-23 at 15.39.41.png +0 -0
- package/setup-database-v2.sql +0 -53
- package/setup-database.sql +0 -41
- package/test-auth-config.js +0 -17
- package/test-cache-bust.sh +0 -43
- package/test-docs/README.md +0 -39
- package/test-html/404.html +0 -115
- package/test-html/README.html +0 -172
- package/test-html/auth.js +0 -97
- package/test-html/css/notion-style.css +0 -2036
- package/test-html/index.html +0 -172
- package/test-html/js/auth.js +0 -97
- package/test-html/js/main.js +0 -1485
- package/test-html/login.html +0 -102
- package/test-html/logout.html +0 -18
- package/update-domain.sql +0 -9
- package/user-access-view.sql +0 -49
- package/user-management/README.md +0 -301
- package/user-management/add-users.sh +0 -776
- package/user-management/create-user.js +0 -65
- package/user-management/users.txt +0 -15
- package/view-all-users.sql +0 -40
- package/wru-auth-config.js +0 -17
package/public-html/js/main.js
DELETED
|
@@ -1,1485 +0,0 @@
|
|
|
1
|
-
// Documentation Builder - Main JavaScript
|
|
2
|
-
|
|
3
|
-
// Preview Banner Management
|
|
4
|
-
// Set up banner state immediately to prevent flash
|
|
5
|
-
const bannerDismissed = localStorage.getItem('banner-dismissed') === 'true';
|
|
6
|
-
|
|
7
|
-
// Apply styles immediately if banner should be visible
|
|
8
|
-
if (!bannerDismissed) {
|
|
9
|
-
document.documentElement.style.setProperty('--banner-offset', '3.5rem');
|
|
10
|
-
} else {
|
|
11
|
-
document.documentElement.style.setProperty('--banner-offset', '0rem');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
15
|
-
const banner = document.getElementById('preview-banner');
|
|
16
|
-
const dismissButton = document.getElementById('dismiss-banner');
|
|
17
|
-
const mainWrapper = document.querySelector('.main-wrapper');
|
|
18
|
-
const sidebar = document.querySelector('.sidebar');
|
|
19
|
-
const breadcrumbs = document.querySelector('.breadcrumbs');
|
|
20
|
-
|
|
21
|
-
if (bannerDismissed) {
|
|
22
|
-
banner.classList.add('hidden');
|
|
23
|
-
} else {
|
|
24
|
-
// Show banner and adjust layout
|
|
25
|
-
banner.classList.add('visible');
|
|
26
|
-
mainWrapper.classList.add('banner-visible');
|
|
27
|
-
sidebar.classList.add('banner-visible');
|
|
28
|
-
breadcrumbs?.classList.add('banner-visible');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Handle banner dismissal
|
|
32
|
-
if (dismissButton) {
|
|
33
|
-
dismissButton.addEventListener('click', function() {
|
|
34
|
-
banner.classList.remove('visible');
|
|
35
|
-
banner.classList.add('hidden');
|
|
36
|
-
mainWrapper.classList.remove('banner-visible');
|
|
37
|
-
sidebar.classList.remove('banner-visible');
|
|
38
|
-
breadcrumbs?.classList.remove('banner-visible');
|
|
39
|
-
document.documentElement.style.setProperty('--banner-offset', '0rem');
|
|
40
|
-
|
|
41
|
-
// Remember that the banner was dismissed
|
|
42
|
-
localStorage.setItem('banner-dismissed', 'true');
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Handle Escape key to dismiss banner
|
|
47
|
-
document.addEventListener('keydown', function(e) {
|
|
48
|
-
if (e.key === 'Escape' && banner.classList.contains('visible')) {
|
|
49
|
-
dismissButton.click();
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Initialize Mermaid Full Screen Functionality
|
|
54
|
-
initializeMermaidFullScreen();
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// Mermaid Full Screen Viewer
|
|
58
|
-
function initializeMermaidFullScreen() {
|
|
59
|
-
// Wait for Mermaid to initialize
|
|
60
|
-
if (typeof mermaid === 'undefined') {
|
|
61
|
-
setTimeout(initializeMermaidFullScreen, 100);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Find all Mermaid diagrams and wrap them with full-screen controls
|
|
66
|
-
const mermaidDivs = document.querySelectorAll('.mermaid');
|
|
67
|
-
|
|
68
|
-
mermaidDivs.forEach((mermaidDiv, index) => {
|
|
69
|
-
// Skip if already processed
|
|
70
|
-
if (mermaidDiv.closest('.mermaid-container')) {
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Create container
|
|
75
|
-
const container = document.createElement('div');
|
|
76
|
-
container.className = 'mermaid-container';
|
|
77
|
-
|
|
78
|
-
// Create toolbar
|
|
79
|
-
const toolbar = document.createElement('div');
|
|
80
|
-
toolbar.className = 'mermaid-toolbar';
|
|
81
|
-
|
|
82
|
-
const actions = document.createElement('div');
|
|
83
|
-
actions.className = 'mermaid-actions';
|
|
84
|
-
|
|
85
|
-
// Full screen button
|
|
86
|
-
const fullScreenBtn = document.createElement('button');
|
|
87
|
-
fullScreenBtn.className = 'mermaid-btn';
|
|
88
|
-
fullScreenBtn.innerHTML = '<i class="fas fa-expand"></i> Full Screen';
|
|
89
|
-
fullScreenBtn.addEventListener('click', () => openMermaidFullScreen(mermaidDiv, index));
|
|
90
|
-
|
|
91
|
-
actions.appendChild(fullScreenBtn);
|
|
92
|
-
|
|
93
|
-
toolbar.appendChild(actions);
|
|
94
|
-
|
|
95
|
-
// Create wrapper for the diagram
|
|
96
|
-
const wrapper = document.createElement('div');
|
|
97
|
-
wrapper.className = 'mermaid-wrapper';
|
|
98
|
-
|
|
99
|
-
// Insert container before mermaid div
|
|
100
|
-
mermaidDiv.parentNode.insertBefore(container, mermaidDiv);
|
|
101
|
-
|
|
102
|
-
// Move mermaid div into wrapper
|
|
103
|
-
wrapper.appendChild(mermaidDiv);
|
|
104
|
-
|
|
105
|
-
// Assemble container
|
|
106
|
-
container.appendChild(toolbar);
|
|
107
|
-
container.appendChild(wrapper);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// Create fullscreen modal (only once)
|
|
111
|
-
if (!document.getElementById('mermaid-fullscreen-modal')) {
|
|
112
|
-
createMermaidFullScreenModal();
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function createMermaidFullScreenModal() {
|
|
117
|
-
const modal = document.createElement('div');
|
|
118
|
-
modal.id = 'mermaid-fullscreen-modal';
|
|
119
|
-
modal.className = 'mermaid-fullscreen';
|
|
120
|
-
|
|
121
|
-
modal.innerHTML = `
|
|
122
|
-
<div class="mermaid-fullscreen-toolbar">
|
|
123
|
-
<div class="mermaid-fullscreen-title">Mermaid Diagram - Full Screen View</div>
|
|
124
|
-
<div class="mermaid-fullscreen-controls">
|
|
125
|
-
<div class="mermaid-zoom-controls">
|
|
126
|
-
<button class="mermaid-zoom-btn" id="zoom-out">
|
|
127
|
-
<i class="fas fa-minus"></i>
|
|
128
|
-
</button>
|
|
129
|
-
<div class="mermaid-zoom-level" id="zoom-level">100%</div>
|
|
130
|
-
<button class="mermaid-zoom-btn" id="zoom-in">
|
|
131
|
-
<i class="fas fa-plus"></i>
|
|
132
|
-
</button>
|
|
133
|
-
<button class="mermaid-zoom-btn" id="zoom-reset">
|
|
134
|
-
<i class="fas fa-expand-arrows-alt"></i>
|
|
135
|
-
</button>
|
|
136
|
-
</div>
|
|
137
|
-
<button class="mermaid-close-btn" id="close-fullscreen">
|
|
138
|
-
<i class="fas fa-times"></i> Close
|
|
139
|
-
</button>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
<div class="mermaid-fullscreen-content">
|
|
143
|
-
<div class="mermaid-fullscreen-wrapper" id="fullscreen-wrapper">
|
|
144
|
-
<div class="mermaid-fullscreen-diagram" id="fullscreen-diagram">
|
|
145
|
-
<!-- Diagram will be inserted here -->
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
`;
|
|
150
|
-
|
|
151
|
-
document.body.appendChild(modal);
|
|
152
|
-
|
|
153
|
-
// Set up event listeners - store zoom in modal element
|
|
154
|
-
modal.currentZoom = 1;
|
|
155
|
-
const wrapper = document.getElementById('fullscreen-wrapper');
|
|
156
|
-
const zoomLevel = document.getElementById('zoom-level');
|
|
157
|
-
|
|
158
|
-
function updateZoom() {
|
|
159
|
-
const currentZoom = modal.currentZoom || 1;
|
|
160
|
-
wrapper.style.transform = `scale(${currentZoom})`;
|
|
161
|
-
zoomLevel.textContent = `${Math.round(currentZoom * 100)}%`;
|
|
162
|
-
|
|
163
|
-
if (currentZoom > 1) {
|
|
164
|
-
wrapper.classList.add('zoomed');
|
|
165
|
-
} else {
|
|
166
|
-
wrapper.classList.remove('zoomed');
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Zoom controls
|
|
171
|
-
document.getElementById('zoom-in').addEventListener('click', () => {
|
|
172
|
-
modal.currentZoom = Math.min((modal.currentZoom || 1) + 0.25, 3);
|
|
173
|
-
updateZoom();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
document.getElementById('zoom-out').addEventListener('click', () => {
|
|
177
|
-
modal.currentZoom = Math.max((modal.currentZoom || 1) - 0.25, 0.25);
|
|
178
|
-
updateZoom();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
document.getElementById('zoom-reset').addEventListener('click', () => {
|
|
182
|
-
modal.currentZoom = 1;
|
|
183
|
-
updateZoom();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Close functionality
|
|
187
|
-
document.getElementById('close-fullscreen').addEventListener('click', closeMermaidFullScreen);
|
|
188
|
-
|
|
189
|
-
// Close on backdrop click
|
|
190
|
-
modal.addEventListener('click', (e) => {
|
|
191
|
-
if (e.target === modal) {
|
|
192
|
-
closeMermaidFullScreen();
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Close on Escape key
|
|
197
|
-
document.addEventListener('keydown', (e) => {
|
|
198
|
-
if (e.key === 'Escape' && modal.classList.contains('active')) {
|
|
199
|
-
closeMermaidFullScreen();
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function openMermaidFullScreen(mermaidDiv, index) {
|
|
205
|
-
const modal = document.getElementById('mermaid-fullscreen-modal');
|
|
206
|
-
const diagramContainer = document.getElementById('fullscreen-diagram');
|
|
207
|
-
const wrapper = document.getElementById('fullscreen-wrapper');
|
|
208
|
-
const zoomLevel = document.getElementById('zoom-level');
|
|
209
|
-
|
|
210
|
-
// Reset zoom to 100% when opening new diagram
|
|
211
|
-
modal.currentZoom = 1;
|
|
212
|
-
wrapper.style.transform = `scale(${modal.currentZoom})`;
|
|
213
|
-
zoomLevel.textContent = `${Math.round(modal.currentZoom * 100)}%`;
|
|
214
|
-
wrapper.classList.remove('zoomed');
|
|
215
|
-
|
|
216
|
-
// Clone the mermaid diagram
|
|
217
|
-
const clonedDiagram = mermaidDiv.cloneNode(true);
|
|
218
|
-
|
|
219
|
-
// Reset all styles that might interfere
|
|
220
|
-
clonedDiagram.style.cssText = '';
|
|
221
|
-
|
|
222
|
-
// Find the SVG and make it scale properly
|
|
223
|
-
const svg = clonedDiagram.querySelector('svg');
|
|
224
|
-
if (svg) {
|
|
225
|
-
// Store original dimensions for reference
|
|
226
|
-
const originalWidth = svg.getAttribute('width');
|
|
227
|
-
const originalHeight = svg.getAttribute('height');
|
|
228
|
-
const originalViewBox = svg.getAttribute('viewBox');
|
|
229
|
-
|
|
230
|
-
// Reset SVG styles
|
|
231
|
-
svg.style.cssText = '';
|
|
232
|
-
|
|
233
|
-
// Ensure we have a proper viewBox for scaling
|
|
234
|
-
if (!originalViewBox && originalWidth && originalHeight) {
|
|
235
|
-
svg.setAttribute('viewBox', `0 0 ${originalWidth} ${originalHeight}`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Remove fixed dimensions to enable responsive scaling
|
|
239
|
-
svg.removeAttribute('width');
|
|
240
|
-
svg.removeAttribute('height');
|
|
241
|
-
|
|
242
|
-
// Set responsive attributes
|
|
243
|
-
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
244
|
-
|
|
245
|
-
// Apply CSS for proper scaling
|
|
246
|
-
svg.style.width = '100%';
|
|
247
|
-
svg.style.height = '100%';
|
|
248
|
-
svg.style.maxWidth = '100%';
|
|
249
|
-
svg.style.maxHeight = '100%';
|
|
250
|
-
svg.style.display = 'block';
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Apply proper styles to the cloned diagram
|
|
254
|
-
clonedDiagram.style.width = '100%';
|
|
255
|
-
clonedDiagram.style.height = '100%';
|
|
256
|
-
clonedDiagram.style.display = 'flex';
|
|
257
|
-
clonedDiagram.style.justifyContent = 'center';
|
|
258
|
-
clonedDiagram.style.alignItems = 'center';
|
|
259
|
-
|
|
260
|
-
// Clear previous content and add new diagram
|
|
261
|
-
diagramContainer.innerHTML = '';
|
|
262
|
-
diagramContainer.appendChild(clonedDiagram);
|
|
263
|
-
|
|
264
|
-
// Show modal
|
|
265
|
-
modal.classList.add('active');
|
|
266
|
-
document.body.style.overflow = 'hidden';
|
|
267
|
-
|
|
268
|
-
// Update title
|
|
269
|
-
const title = document.querySelector('.mermaid-fullscreen-title');
|
|
270
|
-
const container = mermaidDiv.closest('.mermaid-container');
|
|
271
|
-
const originalTitle = container ? container.querySelector('.mermaid-toolbar div').textContent : 'Mermaid Diagram';
|
|
272
|
-
title.textContent = `${originalTitle} - Full Screen View`;
|
|
273
|
-
|
|
274
|
-
// Debug logging
|
|
275
|
-
console.log('Fullscreen opened with diagram:', clonedDiagram);
|
|
276
|
-
console.log('SVG found:', svg);
|
|
277
|
-
if (svg) {
|
|
278
|
-
console.log('SVG viewBox:', svg.getAttribute('viewBox'));
|
|
279
|
-
console.log('SVG dimensions:', svg.getBoundingClientRect());
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function closeMermaidFullScreen() {
|
|
284
|
-
const modal = document.getElementById('mermaid-fullscreen-modal');
|
|
285
|
-
modal.classList.remove('active');
|
|
286
|
-
document.body.style.overflow = '';
|
|
287
|
-
|
|
288
|
-
// Clear diagram content
|
|
289
|
-
setTimeout(() => {
|
|
290
|
-
const diagramContainer = document.getElementById('fullscreen-diagram');
|
|
291
|
-
diagramContainer.innerHTML = '';
|
|
292
|
-
}, 300);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function copyMermaidSVG(mermaidDiv) {
|
|
296
|
-
try {
|
|
297
|
-
const svg = mermaidDiv.querySelector('svg');
|
|
298
|
-
if (svg) {
|
|
299
|
-
const svgString = new XMLSerializer().serializeToString(svg);
|
|
300
|
-
|
|
301
|
-
// Try to use the modern clipboard API
|
|
302
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
303
|
-
navigator.clipboard.writeText(svgString).then(() => {
|
|
304
|
-
showCopySuccess('SVG copied to clipboard!');
|
|
305
|
-
}).catch(() => {
|
|
306
|
-
fallbackCopy(svgString, 'SVG');
|
|
307
|
-
});
|
|
308
|
-
} else {
|
|
309
|
-
fallbackCopy(svgString, 'SVG');
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
console.error('Error copying SVG:', error);
|
|
314
|
-
showCopyError();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function copyMermaidSource(mermaidDiv) {
|
|
319
|
-
try {
|
|
320
|
-
// Find the original Mermaid source code
|
|
321
|
-
let mermaidSource = '';
|
|
322
|
-
|
|
323
|
-
// Try to get from data attribute first
|
|
324
|
-
if (mermaidDiv.dataset.mermaidSource) {
|
|
325
|
-
mermaidSource = mermaidDiv.dataset.mermaidSource;
|
|
326
|
-
} else {
|
|
327
|
-
// Try to find in the page content - look for the nearest pre code block
|
|
328
|
-
const container = mermaidDiv.closest('.content');
|
|
329
|
-
if (container) {
|
|
330
|
-
const codeBlocks = container.querySelectorAll('pre code');
|
|
331
|
-
for (const block of codeBlocks) {
|
|
332
|
-
if (block.textContent.includes('flowchart') || block.textContent.includes('graph')) {
|
|
333
|
-
mermaidSource = block.textContent;
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Fallback: extract from the SVG if available
|
|
340
|
-
if (!mermaidSource) {
|
|
341
|
-
const svg = mermaidDiv.querySelector('svg');
|
|
342
|
-
if (svg) {
|
|
343
|
-
// Try to reconstruct basic Mermaid from SVG elements
|
|
344
|
-
mermaidSource = reconstructMermaidFromSVG(svg);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (mermaidSource) {
|
|
350
|
-
// Try to use the modern clipboard API
|
|
351
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
352
|
-
navigator.clipboard.writeText(mermaidSource).then(() => {
|
|
353
|
-
showCopySuccess('Mermaid source copied to clipboard!');
|
|
354
|
-
}).catch(() => {
|
|
355
|
-
fallbackCopy(mermaidSource, 'Mermaid');
|
|
356
|
-
});
|
|
357
|
-
} else {
|
|
358
|
-
fallbackCopy(mermaidSource, 'Mermaid');
|
|
359
|
-
}
|
|
360
|
-
} else {
|
|
361
|
-
showCopyError('Could not find Mermaid source');
|
|
362
|
-
}
|
|
363
|
-
} catch (error) {
|
|
364
|
-
console.error('Error copying Mermaid source:', error);
|
|
365
|
-
showCopyError();
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function reconstructMermaidFromSVG(svg) {
|
|
370
|
-
// Basic reconstruction - this is a fallback method
|
|
371
|
-
let mermaidCode = 'flowchart TD\n';
|
|
372
|
-
|
|
373
|
-
// Try to extract node information from SVG
|
|
374
|
-
const nodes = svg.querySelectorAll('g.node');
|
|
375
|
-
const edges = svg.querySelectorAll('g.edgePath');
|
|
376
|
-
|
|
377
|
-
// Add nodes
|
|
378
|
-
nodes.forEach((node, index) => {
|
|
379
|
-
const label = node.querySelector('span, text, foreignObject');
|
|
380
|
-
if (label) {
|
|
381
|
-
const nodeText = label.textContent.trim();
|
|
382
|
-
const nodeId = `N${index + 1}`;
|
|
383
|
-
|
|
384
|
-
if (nodeText.includes('?')) {
|
|
385
|
-
mermaidCode += ` ${nodeId}{{"${nodeText}"}}\n`;
|
|
386
|
-
} else {
|
|
387
|
-
mermaidCode += ` ${nodeId}["${nodeText}"]\n`;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
mermaidCode += '\n %% Note: This is a reconstructed version - original source may differ\n';
|
|
393
|
-
|
|
394
|
-
return mermaidCode;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function fallbackCopy(text, type = 'content') {
|
|
398
|
-
// Fallback for older browsers
|
|
399
|
-
const textArea = document.createElement('textarea');
|
|
400
|
-
textArea.value = text;
|
|
401
|
-
textArea.style.position = 'fixed';
|
|
402
|
-
textArea.style.opacity = '0';
|
|
403
|
-
document.body.appendChild(textArea);
|
|
404
|
-
textArea.select();
|
|
405
|
-
|
|
406
|
-
try {
|
|
407
|
-
document.execCommand('copy');
|
|
408
|
-
showCopySuccess(`${type} copied to clipboard!`);
|
|
409
|
-
} catch (error) {
|
|
410
|
-
showCopyError(`Failed to copy ${type.toLowerCase()}`);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
document.body.removeChild(textArea);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function showCopySuccess(message = 'Content copied to clipboard!') {
|
|
417
|
-
// Create temporary success message
|
|
418
|
-
const messageDiv = document.createElement('div');
|
|
419
|
-
messageDiv.textContent = message;
|
|
420
|
-
messageDiv.style.cssText = `
|
|
421
|
-
position: fixed;
|
|
422
|
-
top: 50%;
|
|
423
|
-
left: 50%;
|
|
424
|
-
transform: translate(-50%, -50%);
|
|
425
|
-
background: var(--success);
|
|
426
|
-
color: white;
|
|
427
|
-
padding: 1rem 2rem;
|
|
428
|
-
border-radius: 0.5rem;
|
|
429
|
-
z-index: 10001;
|
|
430
|
-
font-size: 0.875rem;
|
|
431
|
-
box-shadow: var(--shadow-lg);
|
|
432
|
-
max-width: 300px;
|
|
433
|
-
text-align: center;
|
|
434
|
-
`;
|
|
435
|
-
|
|
436
|
-
document.body.appendChild(messageDiv);
|
|
437
|
-
|
|
438
|
-
setTimeout(() => {
|
|
439
|
-
messageDiv.remove();
|
|
440
|
-
}, 2000);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function showCopyError(message = 'Failed to copy content') {
|
|
444
|
-
// Create temporary error message
|
|
445
|
-
const messageDiv = document.createElement('div');
|
|
446
|
-
messageDiv.textContent = message;
|
|
447
|
-
messageDiv.style.cssText = `
|
|
448
|
-
position: fixed;
|
|
449
|
-
top: 50%;
|
|
450
|
-
left: 50%;
|
|
451
|
-
transform: translate(-50%, -50%);
|
|
452
|
-
background: var(--danger);
|
|
453
|
-
color: white;
|
|
454
|
-
padding: 1rem 2rem;
|
|
455
|
-
border-radius: 0.5rem;
|
|
456
|
-
z-index: 10001;
|
|
457
|
-
font-size: 0.875rem;
|
|
458
|
-
box-shadow: var(--shadow-lg);
|
|
459
|
-
max-width: 300px;
|
|
460
|
-
text-align: center;
|
|
461
|
-
`;
|
|
462
|
-
|
|
463
|
-
document.body.appendChild(messageDiv);
|
|
464
|
-
|
|
465
|
-
setTimeout(() => {
|
|
466
|
-
messageDiv.remove();
|
|
467
|
-
}, 2000);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Theme Management
|
|
471
|
-
const themeToggle = document.getElementById('theme-toggle');
|
|
472
|
-
const html = document.documentElement;
|
|
473
|
-
|
|
474
|
-
// Check for saved theme preference or default to 'light'
|
|
475
|
-
const currentTheme = localStorage.getItem('theme') || 'light';
|
|
476
|
-
html.setAttribute('data-theme', currentTheme);
|
|
477
|
-
updateThemeIcon(currentTheme);
|
|
478
|
-
|
|
479
|
-
themeToggle.addEventListener('click', () => {
|
|
480
|
-
const newTheme = html.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
|
|
481
|
-
html.setAttribute('data-theme', newTheme);
|
|
482
|
-
localStorage.setItem('theme', newTheme);
|
|
483
|
-
updateThemeIcon(newTheme);
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
function updateThemeIcon(theme) {
|
|
487
|
-
const icon = themeToggle.querySelector('i');
|
|
488
|
-
if (icon) {
|
|
489
|
-
icon.className = theme === 'light' ? 'fas fa-moon' : 'fas fa-sun';
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Mobile Menu Toggle
|
|
494
|
-
const menuToggle = document.getElementById('menu-toggle');
|
|
495
|
-
const sidebar = document.querySelector('.sidebar');
|
|
496
|
-
|
|
497
|
-
// Set initial menu state based on configuration
|
|
498
|
-
const menuDefaultOpen = window.docBuilderConfig?.features?.menuDefaultOpen !== false;
|
|
499
|
-
if (sidebar && window.innerWidth > 768) {
|
|
500
|
-
if (!menuDefaultOpen) {
|
|
501
|
-
sidebar.classList.add('closed');
|
|
502
|
-
// Add class to body to show menu toggle on desktop when menu starts closed
|
|
503
|
-
document.body.classList.add('menu-starts-closed');
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Create overlay element for mobile
|
|
508
|
-
let overlay = document.querySelector('.sidebar-overlay');
|
|
509
|
-
if (!overlay && window.innerWidth <= 768) {
|
|
510
|
-
overlay = document.createElement('div');
|
|
511
|
-
overlay.className = 'sidebar-overlay';
|
|
512
|
-
document.body.appendChild(overlay);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (menuToggle) {
|
|
516
|
-
menuToggle.addEventListener('click', () => {
|
|
517
|
-
if (window.innerWidth <= 768) {
|
|
518
|
-
// Mobile: toggle 'open' class
|
|
519
|
-
sidebar.classList.toggle('open');
|
|
520
|
-
} else {
|
|
521
|
-
// Desktop: toggle 'closed' class
|
|
522
|
-
sidebar.classList.toggle('closed');
|
|
523
|
-
// Update visibility of menu toggle based on sidebar state
|
|
524
|
-
updateMenuToggleVisibility();
|
|
525
|
-
}
|
|
526
|
-
if (overlay) {
|
|
527
|
-
overlay.classList.toggle('active');
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// Function to update menu toggle visibility
|
|
533
|
-
function updateMenuToggleVisibility() {
|
|
534
|
-
if (window.innerWidth > 768) {
|
|
535
|
-
if (!menuDefaultOpen || sidebar.classList.contains('closed')) {
|
|
536
|
-
document.body.classList.add('show-menu-toggle');
|
|
537
|
-
} else {
|
|
538
|
-
document.body.classList.remove('show-menu-toggle');
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Initial check
|
|
544
|
-
updateMenuToggleVisibility();
|
|
545
|
-
|
|
546
|
-
// Update on window resize
|
|
547
|
-
window.addEventListener('resize', updateMenuToggleVisibility);
|
|
548
|
-
|
|
549
|
-
// Close menu when clicking overlay
|
|
550
|
-
if (overlay) {
|
|
551
|
-
overlay.addEventListener('click', () => {
|
|
552
|
-
sidebar.classList.remove('open');
|
|
553
|
-
overlay.classList.remove('active');
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// Floating Menu Button for Mobile
|
|
558
|
-
function initFloatingMenuButton() {
|
|
559
|
-
// Only initialize on mobile
|
|
560
|
-
if (window.innerWidth > 768) return;
|
|
561
|
-
|
|
562
|
-
// Check if button already exists
|
|
563
|
-
if (document.getElementById('floating-menu-toggle')) return;
|
|
564
|
-
|
|
565
|
-
// Create floating button
|
|
566
|
-
const floatingButton = document.createElement('button');
|
|
567
|
-
floatingButton.id = 'floating-menu-toggle';
|
|
568
|
-
floatingButton.className = 'floating-menu-toggle';
|
|
569
|
-
floatingButton.setAttribute('aria-label', 'Toggle menu');
|
|
570
|
-
floatingButton.innerHTML = '<i class="fas fa-bars"></i>';
|
|
571
|
-
floatingButton.style.display = 'flex'; // Always visible on mobile
|
|
572
|
-
floatingButton.classList.add('visible'); // Start visible
|
|
573
|
-
|
|
574
|
-
// Add to body
|
|
575
|
-
document.body.appendChild(floatingButton);
|
|
576
|
-
|
|
577
|
-
// Toggle sidebar on click
|
|
578
|
-
floatingButton.addEventListener('click', () => {
|
|
579
|
-
sidebar.classList.toggle('open');
|
|
580
|
-
|
|
581
|
-
// Handle overlay
|
|
582
|
-
let overlay = document.querySelector('.sidebar-overlay');
|
|
583
|
-
if (!overlay) {
|
|
584
|
-
overlay = document.createElement('div');
|
|
585
|
-
overlay.className = 'sidebar-overlay';
|
|
586
|
-
document.body.appendChild(overlay);
|
|
587
|
-
|
|
588
|
-
// Add overlay click handler
|
|
589
|
-
overlay.addEventListener('click', () => {
|
|
590
|
-
sidebar.classList.remove('open');
|
|
591
|
-
overlay.classList.remove('active');
|
|
592
|
-
floatingButton.querySelector('i').className = 'fas fa-bars';
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (overlay) {
|
|
597
|
-
overlay.classList.toggle('active');
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// Update icon based on state
|
|
601
|
-
const icon = floatingButton.querySelector('i');
|
|
602
|
-
if (sidebar.classList.contains('open')) {
|
|
603
|
-
icon.className = 'fas fa-times';
|
|
604
|
-
} else {
|
|
605
|
-
icon.className = 'fas fa-bars';
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
// Remove scroll-based visibility - button is always visible on mobile
|
|
610
|
-
|
|
611
|
-
// Update icon when sidebar state changes from other sources
|
|
612
|
-
const observer = new MutationObserver(() => {
|
|
613
|
-
const icon = floatingButton.querySelector('i');
|
|
614
|
-
if (sidebar.classList.contains('open')) {
|
|
615
|
-
icon.className = 'fas fa-times';
|
|
616
|
-
} else {
|
|
617
|
-
icon.className = 'fas fa-bars';
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
observer.observe(sidebar, {
|
|
622
|
-
attributes: true,
|
|
623
|
-
attributeFilter: ['class']
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Initialize floating button on load and resize
|
|
628
|
-
document.addEventListener('DOMContentLoaded', initFloatingMenuButton);
|
|
629
|
-
window.addEventListener('resize', () => {
|
|
630
|
-
const existingButton = document.getElementById('floating-menu-toggle');
|
|
631
|
-
if (window.innerWidth > 768 && existingButton) {
|
|
632
|
-
existingButton.remove();
|
|
633
|
-
} else if (window.innerWidth <= 768 && !existingButton) {
|
|
634
|
-
initFloatingMenuButton();
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
// Prevent sidebar from closing when clicking nav items
|
|
639
|
-
// Only close when clicking outside the sidebar or the close button
|
|
640
|
-
document.addEventListener('click', (e) => {
|
|
641
|
-
// Check if we're on mobile
|
|
642
|
-
if (window.innerWidth <= 768) {
|
|
643
|
-
const isClickInsideSidebar = sidebar && sidebar.contains(e.target);
|
|
644
|
-
const isMenuToggle = e.target.closest('#menu-toggle');
|
|
645
|
-
const isFloatingButton = e.target.closest('#floating-menu-toggle');
|
|
646
|
-
const isNavItem = e.target.closest('.nav-item, .nav-title');
|
|
647
|
-
const overlay = document.querySelector('.sidebar-overlay');
|
|
648
|
-
|
|
649
|
-
// Close sidebar only if clicking outside AND not on menu toggle AND not on nav items
|
|
650
|
-
if (!isClickInsideSidebar && !isMenuToggle && !isFloatingButton && !isNavItem && sidebar?.classList.contains('open')) {
|
|
651
|
-
sidebar.classList.remove('open');
|
|
652
|
-
if (overlay) {
|
|
653
|
-
overlay.classList.remove('active');
|
|
654
|
-
}
|
|
655
|
-
// Update floating button icon if it exists
|
|
656
|
-
const floatingBtn = document.getElementById('floating-menu-toggle');
|
|
657
|
-
if (floatingBtn) {
|
|
658
|
-
floatingBtn.querySelector('i').className = 'fas fa-bars';
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
// Smooth Scrolling for Anchor Links
|
|
665
|
-
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
666
|
-
anchor.addEventListener('click', function (e) {
|
|
667
|
-
e.preventDefault();
|
|
668
|
-
const target = document.querySelector(this.getAttribute('href'));
|
|
669
|
-
if (target) {
|
|
670
|
-
target.scrollIntoView({
|
|
671
|
-
behavior: 'smooth',
|
|
672
|
-
block: 'start'
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
// Active Navigation Highlighting
|
|
679
|
-
const sections = document.querySelectorAll('section[id]');
|
|
680
|
-
const navItems = document.querySelectorAll('.nav-item');
|
|
681
|
-
|
|
682
|
-
function highlightNavigation() {
|
|
683
|
-
const scrollY = window.pageYOffset;
|
|
684
|
-
|
|
685
|
-
sections.forEach(section => {
|
|
686
|
-
const sectionHeight = section.offsetHeight;
|
|
687
|
-
const sectionTop = section.offsetTop - 100;
|
|
688
|
-
const sectionId = section.getAttribute('id');
|
|
689
|
-
|
|
690
|
-
if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
|
|
691
|
-
navItems.forEach(item => {
|
|
692
|
-
item.classList.remove('active');
|
|
693
|
-
if (item.getAttribute('href') === `#${sectionId}`) {
|
|
694
|
-
item.classList.add('active');
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
window.addEventListener('scroll', highlightNavigation);
|
|
702
|
-
|
|
703
|
-
// Search Functionality (Basic Implementation)
|
|
704
|
-
const searchInput = document.getElementById('search-input');
|
|
705
|
-
const searchResults = document.getElementById('search-results');
|
|
706
|
-
|
|
707
|
-
if (searchInput) {
|
|
708
|
-
searchInput.addEventListener('input', (e) => {
|
|
709
|
-
const query = e.target.value.toLowerCase();
|
|
710
|
-
|
|
711
|
-
if (query.length < 2) {
|
|
712
|
-
searchResults.style.display = 'none';
|
|
713
|
-
return;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
// This would be replaced with actual search logic
|
|
717
|
-
performSearch(query);
|
|
718
|
-
});
|
|
719
|
-
|
|
720
|
-
// Close search results when clicking outside
|
|
721
|
-
document.addEventListener('click', (e) => {
|
|
722
|
-
if (!e.target.closest('.search-box')) {
|
|
723
|
-
searchResults.style.display = 'none';
|
|
724
|
-
}
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function performSearch(query) {
|
|
729
|
-
// Placeholder for search functionality
|
|
730
|
-
// In a real implementation, this would search through all content
|
|
731
|
-
searchResults.innerHTML = `
|
|
732
|
-
<div class="search-result-item">
|
|
733
|
-
<strong>Search results for "${query}"</strong>
|
|
734
|
-
<p>Search functionality will be implemented here...</p>
|
|
735
|
-
</div>
|
|
736
|
-
`;
|
|
737
|
-
searchResults.style.display = 'block';
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
// Copy Code Blocks
|
|
741
|
-
document.querySelectorAll('pre').forEach(block => {
|
|
742
|
-
const wrapper = document.createElement('div');
|
|
743
|
-
wrapper.className = 'code-block-wrapper';
|
|
744
|
-
block.parentNode.insertBefore(wrapper, block);
|
|
745
|
-
wrapper.appendChild(block);
|
|
746
|
-
|
|
747
|
-
const button = document.createElement('button');
|
|
748
|
-
button.className = 'copy-button';
|
|
749
|
-
button.textContent = 'Copy';
|
|
750
|
-
wrapper.appendChild(button);
|
|
751
|
-
|
|
752
|
-
button.addEventListener('click', () => {
|
|
753
|
-
const code = block.textContent;
|
|
754
|
-
navigator.clipboard.writeText(code).then(() => {
|
|
755
|
-
button.textContent = 'Copied!';
|
|
756
|
-
setTimeout(() => {
|
|
757
|
-
button.textContent = 'Copy';
|
|
758
|
-
}, 2000);
|
|
759
|
-
});
|
|
760
|
-
});
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
// Table of Contents Generation
|
|
764
|
-
function generateTableOfContents() {
|
|
765
|
-
const toc = document.getElementById('table-of-contents');
|
|
766
|
-
if (!toc) return;
|
|
767
|
-
|
|
768
|
-
const headings = document.querySelectorAll('.content h2, .content h3');
|
|
769
|
-
const tocList = document.createElement('ul');
|
|
770
|
-
tocList.className = 'toc-list';
|
|
771
|
-
|
|
772
|
-
headings.forEach(heading => {
|
|
773
|
-
const li = document.createElement('li');
|
|
774
|
-
const a = document.createElement('a');
|
|
775
|
-
a.href = `#${heading.id}`;
|
|
776
|
-
a.textContent = heading.textContent;
|
|
777
|
-
a.className = heading.tagName.toLowerCase() === 'h3' ? 'toc-h3' : 'toc-h2';
|
|
778
|
-
li.appendChild(a);
|
|
779
|
-
tocList.appendChild(li);
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
toc.appendChild(tocList);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Keyboard Shortcuts
|
|
786
|
-
document.addEventListener('keydown', (e) => {
|
|
787
|
-
// Cmd/Ctrl + K for search
|
|
788
|
-
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
789
|
-
e.preventDefault();
|
|
790
|
-
searchInput?.focus();
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Escape to close mobile menu
|
|
794
|
-
if (e.key === 'Escape') {
|
|
795
|
-
sidebar?.classList.remove('open');
|
|
796
|
-
}
|
|
797
|
-
});
|
|
798
|
-
|
|
799
|
-
// Fade-in Animation on Scroll
|
|
800
|
-
const observerOptions = {
|
|
801
|
-
threshold: 0.1,
|
|
802
|
-
rootMargin: '0px 0px -100px 0px'
|
|
803
|
-
};
|
|
804
|
-
|
|
805
|
-
const observer = new IntersectionObserver((entries) => {
|
|
806
|
-
entries.forEach(entry => {
|
|
807
|
-
if (entry.isIntersecting) {
|
|
808
|
-
entry.target.classList.add('fade-in');
|
|
809
|
-
observer.unobserve(entry.target);
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
}, observerOptions);
|
|
813
|
-
|
|
814
|
-
document.querySelectorAll('.feature-card, .timeline-item, .metric-card').forEach(el => {
|
|
815
|
-
observer.observe(el);
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
// Sidebar Resizing
|
|
819
|
-
function initSidebarResize() {
|
|
820
|
-
const sidebar = document.querySelector('.sidebar');
|
|
821
|
-
const resizeHandle = document.querySelector('.resize-handle');
|
|
822
|
-
const content = document.querySelector('.content');
|
|
823
|
-
|
|
824
|
-
if (!sidebar || !resizeHandle || !content) return;
|
|
825
|
-
|
|
826
|
-
let isResizing = false;
|
|
827
|
-
let startX = 0;
|
|
828
|
-
let startWidth = 0;
|
|
829
|
-
|
|
830
|
-
// Restore saved width on load
|
|
831
|
-
const savedWidth = localStorage.getItem('sidebarWidth');
|
|
832
|
-
if (savedWidth && savedWidth >= 200 && savedWidth <= 500) {
|
|
833
|
-
sidebar.style.width = `${savedWidth}px`;
|
|
834
|
-
// Don't set margin-left - flexbox handles the layout
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// Mouse down on resize handle
|
|
838
|
-
resizeHandle.addEventListener('mousedown', (e) => {
|
|
839
|
-
isResizing = true;
|
|
840
|
-
startX = e.clientX;
|
|
841
|
-
startWidth = parseInt(document.defaultView.getComputedStyle(sidebar).width, 10);
|
|
842
|
-
|
|
843
|
-
// Add global event listeners
|
|
844
|
-
document.addEventListener('mousemove', handleMouseMove);
|
|
845
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
846
|
-
|
|
847
|
-
// Prevent text selection during resize
|
|
848
|
-
document.body.style.userSelect = 'none';
|
|
849
|
-
document.body.style.cursor = 'col-resize';
|
|
850
|
-
|
|
851
|
-
e.preventDefault();
|
|
852
|
-
});
|
|
853
|
-
|
|
854
|
-
function handleMouseMove(e) {
|
|
855
|
-
if (!isResizing) return;
|
|
856
|
-
|
|
857
|
-
const width = startWidth + e.clientX - startX;
|
|
858
|
-
|
|
859
|
-
// Constrain width between 200px and 500px
|
|
860
|
-
const constrainedWidth = Math.max(200, Math.min(500, width));
|
|
861
|
-
|
|
862
|
-
sidebar.style.width = `${constrainedWidth}px`;
|
|
863
|
-
// Don't set margin-left - flexbox handles the layout
|
|
864
|
-
|
|
865
|
-
e.preventDefault();
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function handleMouseUp() {
|
|
869
|
-
if (!isResizing) return;
|
|
870
|
-
|
|
871
|
-
isResizing = false;
|
|
872
|
-
|
|
873
|
-
// Remove global event listeners
|
|
874
|
-
document.removeEventListener('mousemove', handleMouseMove);
|
|
875
|
-
document.removeEventListener('mouseup', handleMouseUp);
|
|
876
|
-
|
|
877
|
-
// Restore normal cursor and text selection
|
|
878
|
-
document.body.style.userSelect = '';
|
|
879
|
-
document.body.style.cursor = '';
|
|
880
|
-
|
|
881
|
-
// Save the current width
|
|
882
|
-
const currentWidth = parseInt(document.defaultView.getComputedStyle(sidebar).width, 10);
|
|
883
|
-
localStorage.setItem('sidebarWidth', currentWidth);
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
// Touch events for mobile support
|
|
887
|
-
resizeHandle.addEventListener('touchstart', (e) => {
|
|
888
|
-
isResizing = true;
|
|
889
|
-
startX = e.touches[0].clientX;
|
|
890
|
-
startWidth = parseInt(document.defaultView.getComputedStyle(sidebar).width, 10);
|
|
891
|
-
|
|
892
|
-
document.addEventListener('touchmove', handleTouchMove);
|
|
893
|
-
document.addEventListener('touchend', handleTouchEnd);
|
|
894
|
-
|
|
895
|
-
e.preventDefault();
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
function handleTouchMove(e) {
|
|
899
|
-
if (!isResizing) return;
|
|
900
|
-
|
|
901
|
-
const width = startWidth + e.touches[0].clientX - startX;
|
|
902
|
-
const constrainedWidth = Math.max(200, Math.min(500, width));
|
|
903
|
-
|
|
904
|
-
sidebar.style.width = `${constrainedWidth}px`;
|
|
905
|
-
// Don't set margin-left - flexbox handles the layout
|
|
906
|
-
|
|
907
|
-
e.preventDefault();
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
function handleTouchEnd() {
|
|
911
|
-
if (!isResizing) return;
|
|
912
|
-
|
|
913
|
-
isResizing = false;
|
|
914
|
-
|
|
915
|
-
document.removeEventListener('touchmove', handleTouchMove);
|
|
916
|
-
document.removeEventListener('touchend', handleTouchEnd);
|
|
917
|
-
|
|
918
|
-
// Save the current width
|
|
919
|
-
const currentWidth = parseInt(document.defaultView.getComputedStyle(sidebar).width, 10);
|
|
920
|
-
localStorage.setItem('sidebarWidth', currentWidth);
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
// Collapsible Navigation
|
|
925
|
-
function initCollapsibleNavigation() {
|
|
926
|
-
// Debug: Log initial state
|
|
927
|
-
console.log('[Navigation] Initializing collapsible navigation');
|
|
928
|
-
const allNavSections = document.querySelectorAll('.nav-section');
|
|
929
|
-
console.log(`[Navigation] Found ${allNavSections.length} nav sections`);
|
|
930
|
-
|
|
931
|
-
// First, ensure sections with active items are expanded on page load
|
|
932
|
-
expandActiveNavSections();
|
|
933
|
-
|
|
934
|
-
// Also run it again after a short delay to handle any timing issues
|
|
935
|
-
setTimeout(() => {
|
|
936
|
-
console.log('[Navigation] Running delayed expandActiveNavSections');
|
|
937
|
-
expandActiveNavSections();
|
|
938
|
-
}, 100);
|
|
939
|
-
|
|
940
|
-
// Additional fallback: if no active items found, try to expand based on current URL
|
|
941
|
-
setTimeout(() => {
|
|
942
|
-
const activeItems = document.querySelectorAll('.nav-item.active');
|
|
943
|
-
if (activeItems.length === 0) {
|
|
944
|
-
console.log('[Navigation] No active items found, trying URL-based expansion');
|
|
945
|
-
expandSectionByCurrentURL();
|
|
946
|
-
}
|
|
947
|
-
}, 200);
|
|
948
|
-
|
|
949
|
-
const collapsibleTitles = document.querySelectorAll('.nav-title.collapsible');
|
|
950
|
-
|
|
951
|
-
collapsibleTitles.forEach(title => {
|
|
952
|
-
title.addEventListener('click', (e) => {
|
|
953
|
-
// Prevent default link behavior for collapsible titles
|
|
954
|
-
e.preventDefault();
|
|
955
|
-
|
|
956
|
-
// Get the target content to toggle
|
|
957
|
-
const targetId = title.getAttribute('data-target');
|
|
958
|
-
const content = document.getElementById(targetId);
|
|
959
|
-
|
|
960
|
-
if (content) {
|
|
961
|
-
const isExpanded = title.classList.contains('expanded');
|
|
962
|
-
|
|
963
|
-
if (isExpanded) {
|
|
964
|
-
// Collapse this section
|
|
965
|
-
title.classList.remove('expanded');
|
|
966
|
-
content.classList.add('collapsed');
|
|
967
|
-
|
|
968
|
-
// Also collapse all child sections within this content
|
|
969
|
-
const childSections = content.querySelectorAll('.nav-title.collapsible');
|
|
970
|
-
childSections.forEach(childTitle => {
|
|
971
|
-
const childTargetId = childTitle.getAttribute('data-target');
|
|
972
|
-
const childContent = document.getElementById(childTargetId);
|
|
973
|
-
if (childContent) {
|
|
974
|
-
childTitle.classList.remove('expanded');
|
|
975
|
-
childContent.classList.add('collapsed');
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
|
-
} else {
|
|
979
|
-
// Expand this section
|
|
980
|
-
title.classList.add('expanded');
|
|
981
|
-
content.classList.remove('collapsed');
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
});
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
// Prevent nav items from triggering collapse and maintain parent expansion
|
|
988
|
-
const navItems = document.querySelectorAll('.nav-item');
|
|
989
|
-
navItems.forEach(item => {
|
|
990
|
-
item.addEventListener('click', (e) => {
|
|
991
|
-
// Only stop propagation to prevent collapse, but allow normal link navigation
|
|
992
|
-
e.stopPropagation(); // Prevent event from bubbling up to the nav-title
|
|
993
|
-
|
|
994
|
-
// Ensure ALL parent sections stay expanded when navigating within them
|
|
995
|
-
let currentElement = item;
|
|
996
|
-
while (currentElement) {
|
|
997
|
-
const parentContent = currentElement.closest('.nav-content');
|
|
998
|
-
if (!parentContent) break;
|
|
999
|
-
|
|
1000
|
-
const parentTitle = parentContent.parentElement?.querySelector('.nav-title.collapsible');
|
|
1001
|
-
|
|
1002
|
-
if (parentTitle && parentContent) {
|
|
1003
|
-
parentTitle.classList.add('expanded');
|
|
1004
|
-
parentContent.classList.remove('collapsed');
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
// Move up to check for nested sections
|
|
1008
|
-
currentElement = parentContent.parentElement;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// Allow normal link navigation - no preventDefault or manual navigation needed
|
|
1012
|
-
});
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// Expand navigation section based on current URL
|
|
1017
|
-
function expandSectionByCurrentURL() {
|
|
1018
|
-
try {
|
|
1019
|
-
const currentPath = window.location.pathname;
|
|
1020
|
-
console.log('[Navigation] Current path:', currentPath);
|
|
1021
|
-
|
|
1022
|
-
// Find all nav items and check if any match the current URL
|
|
1023
|
-
const navItems = document.querySelectorAll('.nav-item');
|
|
1024
|
-
let foundMatch = false;
|
|
1025
|
-
|
|
1026
|
-
navItems.forEach(item => {
|
|
1027
|
-
const href = item.getAttribute('href');
|
|
1028
|
-
if (href) {
|
|
1029
|
-
// Normalize paths for comparison
|
|
1030
|
-
const itemPath = new URL(href, window.location.href).pathname;
|
|
1031
|
-
if (itemPath === currentPath) {
|
|
1032
|
-
console.log('[Navigation] Found matching nav item by URL:', item.textContent.trim());
|
|
1033
|
-
item.classList.add('active');
|
|
1034
|
-
foundMatch = true;
|
|
1035
|
-
|
|
1036
|
-
// Expand parent sections
|
|
1037
|
-
let currentElement = item;
|
|
1038
|
-
while (currentElement && currentElement !== document.body) {
|
|
1039
|
-
if (currentElement.classList && currentElement.classList.contains('nav-content')) {
|
|
1040
|
-
const parentSection = currentElement.closest('.nav-section');
|
|
1041
|
-
if (parentSection) {
|
|
1042
|
-
const parentTitle = parentSection.querySelector('.nav-title.collapsible');
|
|
1043
|
-
if (parentTitle && currentElement.classList.contains('collapsed')) {
|
|
1044
|
-
parentTitle.classList.add('expanded');
|
|
1045
|
-
currentElement.classList.remove('collapsed');
|
|
1046
|
-
console.log('[Navigation] Expanded section by URL:', parentTitle.textContent.trim());
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
currentElement = currentElement.parentElement;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
if (!foundMatch) {
|
|
1057
|
-
console.warn('[Navigation] No matching nav item found for current URL');
|
|
1058
|
-
}
|
|
1059
|
-
} catch (error) {
|
|
1060
|
-
console.error('[Navigation] Error in expandSectionByCurrentURL:', error);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
// Ensure sections containing active nav items stay expanded
|
|
1065
|
-
function expandActiveNavSections() {
|
|
1066
|
-
try {
|
|
1067
|
-
const activeNavItems = document.querySelectorAll('.nav-item.active');
|
|
1068
|
-
|
|
1069
|
-
console.log(`[Navigation] Found ${activeNavItems.length} active nav items`);
|
|
1070
|
-
|
|
1071
|
-
if (activeNavItems.length === 0) {
|
|
1072
|
-
console.warn('[Navigation] No active navigation items found!');
|
|
1073
|
-
return;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
activeNavItems.forEach(activeItem => {
|
|
1077
|
-
console.log(`[Navigation] Expanding sections for: ${activeItem.textContent.trim()}`);
|
|
1078
|
-
|
|
1079
|
-
// Start from the active item and work up the DOM tree
|
|
1080
|
-
let currentElement = activeItem;
|
|
1081
|
-
let sectionsExpanded = 0;
|
|
1082
|
-
|
|
1083
|
-
while (currentElement && currentElement !== document.body) {
|
|
1084
|
-
// Check if we're inside a nav-content element
|
|
1085
|
-
if (currentElement.classList && currentElement.classList.contains('nav-content')) {
|
|
1086
|
-
console.log('[Navigation] Found nav-content element with id:', currentElement.id);
|
|
1087
|
-
|
|
1088
|
-
// Find the corresponding nav-title in the parent nav-section
|
|
1089
|
-
const parentSection = currentElement.closest('.nav-section');
|
|
1090
|
-
if (parentSection) {
|
|
1091
|
-
const parentTitle = parentSection.querySelector('.nav-title.collapsible');
|
|
1092
|
-
|
|
1093
|
-
if (parentTitle && currentElement.classList.contains('collapsed')) {
|
|
1094
|
-
// Expand this section
|
|
1095
|
-
parentTitle.classList.add('expanded');
|
|
1096
|
-
currentElement.classList.remove('collapsed');
|
|
1097
|
-
sectionsExpanded++;
|
|
1098
|
-
console.log(`[Navigation] Expanded section: ${parentTitle.textContent.trim()}`);
|
|
1099
|
-
} else if (parentTitle && !currentElement.classList.contains('collapsed')) {
|
|
1100
|
-
console.log(`[Navigation] Section already expanded: ${parentTitle.textContent.trim()}`);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
// Move up to the parent element
|
|
1106
|
-
currentElement = currentElement.parentElement;
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
if (sectionsExpanded === 0) {
|
|
1110
|
-
console.warn('[Navigation] No sections were expanded for active item:', activeItem.textContent.trim());
|
|
1111
|
-
} else {
|
|
1112
|
-
console.log(`[Navigation] Successfully expanded ${sectionsExpanded} sections`);
|
|
1113
|
-
}
|
|
1114
|
-
});
|
|
1115
|
-
} catch (error) {
|
|
1116
|
-
console.error('[Navigation] Error in expandActiveNavSections:', error);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// Navigation Filter
|
|
1121
|
-
function initNavigationFilter() {
|
|
1122
|
-
const filterInput = document.getElementById('nav-filter');
|
|
1123
|
-
if (!filterInput) return;
|
|
1124
|
-
|
|
1125
|
-
filterInput.addEventListener('input', (e) => {
|
|
1126
|
-
const query = e.target.value.toLowerCase().trim();
|
|
1127
|
-
const navItems = document.querySelectorAll('.nav-item');
|
|
1128
|
-
const navSections = document.querySelectorAll('.nav-section');
|
|
1129
|
-
|
|
1130
|
-
if (query === '') {
|
|
1131
|
-
// Show all items and restore original state
|
|
1132
|
-
navItems.forEach(item => {
|
|
1133
|
-
item.style.display = 'flex';
|
|
1134
|
-
});
|
|
1135
|
-
navSections.forEach(section => {
|
|
1136
|
-
section.style.display = 'block';
|
|
1137
|
-
});
|
|
1138
|
-
} else {
|
|
1139
|
-
// Filter items
|
|
1140
|
-
navItems.forEach(item => {
|
|
1141
|
-
const text = item.textContent.toLowerCase();
|
|
1142
|
-
const shouldShow = text.includes(query);
|
|
1143
|
-
item.style.display = shouldShow ? 'flex' : 'none';
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
|
-
// Show/hide sections based on whether they have visible items
|
|
1147
|
-
navSections.forEach(section => {
|
|
1148
|
-
const visibleItems = section.querySelectorAll('.nav-item[style*="flex"]');
|
|
1149
|
-
const hasVisibleItems = Array.from(section.querySelectorAll('.nav-item')).some(item =>
|
|
1150
|
-
item.style.display !== 'none'
|
|
1151
|
-
);
|
|
1152
|
-
section.style.display = hasVisibleItems ? 'block' : 'none';
|
|
1153
|
-
|
|
1154
|
-
// Expand sections with matches
|
|
1155
|
-
if (hasVisibleItems && query !== '') {
|
|
1156
|
-
const navContent = section.querySelector('.nav-content');
|
|
1157
|
-
const navTitle = section.querySelector('.nav-title.collapsible');
|
|
1158
|
-
if (navContent && navTitle) {
|
|
1159
|
-
navContent.classList.remove('collapsed');
|
|
1160
|
-
navTitle.classList.add('expanded');
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
});
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
// PDF Export functionality
|
|
1169
|
-
function exportToPDF() {
|
|
1170
|
-
// Hide UI elements for printing
|
|
1171
|
-
const elementsToHide = [
|
|
1172
|
-
'.sidebar',
|
|
1173
|
-
'.header',
|
|
1174
|
-
'.preview-banner',
|
|
1175
|
-
'.resize-handle',
|
|
1176
|
-
'.copy-button'
|
|
1177
|
-
];
|
|
1178
|
-
|
|
1179
|
-
elementsToHide.forEach(selector => {
|
|
1180
|
-
const elements = document.querySelectorAll(selector);
|
|
1181
|
-
elements.forEach(el => el.style.display = 'none');
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
|
-
// Adjust content for printing
|
|
1185
|
-
const content = document.querySelector('.content');
|
|
1186
|
-
const mainWrapper = document.querySelector('.main-wrapper');
|
|
1187
|
-
|
|
1188
|
-
if (content) {
|
|
1189
|
-
content.style.padding = '20px';
|
|
1190
|
-
content.style.maxWidth = 'none';
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
if (mainWrapper) {
|
|
1194
|
-
mainWrapper.style.paddingTop = '0';
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
// Add print-specific styles
|
|
1198
|
-
const printStyles = document.createElement('style');
|
|
1199
|
-
printStyles.id = 'print-styles';
|
|
1200
|
-
printStyles.textContent = `
|
|
1201
|
-
@media print {
|
|
1202
|
-
body {
|
|
1203
|
-
font-size: 12pt;
|
|
1204
|
-
line-height: 1.4;
|
|
1205
|
-
color: black !important;
|
|
1206
|
-
}
|
|
1207
|
-
.content {
|
|
1208
|
-
margin: 0 !important;
|
|
1209
|
-
padding: 0 !important;
|
|
1210
|
-
max-width: none !important;
|
|
1211
|
-
}
|
|
1212
|
-
.main-wrapper {
|
|
1213
|
-
padding-top: 0 !important;
|
|
1214
|
-
}
|
|
1215
|
-
h1, h2, h3, h4, h5, h6 {
|
|
1216
|
-
color: black !important;
|
|
1217
|
-
page-break-after: avoid;
|
|
1218
|
-
}
|
|
1219
|
-
.hero {
|
|
1220
|
-
background: none !important;
|
|
1221
|
-
color: black !important;
|
|
1222
|
-
padding: 20px 0 !important;
|
|
1223
|
-
margin: 0 !important;
|
|
1224
|
-
}
|
|
1225
|
-
.hero h1 {
|
|
1226
|
-
color: black !important;
|
|
1227
|
-
text-shadow: none !important;
|
|
1228
|
-
font-size: 24pt !important;
|
|
1229
|
-
}
|
|
1230
|
-
.hero-subtitle {
|
|
1231
|
-
color: black !important;
|
|
1232
|
-
text-shadow: none !important;
|
|
1233
|
-
}
|
|
1234
|
-
.feature-grid, .metrics-grid {
|
|
1235
|
-
display: block !important;
|
|
1236
|
-
}
|
|
1237
|
-
.feature-card, .metric-card {
|
|
1238
|
-
break-inside: avoid;
|
|
1239
|
-
margin-bottom: 10px;
|
|
1240
|
-
border: 1px solid #ccc;
|
|
1241
|
-
padding: 10px;
|
|
1242
|
-
}
|
|
1243
|
-
.mermaid {
|
|
1244
|
-
break-inside: avoid;
|
|
1245
|
-
background: white !important;
|
|
1246
|
-
border: 1px solid #ccc;
|
|
1247
|
-
}
|
|
1248
|
-
pre, code {
|
|
1249
|
-
background: #f5f5f5 !important;
|
|
1250
|
-
border: 1px solid #ddd;
|
|
1251
|
-
font-size: 10pt;
|
|
1252
|
-
}
|
|
1253
|
-
table {
|
|
1254
|
-
break-inside: avoid;
|
|
1255
|
-
}
|
|
1256
|
-
.timeline {
|
|
1257
|
-
display: block !important;
|
|
1258
|
-
}
|
|
1259
|
-
.timeline-item {
|
|
1260
|
-
break-inside: avoid;
|
|
1261
|
-
margin-bottom: 15px;
|
|
1262
|
-
padding-left: 0 !important;
|
|
1263
|
-
}
|
|
1264
|
-
.timeline::before {
|
|
1265
|
-
display: none;
|
|
1266
|
-
}
|
|
1267
|
-
.timeline-item::before {
|
|
1268
|
-
display: none;
|
|
1269
|
-
}
|
|
1270
|
-
a {
|
|
1271
|
-
color: black !important;
|
|
1272
|
-
text-decoration: underline;
|
|
1273
|
-
}
|
|
1274
|
-
.gradient-text {
|
|
1275
|
-
color: black !important;
|
|
1276
|
-
background: none !important;
|
|
1277
|
-
-webkit-text-fill-color: black !important;
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
`;
|
|
1281
|
-
document.head.appendChild(printStyles);
|
|
1282
|
-
|
|
1283
|
-
// Trigger print dialog
|
|
1284
|
-
setTimeout(() => {
|
|
1285
|
-
window.print();
|
|
1286
|
-
|
|
1287
|
-
// Restore UI after print dialog
|
|
1288
|
-
setTimeout(() => {
|
|
1289
|
-
// Remove print styles
|
|
1290
|
-
const printStylesEl = document.getElementById('print-styles');
|
|
1291
|
-
if (printStylesEl) {
|
|
1292
|
-
printStylesEl.remove();
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
// Restore hidden elements
|
|
1296
|
-
elementsToHide.forEach(selector => {
|
|
1297
|
-
const elements = document.querySelectorAll(selector);
|
|
1298
|
-
elements.forEach(el => el.style.display = '');
|
|
1299
|
-
});
|
|
1300
|
-
|
|
1301
|
-
// Restore content styles
|
|
1302
|
-
if (content) {
|
|
1303
|
-
content.style.padding = '';
|
|
1304
|
-
content.style.maxWidth = '';
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
if (mainWrapper) {
|
|
1308
|
-
mainWrapper.style.paddingTop = '';
|
|
1309
|
-
}
|
|
1310
|
-
}, 500);
|
|
1311
|
-
}, 100);
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
// Add PDF export button functionality
|
|
1315
|
-
function addPDFExportButton() {
|
|
1316
|
-
// Check configuration - default to true if not set
|
|
1317
|
-
const showPdfDownload = window.docBuilderConfig?.features?.showPdfDownload !== false;
|
|
1318
|
-
if (!showPdfDownload) return;
|
|
1319
|
-
|
|
1320
|
-
const headerActions = document.querySelector('.header-actions');
|
|
1321
|
-
if (headerActions) {
|
|
1322
|
-
const pdfButton = document.createElement('button');
|
|
1323
|
-
pdfButton.innerHTML = '<i class="fas fa-file-pdf"></i>';
|
|
1324
|
-
pdfButton.className = 'theme-toggle';
|
|
1325
|
-
pdfButton.title = 'Export to PDF';
|
|
1326
|
-
pdfButton.setAttribute('aria-label', 'Export to PDF');
|
|
1327
|
-
pdfButton.addEventListener('click', exportToPDF);
|
|
1328
|
-
|
|
1329
|
-
// Insert before theme toggle
|
|
1330
|
-
const themeToggle = document.getElementById('theme-toggle');
|
|
1331
|
-
headerActions.insertBefore(pdfButton, themeToggle);
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// Breadcrumb Generation
|
|
1336
|
-
function generateBreadcrumbs() {
|
|
1337
|
-
const breadcrumbContainer = document.getElementById('breadcrumbs');
|
|
1338
|
-
if (!breadcrumbContainer) return;
|
|
1339
|
-
|
|
1340
|
-
// Decode the URL to handle special characters and spaces
|
|
1341
|
-
const currentPath = decodeURIComponent(window.location.pathname);
|
|
1342
|
-
let pathSegments = currentPath.split('/').filter(segment => segment !== '');
|
|
1343
|
-
|
|
1344
|
-
// Find the index of 'html' directory and slice from there
|
|
1345
|
-
const htmlIndex = pathSegments.findIndex(segment => segment === 'html');
|
|
1346
|
-
if (htmlIndex !== -1) {
|
|
1347
|
-
// Remove everything before and including 'html'
|
|
1348
|
-
pathSegments = pathSegments.slice(htmlIndex + 1);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
// Remove .html extension from the last segment
|
|
1352
|
-
if (pathSegments.length > 0) {
|
|
1353
|
-
const lastSegment = pathSegments[pathSegments.length - 1];
|
|
1354
|
-
if (lastSegment.endsWith('.html')) {
|
|
1355
|
-
pathSegments[pathSegments.length - 1] = lastSegment.slice(0, -5);
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
const breadcrumbs = [];
|
|
1360
|
-
|
|
1361
|
-
// Calculate relative path to root for proper navigation
|
|
1362
|
-
const depth = pathSegments.length;
|
|
1363
|
-
const relativeRoot = depth > 0 ? '../'.repeat(depth) : './';
|
|
1364
|
-
|
|
1365
|
-
// Always start with Home (relative to current page)
|
|
1366
|
-
breadcrumbs.push({
|
|
1367
|
-
text: 'Home',
|
|
1368
|
-
href: relativeRoot + 'index.html',
|
|
1369
|
-
icon: 'fas fa-home'
|
|
1370
|
-
});
|
|
1371
|
-
|
|
1372
|
-
// Build breadcrumb path
|
|
1373
|
-
let currentUrl = '';
|
|
1374
|
-
pathSegments.forEach((segment, index) => {
|
|
1375
|
-
currentUrl += '/' + segment;
|
|
1376
|
-
|
|
1377
|
-
// Calculate relative path for this breadcrumb level
|
|
1378
|
-
const remainingDepth = pathSegments.length - index - 1;
|
|
1379
|
-
const relativePath = remainingDepth > 0 ? '../'.repeat(remainingDepth) : './';
|
|
1380
|
-
|
|
1381
|
-
// For the last segment, don't add .html back if it's not index
|
|
1382
|
-
const href = index === pathSegments.length - 1 && segment !== 'index'
|
|
1383
|
-
? '#' // Current page, no navigation needed
|
|
1384
|
-
: relativePath + segment + '.html';
|
|
1385
|
-
|
|
1386
|
-
// Prettify segment names
|
|
1387
|
-
const text = segment
|
|
1388
|
-
.replace(/[-_]/g, ' ')
|
|
1389
|
-
.split(' ')
|
|
1390
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
1391
|
-
.join(' ')
|
|
1392
|
-
.replace(/\b(Api|Html|Css|Js|Pdf|Qa|Ai)\b/g, (match) => match.toUpperCase())
|
|
1393
|
-
.replace(/\bReadme\b/g, 'Overview');
|
|
1394
|
-
|
|
1395
|
-
// Get appropriate icon based on segment
|
|
1396
|
-
let icon = 'fas fa-folder';
|
|
1397
|
-
if (segment.includes('bubble')) icon = 'fas fa-circle';
|
|
1398
|
-
else if (segment.includes('system')) icon = 'fas fa-sitemap';
|
|
1399
|
-
else if (segment.includes('middleware')) icon = 'fas fa-layer-group';
|
|
1400
|
-
else if (segment.includes('quickbase')) icon = 'fas fa-database';
|
|
1401
|
-
else if (segment.includes('product-roadmap')) icon = 'fas fa-road';
|
|
1402
|
-
else if (segment.includes('team')) icon = 'fas fa-users';
|
|
1403
|
-
else if (segment.includes('testing')) icon = 'fas fa-vial';
|
|
1404
|
-
else if (segment.includes('paths')) icon = 'fas fa-route';
|
|
1405
|
-
else if (segment.includes('diagrams')) icon = 'fas fa-project-diagram';
|
|
1406
|
-
else if (segment.includes('technical')) icon = 'fas fa-cogs';
|
|
1407
|
-
else if (segment.includes('application')) icon = 'fas fa-desktop';
|
|
1408
|
-
else if (index === pathSegments.length - 1) icon = 'fas fa-file-alt';
|
|
1409
|
-
|
|
1410
|
-
breadcrumbs.push({
|
|
1411
|
-
text,
|
|
1412
|
-
href,
|
|
1413
|
-
icon,
|
|
1414
|
-
isLast: index === pathSegments.length - 1
|
|
1415
|
-
});
|
|
1416
|
-
});
|
|
1417
|
-
|
|
1418
|
-
// Generate breadcrumb HTML
|
|
1419
|
-
const breadcrumbHTML = breadcrumbs.map((crumb, index) => {
|
|
1420
|
-
if (crumb.isLast) {
|
|
1421
|
-
return `<span class="breadcrumb-item current">
|
|
1422
|
-
<i class="${crumb.icon}"></i>
|
|
1423
|
-
<span>${crumb.text}</span>
|
|
1424
|
-
</span>`;
|
|
1425
|
-
} else {
|
|
1426
|
-
return `<a href="${crumb.href}" class="breadcrumb-item">
|
|
1427
|
-
<i class="${crumb.icon}"></i>
|
|
1428
|
-
<span>${crumb.text}</span>
|
|
1429
|
-
</a>`;
|
|
1430
|
-
}
|
|
1431
|
-
}).join('<i class="fas fa-chevron-right breadcrumb-separator"></i>');
|
|
1432
|
-
|
|
1433
|
-
breadcrumbContainer.innerHTML = breadcrumbHTML;
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
// Initialize tooltip positioning for navigation items
|
|
1437
|
-
function initTooltips() {
|
|
1438
|
-
const tooltipElements = document.querySelectorAll('[data-tooltip]');
|
|
1439
|
-
|
|
1440
|
-
tooltipElements.forEach(element => {
|
|
1441
|
-
element.addEventListener('mouseenter', function(e) {
|
|
1442
|
-
const rect = element.getBoundingClientRect();
|
|
1443
|
-
const tooltip = window.getComputedStyle(element, '::after');
|
|
1444
|
-
|
|
1445
|
-
// Position the tooltip using CSS variables
|
|
1446
|
-
element.style.setProperty('--tooltip-left', `${rect.right + 10}px`);
|
|
1447
|
-
element.style.setProperty('--tooltip-top', `${rect.top + rect.height / 2}px`);
|
|
1448
|
-
});
|
|
1449
|
-
});
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
// Handle .md link redirects
|
|
1453
|
-
function initMarkdownLinkRedirects() {
|
|
1454
|
-
// Check if current URL ends with .md and redirect
|
|
1455
|
-
if (window.location.pathname.endsWith('.md')) {
|
|
1456
|
-
const htmlPath = window.location.pathname.replace(/\.md$/, '.html');
|
|
1457
|
-
console.log(`Redirecting from .md to .html: ${htmlPath}`);
|
|
1458
|
-
window.location.replace(htmlPath);
|
|
1459
|
-
return; // Stop execution as we're redirecting
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
// Intercept clicks on .md links
|
|
1463
|
-
document.addEventListener('click', function(e) {
|
|
1464
|
-
const link = e.target.closest('a');
|
|
1465
|
-
if (link && link.href && link.href.endsWith('.md')) {
|
|
1466
|
-
e.preventDefault();
|
|
1467
|
-
const htmlUrl = link.href.replace(/\.md$/, '.html');
|
|
1468
|
-
console.log(`Converting .md link to .html: ${htmlUrl}`);
|
|
1469
|
-
window.location.href = htmlUrl;
|
|
1470
|
-
}
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
// Initialize on DOM Load
|
|
1475
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
1476
|
-
initMarkdownLinkRedirects();
|
|
1477
|
-
highlightNavigation();
|
|
1478
|
-
generateTableOfContents();
|
|
1479
|
-
initSidebarResize();
|
|
1480
|
-
initCollapsibleNavigation();
|
|
1481
|
-
initNavigationFilter();
|
|
1482
|
-
addPDFExportButton();
|
|
1483
|
-
generateBreadcrumbs();
|
|
1484
|
-
initTooltips();
|
|
1485
|
-
});
|