@knowcode/doc-builder 1.1.11 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,64 @@ All notable changes to @knowcode/doc-builder will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.0] - 2025-07-19
9
+
10
+ ### Added (MAJOR FEATURE RELEASE)
11
+ - **Complete feature parity** with original JUNO documentation system
12
+ - **Preview banner** with dismissible functionality and localStorage
13
+ - **Breadcrumb navigation** automatically generated from URL structure
14
+ - **Advanced sidebar** with filter/search functionality
15
+ - **Hierarchical navigation** with collapsible folders and proper icons
16
+ - **Rich tooltip system** with folder descriptions and hover information
17
+ - **Resize handle** for adjustable sidebar width
18
+ - **Icon mapping** for different content types and folders
19
+ - **Folder descriptions** for better UX and navigation
20
+ - **Logical folder ordering** (strategic content first, technical content later)
21
+
22
+ ### Enhanced Navigation Features
23
+ - **Smart folder detection** and hierarchical structuring
24
+ - **Active section auto-expansion** for current page context
25
+ - **Collapsible navigation sections** with smooth animations
26
+ - **File type icons** (documents, folders, special content types)
27
+ - **Improved file naming** and title generation
28
+ - **README.md as folder overviews** with proper linking
29
+ - **Filter/search** through navigation items
30
+
31
+ ### Layout Improvements
32
+ - **Complete layout restructure** matching original design
33
+ - **Fixed positioning** for banner, breadcrumbs, and sidebar
34
+ - **Responsive design** with mobile-friendly collapsible layout
35
+ - **Proper content area** with max-width and centered layout
36
+ - **Smooth transitions** and hover effects throughout
37
+ - **Dark mode support** for all new components
38
+
39
+ ### Technical Enhancements
40
+ - **Rich HTML structure** with semantic navigation
41
+ - **CSS Grid and Flexbox** layout system
42
+ - **Modern CSS variables** and consistent spacing
43
+ - **Optimized mobile experience** with adaptive layouts
44
+ - **Performance optimized** animations and transitions
45
+
46
+ ## [1.1.12] - 2025-07-19
47
+
48
+ ### Fixed (MAJOR)
49
+ - **Fixed sidebar layout** - Navigation now appears as fixed-width left sidebar instead of above content
50
+ - Added proper flexbox container layout with fixed sidebar positioning
51
+ - Navigation is now 280px fixed width on left side with proper scrolling
52
+ - Content area properly offset to account for sidebar width
53
+
54
+ ### Added
55
+ - Container flexbox layout for proper sidebar + content structure
56
+ - Fixed navigation positioning with proper z-index and boundaries
57
+ - Mobile responsive layout - sidebar becomes horizontal bar on mobile
58
+ - Proper scrolling for both navigation and content areas
59
+
60
+ ### Visual Changes
61
+ - Navigation background set to secondary color with right border
62
+ - Content area now takes full remaining width after sidebar
63
+ - Proper spacing and padding for both sidebar and content
64
+ - Mobile layout stacks navigation above content
65
+
8
66
  ## [1.1.11] - 2025-07-19
9
67
 
10
68
  ### Fixed (MAJOR)
@@ -372,19 +372,194 @@ pre code {
372
372
  font-size: var(--text-sm);
373
373
  }
374
374
 
