@knowcode/doc-builder 1.9.30 → 1.9.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/package.json +12 -2
  2. package/.claude/settings.local.json +0 -56
  3. package/CACHE-BUSTING-GUIDE.md +0 -82
  4. package/CLAUDE.md +0 -86
  5. package/CONTRIBUTING.md +0 -148
  6. package/GITHUB_SETUP.md +0 -203
  7. package/RELEASE-NOTES-1.7.5.md +0 -64
  8. package/Screenshot 2025-07-22 at 19.51.21.png +0 -0
  9. package/Screenshot 2025-07-26 at 17.06.49.png +0 -0
  10. package/add-user-clive.sql +0 -35
  11. package/add-user-lindsay-fixed.sql +0 -85
  12. package/add-user-lindsay.sql +0 -68
  13. package/add-user-pmorgan.sql +0 -35
  14. package/add-user-robbie.sql +0 -35
  15. package/add-wru-users.sql +0 -105
  16. package/debug-login.sql +0 -30
  17. package/doc-builder.config.js +0 -126
  18. package/doc-builder.config.js.backup.1753793768283 +0 -47
  19. package/doc-builder.config.js.backup.1753803964423 +0 -114
  20. package/doc-builder.config.js.backup.1753945707032 +0 -115
  21. package/doc-builder.config.js.backup.1754059241330 +0 -115
  22. package/doc-builder.config.js.backup.1754119567787 +0 -123
  23. package/doc-builder.config.js.backup.1754120048862 +0 -124
  24. package/doc-builder.config.js.backup.1754120529913 +0 -124
  25. package/doc-builder.config.js.backup.1754218469785 +0 -124
  26. package/doc-builder.config.js.backup.1754384764054 +0 -124
  27. package/doc-builder.config.js.backup.1754567425847 +0 -124
  28. package/doc-builder.config.js.backup.1754568137859 +0 -126
  29. package/doc-builder.config.js.backup.1754569388252 +0 -126
  30. package/doc-builder.config.js.backup.1754576694123 +0 -126
  31. package/doc-builder.config.js.backup.1755031374829 +0 -126
  32. package/doc-builder.config.js.backup.1755034500990 +0 -126
  33. package/doc-builder.config.js.backup.1755034809236 +0 -126
  34. package/grant-access.sql +0 -15
  35. package/html/11.png +0 -0
  36. package/html/404.html +0 -115
  37. package/html/README.html +0 -522
  38. package/html/Screenshot 2025-08-12 at 21.35.07.png +0 -0
  39. package/html/about-doc-builder.html +0 -491
  40. package/html/auth.js +0 -157
  41. package/html/claude-workflow-guide.html +0 -525
  42. package/html/css/notion-style.css +0 -2502
  43. package/html/documentation-index.html +0 -471
  44. package/html/guides/authentication-default-change.html +0 -370
  45. package/html/guides/authentication-guide.html +0 -509
  46. package/html/guides/cache-control-anti-pattern.html +0 -361
  47. package/html/guides/claude-workflow-guide.html +0 -1074
  48. package/html/guides/configuration-guide.html +0 -472
  49. package/html/guides/document-standards.html +0 -518
  50. package/html/guides/documentation-standards.html +0 -694
  51. package/html/guides/html-embedding-guide.html +0 -461
  52. package/html/guides/image-modal-guide.html +0 -515
  53. package/html/guides/next-steps-walkthrough.html +0 -638
  54. package/html/guides/phosphor-icons-guide.html +0 -584
  55. package/html/guides/private-directory-authentication-troubleshooting.html +0 -555
  56. package/html/guides/private-directory-authentication.html +0 -541
  57. package/html/guides/public-site-deployment.html +0 -431
  58. package/html/guides/search-engine-verification-guide.html +0 -542
  59. package/html/guides/seo-guide.html +0 -661
  60. package/html/guides/seo-optimization-guide.html +0 -887
  61. package/html/guides/supabase-auth-implementation-plan.html +0 -543
  62. package/html/guides/supabase-auth-integration-plan.html +0 -671
  63. package/html/guides/supabase-auth-setup-guide.html +0 -498
  64. package/html/guides/supabase-authentication-complete-guide.html +0 -866
  65. package/html/guides/troubleshooting-guide.html +0 -633
  66. package/html/guides/vercel-deployment-auth-setup.html +0 -337
  67. package/html/guides/windows-setup-guide.html +0 -859
  68. package/html/image-modal-test.html +0 -318
  69. package/html/index.html +0 -522
  70. package/html/js/auth.js +0 -157
  71. package/html/js/main.js +0 -1754
  72. package/html/launch/README.html +0 -297
  73. package/html/launch/bubble-plugin-specification.html +0 -933
  74. package/html/launch/go-to-market-strategy.html +0 -663
  75. package/html/launch/launch-announcements.html +0 -593
  76. package/html/login.html +0 -102
  77. package/html/logout.html +0 -18
  78. package/html/private/cache-control-anti-pattern.html +0 -429
  79. package/html/private/launch/README.html +0 -371
  80. package/html/private/launch/auth-cleanup-summary.html +0 -361
  81. package/html/private/launch/bubble-plugin-specification.html +0 -1007
  82. package/html/private/launch/go-to-market-strategy.html +0 -737
  83. package/html/private/launch/launch-announcements.html +0 -667
  84. package/html/private/launch/vercel-deployment-auth-setup.html +0 -417
  85. package/html/private/next-steps-walkthrough.html +0 -679
  86. package/html/private/supabase-auth-implementation-completed.html +0 -454
  87. package/html/private/supabase-auth-implementation-plan.html +0 -594
  88. package/html/private/supabase-auth-integration-plan.html +0 -704
  89. package/html/private/supabase-auth-setup-guide.html +0 -555
  90. package/html/private/test-private-doc.html +0 -302
  91. package/html/private/user-management-tooling.html +0 -601
  92. package/html/prompts/Screenshot 2025-08-02 at 08.49.55.png +0 -0
  93. package/html/prompts/beautiful-documentation-design.html +0 -784
  94. package/html/prompts/markdown-document-standards.html +0 -422
  95. package/html/prompts/project-rename-strategy-sasha-publish.html +0 -530
  96. package/html/robots.txt +0 -9
  97. package/html/sitemap.xml +0 -357
  98. package/html/test-questions/how-does-it-work%3F.html +0 -294
  99. package/html/test-questions/step-1%3A%20getting-started.html +0 -289
  100. package/html/test-questions/what-is-the-purpose.html +0 -293
  101. package/html/test-status.html +0 -281
  102. package/html/vercel-cli-setup-guide.html +0 -495
  103. package/html/vercel-first-time-setup-guide.html +0 -454
  104. package/html/vercel.json +0 -29
  105. package/html-static/11.png +0 -0
  106. package/html-static/404.html +0 -115
  107. package/html-static/README.html +0 -609
  108. package/html-static/Screenshot 2025-08-12 at 21.35.07.png +0 -0
  109. package/html-static/about-doc-builder.html +0 -578
  110. package/html-static/css/notion-style.css +0 -2502
  111. package/html-static/documentation-index.html +0 -558
  112. package/html-static/guides/authentication-default-change.html +0 -457
  113. package/html-static/guides/authentication-guide.html +0 -596
  114. package/html-static/guides/claude-workflow-guide.html +0 -1161
  115. package/html-static/guides/configuration-guide.html +0 -559
  116. package/html-static/guides/documentation-standards.html +0 -781
  117. package/html-static/guides/html-embedding-guide.html +0 -548
  118. package/html-static/guides/image-modal-guide.html +0 -602
  119. package/html-static/guides/phosphor-icons-guide.html +0 -671
  120. package/html-static/guides/private-directory-authentication-troubleshooting.html +0 -642
  121. package/html-static/guides/private-directory-authentication.html +0 -628
  122. package/html-static/guides/public-site-deployment.html +0 -518
  123. package/html-static/guides/search-engine-verification-guide.html +0 -629
  124. package/html-static/guides/seo-guide.html +0 -748
  125. package/html-static/guides/seo-optimization-guide.html +0 -974
  126. package/html-static/guides/supabase-authentication-complete-guide.html +0 -953
  127. package/html-static/guides/troubleshooting-guide.html +0 -720
  128. package/html-static/guides/windows-setup-guide.html +0 -946
  129. package/html-static/image-modal-test.html +0 -405
  130. package/html-static/index.html +0 -609
  131. package/html-static/js/main.js +0 -1754
  132. package/html-static/prompts/Screenshot 2025-08-02 at 08.49.55.png +0 -0
  133. package/html-static/prompts/beautiful-documentation-design.html +0 -871
  134. package/html-static/prompts/markdown-document-standards.html +0 -509
  135. package/html-static/prompts/project-rename-strategy-sasha-publish.html +0 -617
  136. package/html-static/robots.txt +0 -5
  137. package/html-static/sitemap.xml +0 -195
  138. package/html-static/test-questions/how-does-it-work%3F.html +0 -381
  139. package/html-static/test-questions/step-1%3A%20getting-started.html +0 -376
  140. package/html-static/test-questions/what-is-the-purpose.html +0 -380
  141. package/html-static/vercel-cli-setup-guide.html +0 -582
  142. package/html-static/vercel-first-time-setup-guide.html +0 -541
  143. package/manage-users.sql +0 -191
  144. package/migrate-to-domain-auth.sql +0 -47
  145. package/package/CACHE-BUSTING-GUIDE.md +0 -82
  146. package/package/CHANGELOG.md +0 -902
  147. package/package/README.md +0 -248
  148. package/package/assets/css/notion-style.css +0 -2211
  149. package/package/assets/js/auth.js +0 -67
  150. package/package/assets/js/main.js +0 -1565
  151. package/package/cli.js +0 -764
  152. package/package/index.js +0 -38
  153. package/package/knowcode-doc-builder-1.3.15.tgz +0 -0
  154. package/package/lib/builder.js +0 -32
  155. package/package/lib/config.js +0 -278
  156. package/package/lib/core-builder.js +0 -957
  157. package/package/lib/deploy.js +0 -497
  158. package/package/lib/dev-server.js +0 -96
  159. package/package/package.json +0 -34
  160. package/package/scripts/npx-runner.js +0 -27
  161. package/package/scripts/setup.js +0 -56
  162. package/package/test-cache-bust.sh +0 -43
  163. package/public-config.js +0 -22
  164. package/public-html/404.html +0 -115
  165. package/public-html/README.html +0 -149
  166. package/public-html/css/notion-style.css +0 -2036
  167. package/public-html/index.html +0 -149
  168. package/public-html/js/auth.js +0 -67
  169. package/public-html/js/main.js +0 -1485
  170. package/quick-test-commands.md +0 -40
  171. package/recordings/Screenshot 2025-07-24 at 18.22.01.png +0 -0
  172. package/recordings/mh-ls-22jul.txt +0 -2305
  173. package/screenshot.png +0 -0
  174. package/scripts/Screenshot 2025-07-23 at 15.39.41.png +0 -0
  175. package/setup-database-v2.sql +0 -53
  176. package/setup-database.sql +0 -41
  177. package/test-auth-config.js +0 -17
  178. package/test-cache-bust.sh +0 -43
  179. package/test-docs/README.md +0 -39
  180. package/test-html/404.html +0 -115
  181. package/test-html/README.html +0 -172
  182. package/test-html/auth.js +0 -97
  183. package/test-html/css/notion-style.css +0 -2036
  184. package/test-html/index.html +0 -172
  185. package/test-html/js/auth.js +0 -97
  186. package/test-html/js/main.js +0 -1485
  187. package/test-html/login.html +0 -102
  188. package/test-html/logout.html +0 -18
  189. package/update-domain.sql +0 -9
  190. package/user-access-view.sql +0 -49
  191. package/user-management/README.md +0 -301
  192. package/user-management/add-users.sh +0 -776
  193. package/user-management/create-user.js +0 -65
  194. package/user-management/users.txt +0 -15
  195. package/view-all-users.sql +0 -40
  196. package/wru-auth-config.js +0 -17
@@ -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
- });