@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 +38 -0
- package/assets/css/notion-style.css +264 -49
- package/lib/core-builder.js +217 -66
- package/package.json +1 -1
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
|
-
/*
|
|
376
|
-
.
|
|
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
|
-
|
|
383
|
-
.
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
-
.
|
|
1482
|
-
position: relative;
|
|
1644
|
+
.sidebar {
|
|
1483
1645
|
width: 100%;
|
|
1484
|
-
height:
|
|
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
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
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
|
|
1500
|
-
.nav-
|
|
1501
|
-
margin-bottom: var(--space-
|
|
1670
|
+
/* Navigation Sections */
|
|
1671
|
+
.nav-section {
|
|
1672
|
+
margin-bottom: var(--space-1);
|
|
1502
1673
|
}
|
|
1503
1674
|
|
|
1504
|
-
.nav-
|
|
1505
|
-
display:
|
|
1506
|
-
|
|
1507
|
-
|
|
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:
|
|
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-
|
|
1689
|
+
.nav-title:hover {
|
|
1516
1690
|
background: var(--color-bg-hover);
|
|
1517
|
-
color: var(--color-text-primary);
|
|
1518
1691
|
}
|
|
1519
1692
|
|
|
1520
|
-
.nav-
|
|
1521
|
-
|
|
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
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
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-
|
|
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
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1713
|
+
.nav-content.collapsed {
|
|
1714
|
+
max-height: 0;
|
|
1715
|
+
opacity: 0;
|
|
1716
|
+
pointer-events: none;
|
|
1539
1717
|
}
|
|
1540
1718
|
|
|
1541
|
-
.
|
|
1542
|
-
|
|
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
|
}
|
package/lib/core-builder.js
CHANGED
|
@@ -123,18 +123,43 @@ function generateHTML(title, content, navigation, currentPath = '', config = {})
|
|
|
123
123
|
</div>
|
|
124
124
|
</header>
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
<
|
|
130
|
-
|
|
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
|
-
|
|
133
|
-
|
|
152
|
+
<nav class="navigation">
|
|
153
|
+
${navigation}
|
|
154
|
+
</nav>
|
|
155
|
+
<div class="resize-handle"></div>
|
|
156
|
+
</aside>
|
|
134
157
|
|
|
135
|
-
<!--
|
|
158
|
+
<!-- Content Area -->
|
|
136
159
|
<main class="content">
|
|
137
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
170
|
-
const
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
let
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
223
|
-
|
|
319
|
+
html += `
|
|
320
|
+
<a href="${linkPath}" class="nav-item${isActive}"><i class="fas fa-file-alt"></i> ${title}</a>`;
|
|
321
|
+
});
|
|
224
322
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
384
|
+
return nav;
|
|
234
385
|
}
|
|
235
386
|
}
|
|
236
387
|
|