375
+ /* Preview Banner */
376
+ .preview-banner {
377
+ background: var(--color-accent-yellow-bg);
378
+ border-bottom: 1px solid var(--color-accent-yellow);
379
+ color: var(--color-text-primary);
380
+ padding: var(--space-2) 0;
381
+ position: fixed;
382
+ top: var(--header-height);
383
+ left: 0;
384
+ right: 0;
385
+ z-index: 1000;
386
+ transition: transform var(--duration-normal);
387
+ }
388
+
389
+ .preview-banner.hidden {
390
+ transform: translateY(-100%);
391
+ }
392
+
393
+ .banner-content {
394
+ display: flex;
395
+ align-items: center;
396
+ justify-content: center;
397
+ gap: var(--space-2);
398
+ max-width: 1400px;
399
+ margin: 0 auto;
400
+ padding: 0 var(--space-4);
401
+ }
402
+
403
+ .banner-icon {
404
+ color: var(--color-accent-yellow);
405
+ font-size: var(--text-sm);
406
+ }
407
+
408
+ .banner-text {
409
+ font-size: var(--text-sm);
410
+ font-weight: var(--font-medium);
411
+ }
412
+
413
+ .banner-dismiss {
414
+ background: none;
415
+ border: none;
416
+ color: var(--color-text-secondary);
417
+ cursor: pointer;
418
+ padding: var(--space-1);
419
+ margin-left: auto;
420
+ border-radius: var(--radius-sm);
421
+ transition: all var(--duration-fast);
422
+ }
423
+
424
+ .banner-dismiss:hover {
425
+ background: var(--color-bg-hover);
426
+ color: var(--color-text-primary);
427
+ }
428
+
429
+ /* Breadcrumbs */
430
+ .breadcrumbs {
431
+ background: var(--color-bg-default);
432
+ border-bottom: 1px solid var(--color-border-default);
433
+ padding: var(--space-2) 0;
434
+ position: fixed;
435
+ top: calc(var(--header-height) + 3.5rem);
436
+ left: 0;
437
+ right: 0;
438
+ z-index: 900;
439
+ transition: top var(--duration-normal);
440
+ }
441
+
442
+ .breadcrumbs.banner-visible {
443
+ top: calc(var(--header-height) + 3.5rem);
444
+ }
445
+
446
+ .breadcrumbs:not(.banner-visible) {
447
+ top: var(--header-height);
448
+ }
449
+
450
+ .breadcrumb-item {
451
+ display: inline-flex;
452
+ align-items: center;
453
+ gap: var(--space-1);
454
+ color: var(--color-text-secondary);
455
+ text-decoration: none;
456
+ font-size: var(--text-sm);
457
+ padding: var(--space-1) var(--space-2);
458
+ border-radius: var(--radius-sm);
459
+ transition: all var(--duration-fast);
460
+ }
461
+
462
+ .breadcrumb-item:hover {
463
+ background: var(--color-bg-hover);
464
+ color: var(--color-text-primary);
465
+ }
466
+
467
+ .breadcrumb-item.current {
468
+ color: var(--color-text-primary);
469
+ font-weight: var(--font-medium);
470
+ }
471
+
472
+ .breadcrumb-separator {
473
+ color: var(--color-text-tertiary);
474
+ font-size: var(--text-xs);
475
+ margin: 0 var(--space-1);
476
+ }
477
+
478
+ /* Main Wrapper */
479
+ .main-wrapper {
480
+ display: flex;
481
+ height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
482
+ margin-top: calc(var(--header-height) + var(--breadcrumb-height));
483
+ overflow: hidden;
484
+ }
485
+
486
+ .main-wrapper.banner-visible {
487
+ height: calc(100vh - var(--header-height) - var(--breadcrumb-height) - 3.5rem);
488
+ margin-top: calc(var(--header-height) + var(--breadcrumb-height) + 3.5rem);
489
+ }
490
+
491
+ /* Sidebar */
492
+ .sidebar {
493
+ width: var(--sidebar-width);
494
+ background: var(--color-bg-secondary);
495
+ border-right: 1px solid var(--color-border-default);
496
+ display: flex;
497
+ flex-direction: column;
498
+ position: relative;
499
+ }
500
+
501
+ .sidebar-header {
502
+ padding: var(--space-3);
503
+ border-bottom: 1px solid var(--color-border-default);
504
+ }
505
+
506
+ .filter-box {
507
+ position: relative;
508
+ display: flex;
509
+ align-items: center;
510
+ }
511
+
512
+ .filter-input {
513
+ width: 100%;
514
+ padding: var(--space-2) var(--space-8) var(--space-2) var(--space-3);
515
+ border: 1px solid var(--color-border-default);
516
+ border-radius: var(--radius-md);
517
+ background: var(--color-bg-default);
518
+ color: var(--color-text-primary);
519
+ font-size: var(--text-sm);
520
+ transition: all var(--duration-fast);
521
+ }
522
+
523
+ .filter-input:focus {
524
+ outline: none;
525
+ border-color: var(--color-border-focus);
526
+ box-shadow: 0 0 0 2px var(--color-accent-blue-bg);
527
+ }
528
+
529
+ .filter-icon {
530
+ position: absolute;
531
+ right: var(--space-3);
532
+ color: var(--color-text-tertiary);
533
+ pointer-events: none;
534
+ }
535
+
375
536
  /* Navigation */
