@knowcode/doc-builder 1.1.12 → 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,44 @@ 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
+
8
46
  ## [1.1.12] - 2025-07-19
9
47
 
10
48
  ### Fixed (MAJOR)
@@ -372,32 +372,194 @@ pre code {
372
372
  font-size: var(--text-sm);
373
373
  }
374
374
 
375
- /* Layout Container */
376
- .container {
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 {
377
480
  display: flex;
378
- height: calc(100vh - var(--header-height));
481
+ height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
482
+ margin-top: calc(var(--header-height) + var(--breadcrumb-height));
379
483
  overflow: hidden;
380
484
  }
381
485
 
382
- /* Navigation */
383
- .navigation {
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 {
384
493
  width: var(--sidebar-width);
385
494
  background: var(--color-bg-secondary);
386
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
+
536
+ /* Navigation */
537
+ .navigation {
538
+ flex: 1;
387
539
  padding: var(--space-2);
388
540
  overflow-y: auto;
389
541
  overflow-x: visible;
390
- position: fixed;
391
- top: var(--header-height);
392
- left: 0;
393
- height: calc(100vh - var(--header-height));
394
- z-index: 100;
395
542
 
396
543
  /* Scrollbar styling */
397
544
  scrollbar-width: thin;
398
545
  scrollbar-color: var(--color-border-default) transparent;
399
546
  }
400
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
+
401
563
  .navigation::-webkit-scrollbar {
402
564
  width: 6px;
403
565
  }
@@ -543,15 +705,14 @@ pre code {
543
705
 
544
706
  .content {
545
707
  flex: 1;
546
- margin-left: var(--sidebar-width);
547
708
  padding: var(--space-6) var(--space-8);
548
709
  overflow-y: auto;
549
- height: calc(100vh - var(--header-height));
550
- max-width: none;
710
+ background: var(--color-bg-default);
551
711
  }
552
712
 
553
713
  .content-inner {
554
714
  max-width: 65rem;
715
+ margin: 0 auto;
555
716
  }
556
717
 
557
718
  /* Tables */
@@ -1474,70 +1635,124 @@ tr:hover {
1474
1635
  display: none;
1475
1636
  }
1476
1637
 
1477
- .container {
1638
+ .main-wrapper {
1478
1639
  flex-direction: column;
1640
+ height: auto;
1641
+ min-height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
1479
1642
  }
1480
1643
 
1481
- .navigation {
1482
- position: relative;
1644
+ .sidebar {
1483
1645
  width: 100%;
1484
- height: auto;
1485
- max-height: 200px;
1486
- top: 0;
1487
- left: 0;
1646
+ max-height: 250px;
1488
1647
  border-right: none;
1489
1648
  border-bottom: 1px solid var(--color-border-default);
1490
1649
  }
1491
1650
 
1492
1651
  .content {
1493
- margin-left: 0;
1494
- height: auto;
1495
- min-height: calc(100vh - var(--header-height) - 200px);
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;
1496
1667
  }
1497
1668
  }
1498
1669
 
1499
- /* Navigation: Enhanced for all structures */
1500
- .nav-item {
1501
- margin-bottom: var(--space-0-5);
1670
+ /* Navigation Sections */
1671
+ .nav-section {
1672
+ margin-bottom: var(--space-1);
1502
1673
  }
1503
1674
 
1504
- .nav-item .nav-link {
1505
- display: block;
1506
- padding: var(--space-1) var(--space-2);
1507
- 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);
1508
1681
  text-decoration: none;
1509
- border-radius: var(--border-radius-md);
1510
- transition: all 0.2s ease;
1511
1682
  font-size: var(--text-sm);
1512
- font-weight: 400;
1683
+ font-weight: var(--font-medium);
1684
+ border-radius: var(--radius-md);
1685
+ transition: all var(--duration-fast);
1686
+ cursor: pointer;
1513
1687
  }
1514
1688
 
1515
- .nav-item .nav-link:hover {
1689
+ .nav-title:hover {
1516
1690
  background: var(--color-bg-hover);
1517
- color: var(--color-text-primary);
1518
1691
  }
1519
1692
 
1520
- .nav-item.active .nav-link,
1521
- .nav-item .nav-link.active {
1522
- background: var(--color-accent-blue-bg);
1523
- color: var(--color-accent-blue);
1524
- font-weight: 500;
1693
+ .nav-title.collapsible {
1694
+ position: relative;
1525
1695
  }
1526
1696
 
1527
- /* Ensure nav sections are visible and functional */
1528
- .nav-section {
1529
- 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);
1530
1705
  }
1531
1706
 
1532
1707
  .nav-content {
1533
- 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);
1534
1711
  }
1535
1712
 
1536
- /* Override any conflicting styles */
1537
- .navigation .nav-section .nav-content {
1538
- display: block;
1713
+ .nav-content.collapsed {
1714
+ max-height: 0;
1715
+ opacity: 0;
1716
+ pointer-events: none;
1539
1717
  }
1540
1718
 
1541
- .navigation .nav-section .nav-content.collapsed {
1542
- 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);
1543
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.12",
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": {