376
537
  .navigation {
538
+ flex: 1;
377
539
  padding: var(--space-2);
378
540
  overflow-y: auto;
379
541
  overflow-x: visible;
380
- flex: 1;
381
- position: relative;
382
542
 
383
543
  /* Scrollbar styling */
384
544
  scrollbar-width: thin;
385
545
  scrollbar-color: var(--color-border-default) transparent;
386
546
  }
387
547
 
548
+ .resize-handle {
549
+ width: 4px;
550
+ background: transparent;
551
+ cursor: col-resize;
552
+ position: absolute;
553
+ top: 0;
554
+ right: 0;
555
+ bottom: 0;
556
+ transition: background var(--duration-fast);
557
+ }
558
+
559
+ .resize-handle:hover {
560
+ background: var(--color-border-focus);
561
+ }
562
+
388
563
  .navigation::-webkit-scrollbar {
389
564
  width: 6px;
390
565
  }
@@ -530,14 +705,14 @@ pre code {
530
705
 
531
706
  .content {
532
707
  flex: 1;
533
- margin-left: var(--sidebar-width);
534
- padding: var(--space-10) var(--space-8);
535
- max-width: calc(65rem + var(--space-8) * 2);
536
- transition: margin-left var(--duration-normal);
708
+ padding: var(--space-6) var(--space-8);
709
+ overflow-y: auto;
710
+ background: var(--color-bg-default);
537
711
  }
538
712
 
539
713
  .content-inner {
540
714
  max-width: 65rem;
715
+ margin: 0 auto;
541
716
  }
542
717
 
543
718
  /* Tables */
@@ -1459,50 +1634,125 @@ tr:hover {
1459
1634
  [data-tooltip]::after {
1460
1635
  display: none;
1461
1636
  }
1637
+
1638
+ .main-wrapper {
1639
+ flex-direction: column;
1640
+ height: auto;
1641
+ min-height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
1642
+ }
1643
+
1644
+ .sidebar {
1645
+ width: 100%;
1646
+ max-height: 250px;
1647
+ border-right: none;
1648
+ border-bottom: 1px solid var(--color-border-default);
1649
+ }
1650
+
1651
+ .content {
1652
+ padding: var(--space-4);
1653
+ }
1654
+
1655
+ .content-inner {
1656
+ max-width: none;
1657
+ }
1658
+
1659
+ .preview-banner {
1660
+ position: relative;
1661
+ top: 0;
1662
+ }
1663
+
1664
+ .breadcrumbs {
1665
+ position: relative;
1666
+ top: 0;
1667
+ }
1462
1668
  }
1463
1669
 
1464
- /* Navigation: Enhanced for all structures */
1465
- .nav-item {
1466
- margin-bottom: var(--space-0-5);
1670
+ /* Navigation Sections */
1671
+ .nav-section {
1672
+ margin-bottom: var(--space-1);
1467
1673
  }
1468
1674
 
1469
- .nav-item .nav-link {
1470
- display: block;
1471
- padding: var(--space-1) var(--space-2);
1472
- color: var(--color-text-secondary);
1675
+ .nav-title {
1676
+ display: flex;
1677
+ align-items: center;
1678
+ gap: var(--space-2);
1679
+ padding: var(--space-2);
1680
+ color: var(--color-text-primary);
1473
1681
  text-decoration: none;
1474
- border-radius: var(--border-radius-md);
1475
- transition: all 0.2s ease;
1476
1682
  font-size: var(--text-sm);
1477
- font-weight: 400;
1683
+ font-weight: var(--font-medium);
1684
+ border-radius: var(--radius-md);
1685
+ transition: all var(--duration-fast);
1686
+ cursor: pointer;
1478
1687
  }
1479
1688
 
1480
- .nav-item .nav-link:hover {
1689
+ .nav-title:hover {
1481
1690
  background: var(--color-bg-hover);
1482
- color: var(--color-text-primary);
1483
1691
  }
1484
1692
 
1485
- .nav-item.active .nav-link,
1486
- .nav-item .nav-link.active {
1487
- background: var(--color-accent-blue-bg);
1488
- color: var(--color-accent-blue);
1489
- font-weight: 500;
1693
+ .nav-title.collapsible {
1694
+ position: relative;
1490
1695
  }
1491
1696
 
1492
- /* Ensure nav sections are visible and functional */
1493
- .nav-section {
1494
- margin-bottom: var(--space-1);
1697
+ .nav-title .collapse-icon {
1698
+ transition: transform var(--duration-fast);
1699
+ color: var(--color-text-tertiary);
1700
+ font-size: var(--text-xs);
1701
+ }
1702
+
1703
+ .nav-title.expanded .collapse-icon {
1704
+ transform: rotate(90deg);
1495
1705
  }
1496
1706
 
1497
1707
  .nav-content {
1498
- padding-left: var(--space-2);
1708
+ padding-left: var(--space-4);
1709
+ overflow: hidden;
1710
+ transition: max-height var(--duration-normal), opacity var(--duration-normal);
1499
1711
  }
1500
1712
 
1501
- /* Override any conflicting styles */
1502
- .navigation .nav-section .nav-content {
1503
- display: block;
1713
+ .nav-content.collapsed {
1714
+ max-height: 0;
1715
+ opacity: 0;
1716
+ pointer-events: none;
1504
1717
  }
1505
1718
 
1506
- .navigation .nav-section .nav-content.collapsed {
1507
- display: none;
1719
+ .nav-content:not(.collapsed) {
1720
+ max-height: 1000px;
1721
+ opacity: 1;
1722
+ }
1723
+
1724
+ /* Navigation Items */
1725
+ .nav-item {
1726
+ display: flex;
1727
+ align-items: center;
1728
+ gap: var(--space-2);
1729
+ padding: var(--space-1-5) var(--space-2);
1730
+ color: var(--color-text-secondary);
1731
+ text-decoration: none;
1732
+ font-size: var(--text-sm);
1733
+ border-radius: var(--radius-md);
1734
+ transition: all var(--duration-fast);
1735
+ margin-bottom: var(--space-0-5);
1736
+ }
1737
+
1738
+ .nav-item:hover {
1739
+ background: var(--color-bg-hover);
1740
+ color: var(--color-text-primary);
1741
+ }
1742
+
1743
+ .nav-item.active {
1744
+ background: var(--color-accent-blue-bg);
1745
+ color: var(--color-accent-blue);
1746
+ font-weight: var(--font-medium);
1747
+ }
1748
+
1749
+ .nav-item i {
1750
+ color: var(--color-text-tertiary);
1751
+ font-size: var(--text-xs);
1752
+ width: 12px;
1753
+ text-align: center;
1754
+ }
1755
+
1756
+ .nav-item.active i {
1757
+ color: var(--color-accent-blue);
1508
1758
  }
@@ -123,18 +123,43 @@ function generateHTML(title, content, navigation, currentPath = '', config = {})
123
123
  </div>
124
124
  </header>
125
125
 
126
- <div class="container">
127
- <!-- Navigation -->
128
- <nav id="navigation" class="navigation">
129
- <div class="nav-header">
130
- <h3>${siteName}</h3>
126
+ <!-- Preview Banner -->
127
+ <div id="preview-banner" class="preview-banner">
128
+ <div class="banner-content">
129
+ <i class="fas fa-exclamation-triangle banner-icon"></i>
130
+ <span class="banner-text">This documentation is a preview version - some content may be incomplete</span>
131
+ <button id="dismiss-banner" class="banner-dismiss" aria-label="Dismiss banner">
132
+ <i class="fas fa-times"></i>
133
+ </button>
134
+ </div>
135
+ </div>
136
+
137
+ <!-- Breadcrumbs -->
138
+ <nav class="breadcrumbs" id="breadcrumbs">
139
+ <!-- Breadcrumbs will be generated by JavaScript -->
140
+ </nav>
141
+
142
+ <!-- Main Content -->
143
+ <div class="main-wrapper">
144
+ <!-- Sidebar -->
145
+ <aside class="sidebar">
146
+ <div class="sidebar-header">
147
+ <div class="filter-box">
148
+ <input type="text" placeholder="Filter items..." class="filter-input" id="nav-filter">
149
+ <i class="fas fa-search filter-icon"></i>
150
+ </div>
131
151
  </div>
132
- ${navigation}
133
- </nav>
152
+ <nav class="navigation">
153
+ ${navigation}
154
+ </nav>
155
+ <div class="resize-handle"></div>
156
+ </aside>
134
157
 
135
- <!-- Main Content -->
158
+ <!-- Content Area -->
136
159
  <main class="content">
137
- ${content}
160
+ <div class="content-inner">
161
+ ${content}
162
+ </div>
138
163
  </main>
139
164
  </div>
140
165
 
@@ -145,7 +170,28 @@ function generateHTML(title, content, navigation, currentPath = '', config = {})
145
170
  </html>`;
146
171
  }
147
172
 
148
- // Build navigation structure
173
+ // Define folder descriptions for tooltips
174
+ const folderDescriptions = {
175
+ 'product-roadmap': 'Strategic vision, timeline, and feature planning',
176
+ 'product-requirements': 'Detailed product specifications, requirements documents, and feature definitions',
177
+ 'architecture': 'System design, data flows, and technical infrastructure documentation',
178
+ 'system-analysis': 'Comprehensive system analysis, functional requirements, and cross-component documentation',
179
+ 'bubble': 'Core application platform - business logic, UI/UX, and user workflows',
180
+ 'quickbase': 'Database schema, data management, and backend operations',
181
+ 'activecampaign': 'Marketing automation integration and lead management system',
182
+ 'juno-signer': 'Document signing service for digital signatures and PDF generation',
183
+ 'juno-api-deprecated': 'Legacy API documentation (deprecated, for reference only)',
184
+ 'postman': 'API testing tools, collections, and test automation',
185
+ 'mcp': 'Model Context Protocol integration and configuration',
186
+ 'team': 'Team structure, roles, and responsibilities',
187
+ 'thought-leadership': 'Strategic insights and industry perspectives',
188
+ 'middleware': 'Integration layers and data transformation services',
189
+ 'paths': 'User journey flows and process workflows',
190
+ 'testing': 'Test strategies, scenarios, and quality assurance processes',
191
+ 'juno-api': 'API documentation and integration guides'
192
+ };
193
+
194
+ // Build navigation structure with rich functionality
149
195
  function buildNavigationStructure(files, currentFile) {
150
196
  const tree = { files: [], folders: {} };
151
197
 
@@ -166,71 +212,176 @@ function buildNavigationStructure(files, currentFile) {
166
212
  current.files.push(file);
167
213
  });
168
214
 
169
- // Check if this is a flat structure
170
- const hasFolders = Object.keys(tree.folders).length > 0;
215
+ // Helper function to check if a node has active child
216
+ const checkActiveChild = (node, currentFile) => {
217
+ // Check files
218
+ if (node.files.some(f => f.urlPath === currentFile)) return true;
219
+
220
+ // Check folders recursively
221
+ return Object.values(node.folders).some(folder => checkActiveChild(folder, currentFile));
222
+ };
171
223
 
172
- if (!hasFolders) {
173
- // Generate simple flat navigation that matches what JavaScript expects
174
- let html = '<div class="nav-section">';
175
- html += '<div class="nav-content">';
176
-
177
- tree.files.forEach(file => {
178
- const isActive = file.urlPath === currentFile;
179
- const href = file.urlPath.replace(/\\/g, '/');
180
- html += `<div class="nav-item ${isActive ? 'active' : ''}">
181
- <a href="${href}" class="nav-link ${isActive ? 'active' : ''}">
182
- <span class="nav-text">${file.displayName}</span>
224
+ // Helper function to generate file title
225
+ const generateFileTitle = (file, parentDisplayName, level) => {
226
+ let title = file.displayName;
227
+
228
+ if (file.displayName === 'README') {
229
+ return level === 0 ? 'Overview' : `${parentDisplayName} Overview`;
230
+ }
231
+
232
+ // Clean up title by removing common prefixes and improving formatting
233
+ title = title
234
+ .replace(/^(bubble|system|quickbase|middleware|product-roadmap)-?/, '')
235
+ .replace(/-/g, ' ')
236
+ .replace(/\b\w/g, l => l.toUpperCase());
237
+
238
+ return title;
239
+ };
240
+
241
+ // Helper function to render a section
242
+ const renderSection = (folderName, folderData, level = 0, parentPath = '') => {
243
+ const icons = {
244
+ 'root': 'fas fa-home',
245
+ 'product-roadmap': 'fas fa-road',
246
+ 'product-requirements': 'fas fa-clipboard-list',
247
+ 'architecture': 'fas fa-sitemap',
248
+ 'system-analysis': 'fas fa-chart-line',
249
+ 'system': 'fas fa-cogs',
250
+ 'bubble': 'fas fa-circle',
251
+ 'quickbase': 'fas fa-database',
252
+ 'activecampaign': 'fas fa-envelope',
253
+ 'juno-signer': 'fas fa-signature',
254
+ 'juno-api-deprecated': 'fas fa-archive',
255
+ 'postman': 'fas fa-flask',
256
+ 'mcp': 'fas fa-puzzle-piece',
257
+ 'team': 'fas fa-users',
258
+ 'thought-leadership': 'fas fa-lightbulb',
259
+ 'middleware': 'fas fa-layer-group',
260
+ 'paths': 'fas fa-route',
261
+ 'testing': 'fas fa-vial',
262
+ 'juno-api': 'fas fa-plug',
263
+ 'documentation-tool': 'fas fa-tools'
264
+ };
265
+
266
+ const displayName = folderName === 'root' ? 'Documentation' :
267
+ folderName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
268
+ const icon = icons[folderName] || 'fas fa-folder';
269
+
270
+ if (!folderData.files.length && !Object.keys(folderData.folders).length) {
271
+ return '';
272
+ }
273
+
274
+ // Include parent path in section ID to make it unique
275
+ const pathParts = parentPath ? [parentPath, folderName].join('-') : folderName;
276
+ const sectionId = `nav-${pathParts}-${level}`;
277
+ const isCollapsible = level > 0 || folderName !== 'root';
278
+ const collapseIcon = isCollapsible ? '<i class="fas fa-chevron-right collapse-icon"></i>' : '';
279
+
280
+ // Check if this folder has a README.md file to link to
281
+ const readmeFile = folderData.files.find(f => f.displayName === 'README');
282
+ const folderLink = readmeFile ? `href="/${readmeFile.urlPath}"` : 'href="#"';
283
+
284
+ // Get folder description for tooltip
285
+ const folderDescription = folderDescriptions[folderName] || '';
286
+ const tooltipAttr = folderDescription ? `data-tooltip="${escapeHtml(folderDescription)}"` : '';
287
+
288
+ // Start all sections collapsed by default (JavaScript will expand sections containing active items)
289
+ const hasActiveChild = checkActiveChild(folderData, currentFile);
290
+
291
+ let html = `
292
+ <div class="nav-section" data-level="${level}">
293
+ <a class="nav-title${isCollapsible ? ' collapsible' : ''}${hasActiveChild ? ' expanded' : ''}" ${folderLink} ${isCollapsible ? `data-target="${sectionId}"` : ''} ${tooltipAttr}>
294
+ ${collapseIcon}<i class="${icon}"></i> ${displayName}
183
295
  </a>
184
- </div>`;
296
+ <div class="nav-content${isCollapsible ? (hasActiveChild ? '' : ' collapsed') : ''}" ${isCollapsible ? `id="${sectionId}"` : ''}>`;
297
+
298
+ // Sort and render files
299
+ const sortedFiles = [...folderData.files].sort((a, b) => {
300
+ if (a.displayName === 'README') return -1;
301
+ if (b.displayName === 'README') return 1;
302
+ return a.displayName.localeCompare(b.displayName);
185
303
  });
186
304
 
187
- html += '</div></div>';
188
- return html;
189
- } else {
190
- // Generate collapsible navigation for folder structures
191
- const generateNavHTML = (node, path = '', level = 0) => {
192
- let html = '';
305
+ sortedFiles.forEach(file => {
306
+ const title = generateFileTitle(file, displayName, level);
193
307
 
194
- // Add folders as sections
195
- Object.entries(node.folders).forEach(([folderName, folderNode]) => {
196
- const folderPath = path ? `${path}/${folderName}` : folderName;
197
- const hasActiveChild = checkActiveChild(folderNode, currentFile);
198
- const sectionId = `nav-${folderPath.replace(/\//g, '-')}`;
199
-
200
- html += `<div class="nav-section ${hasActiveChild ? 'expanded' : ''}">
201
- <div class="nav-title collapsible ${hasActiveChild ? 'expanded' : ''}" data-target="${sectionId}">
202
- <i class="fas fa-chevron-${hasActiveChild ? 'down' : 'right'}"></i>
203
- <span class="nav-text">${folderName}</span>
204
- </div>
205
- <div class="nav-content ${hasActiveChild ? '' : 'collapsed'}" id="${sectionId}">
206
- ${generateNavHTML(folderNode, folderPath, level + 1)}
207
- </div>
208
- </div>`;
209
- });
308
+ // Check if this file is active
309
+ let isActive = '';
310
+ if (currentFile === file.urlPath) {
311
+ isActive = ' active';
312
+ } else if (currentFile === 'index.html' && file.displayName === 'README' && folderName === 'root') {
313
+ // Mark root README as active when viewing index.html
314
+ isActive = ' active';
315
+ }
210
316
 
211
- // Add files
212
- node.files.forEach(file => {
213
- const isActive = file.urlPath === currentFile;
214
- const href = file.urlPath.replace(/\\/g, '/');
215
- html += `<div class="nav-item ${isActive ? 'active' : ''}">
216
- <a href="${href}" class="nav-link ${isActive ? 'active' : ''}">
217
- <span class="nav-text">${file.displayName}</span>
218
- </a>
219
- </div>`;
220
- });
317
+ const linkPath = '/' + file.urlPath;
221
318
 
222
- return html;
223
- };
319
+ html += `
320
+ <a href="${linkPath}" class="nav-item${isActive}"><i class="fas fa-file-alt"></i> ${title}</a>`;
321
+ });
224
322
 
225
- const checkActiveChild = (node, currentFile) => {
226
- // Check files
227
- if (node.files.some(f => f.urlPath === currentFile)) return true;
228
-
229
- // Check folders recursively
230
- return Object.values(node.folders).some(folder => checkActiveChild(folder, currentFile));
231
- };
323
+ html += `</div></div>`;
324
+
325
+ // Render subfolders AFTER closing the parent section
326
+ Object.keys(folderData.folders)
327
+ .sort()
328
+ .forEach(subFolder => {
329
+ // Build the path for the subfolder including current folder
330
+ const currentPath = parentPath ? `${parentPath}-${folderName}` : folderName;
331
+ html += renderSection(subFolder, folderData.folders[subFolder], level + 1, currentPath);
332
+ });
333
+
334
+ return html;
335
+ };
336
+
337
+ // Check if this is a flat structure
338
+ const hasFolders = Object.keys(tree.folders).length > 0;
339
+
340
+ if (!hasFolders) {
341
+ // Generate simple flat navigation for all files in root
342
+ return renderSection('root', { files: tree.files, folders: {} }, 0);
343
+ } else {
344
+ // Generate hierarchical navigation
345
+ let nav = '';
346
+
347
+ // Render root files first
348
+ if (tree.files.length > 0) {
349
+ nav += renderSection('root', { files: tree.files, folders: {} }, 0);
350
+ }
351
+
352
+ // Add other top-level folders in logical order
353
+ const folderOrder = [
354
+ 'product-roadmap',
355
+ 'product-requirements',
356
+ 'architecture',
357
+ 'system-analysis',
358
+ 'bubble',
359
+ 'quickbase',
360
+ 'activecampaign',
361
+ 'juno-signer',
362
+ 'juno-api-deprecated',
363
+ 'postman',
364
+ 'mcp',
365
+ 'team',
366
+ 'thought-leadership',
367
+ 'documentation-tool'
368
+ ];
369
+
370
+ folderOrder.forEach(folderName => {
371
+ if (tree.folders[folderName]) {
372
+ nav += renderSection(folderName, tree.folders[folderName], 1);
373
+ delete tree.folders[folderName]; // Remove so we don't render it again
374
+ }
375
+ });
376
+
377
+ // Render any remaining folders not in the predefined order
378
+ Object.keys(tree.folders)
379
+ .sort()
380
+ .forEach(folderName => {
381
+ nav += renderSection(folderName, tree.folders[folderName], 1);
382
+ });
232
383
 
233
- return generateNavHTML(tree);
384
+ return nav;
234
385
  }
235
386
  }
236
387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowcode/doc-builder",
3
- "version": "1.1.11",
3
+ "version": "1.2.0",
4
4
  "description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
5
5
  "main": "index.js",
6
6
  "bin": {