@knowcode/doc-builder 1.4.1 → 1.4.2

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,13 @@ 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.4.2] - 2025-01-20
9
+
10
+ ### Fixed
11
+ - Fixed excessive left padding on content area by removing JavaScript margin-left assignments
12
+ - Content layout now properly uses flexbox spacing without manual margin adjustments
13
+ - The JavaScript was adding inline `style="margin-left: 200px;"` which was causing the issue
14
+
8
15
  ## [1.4.1] - 2025-07-20
9
16
 
10
17
  ### Fixed
@@ -125,8 +125,8 @@
125
125
  /* Layout */
126
126
  --container-padding-mobile: 20px;
127
127
  --container-padding-desktop: 40px;
128
- --header-height: 64px;
129
- --breadcrumb-height: 40px;
128
+ --header-height: 40px;
129
+ --breadcrumb-height: 0px;
130
130
  --sidebar-width: 280px;
131
131
  }
132
132
 
@@ -169,6 +169,7 @@ h2 {
169
169
  h3 {
170
170
  font-size: var(--text-xl);
171
171
  margin-top: var(--space-8);
172
+ margin-bottom: var(--space-4);
172
173
  }
173
174
 
174
175
  h4 {
@@ -332,7 +333,42 @@ pre code {
332
333
  top: calc(var(--header-height) + var(--breadcrumb-height) + 3.5rem);
333
334
  }
334
335
 
335
- /* Sidebar header styles moved to main sidebar section */
336
+ .sidebar-header {
337
+ padding: var(--space-4);
338
+ border-bottom: 1px solid var(--color-border-default);
339
+ margin-bottom: var(--space-4);
340
+ }
341
+
342
+ /* Sidebar Breadcrumbs */
343
+ .sidebar-breadcrumbs {
344
+ margin-bottom: var(--space-3);
345
+ }
346
+
347
+ .sidebar-home-link {
348
+ display: inline-flex;
349
+ align-items: center;
350
+ gap: var(--space-2);
351
+ padding: var(--space-2) var(--space-3);
352
+ color: var(--color-text-secondary);
353
+ text-decoration: none;
354
+ border-radius: var(--radius-base);
355
+ font-size: 0.875rem;
356
+ font-weight: 500;
357
+ transition: all 0.2s;
358
+ }
359
+
360
+ .sidebar-home-link:hover {
361
+ background: var(--color-bg-secondary);
362
+ color: var(--color-text-primary);
363
+ }
364
+
365
+ .sidebar-home-link i {
366
+ font-size: 1rem;
367
+ }
368
+
369
+ .filter-box {
370
+ position: relative;
371
+ }
336
372
 
337
373
  .filter-input {
338
374
  width: 100%;
@@ -493,6 +529,12 @@ pre code {
493
529
  .sidebar-header {
494
530
  padding: var(--space-3);
495
531
  border-bottom: 1px solid var(--color-border-default);
532
+ margin-bottom: var(--space-4);
533
+ }
534
+
535
+ /* Already defined above, but ensure consistency */
536
+ .sidebar-breadcrumbs {
537
+ margin-bottom: var(--space-3);
496
538
  }
497
539
 
498
540
  .filter-box {
@@ -683,11 +725,21 @@ pre code {
683
725
  }
684
726
 
685
727
  /* Main Content */
686
- /* Removed duplicate .main-wrapper - using the one defined earlier */
728
+ .main-wrapper {
729
+ display: flex;
730
+ padding-top: calc(var(--header-height) + var(--breadcrumb-height));
731
+ min-height: 100vh;
732
+ transition: padding-top var(--duration-normal);
733
+ }
734
+
735
+ /* Adjust layout when banner is visible */
736
+ .main-wrapper.banner-visible {
737
+ padding-top: calc(var(--header-height) + var(--breadcrumb-height) + 3.5rem);
738
+ }
687
739
 
688
740
  .content {
689
741
  flex: 1;
690
- padding: var(--space-6) var(--space-8);
742
+ padding: 40px var(--space-8);
691
743
  overflow-y: auto;
692
744
  background: var(--color-bg-default);
693
745
  }
@@ -788,6 +840,7 @@ tr:hover {
788
840
  border: 1px solid var(--color-border-default);
789
841
  border-radius: var(--radius-lg);
790
842
  padding: var(--space-6);
843
+ margin-bottom: var(--space-4);
791
844
  box-shadow: var(--shadow-sm);
792
845
  transition: all var(--duration-normal) var(--easing-out);
793
846
  }
@@ -970,43 +1023,6 @@ tr:hover {
970
1023
  cursor: pointer;
971
1024
  color: var(--color-text-primary);
972
1025
  padding: var(--space-2);
973
- margin-left: var(--space-2);
974
- transition: color var(--duration-fast);
975
- z-index: 1001;
976
- }
977
-
978
- .menu-toggle:hover {
979
- color: var(--color-accent-blue);
980
- }
981
-
982
- .menu-toggle.active i::before {
983
- content: "\f00d"; /* fa-times icon */
984
- }
985
-
986
- /* Mobile Sidebar Overlay */
987
- .sidebar-overlay {
988
- position: fixed;
989
- top: 0;
990
- left: 0;
991
- right: 0;
992
- bottom: 0;
993
- background: var(--color-bg-overlay);
994
- z-index: 998;
995
- opacity: 0;
996
- visibility: hidden;
997
- transition: opacity var(--duration-normal), visibility var(--duration-normal);
998
- display: none;
999
- }
1000
-
1001
- @media (max-width: 1024px) {
1002
- .sidebar-overlay {
1003
- display: block;
1004
- }
1005
- }
1006
-
1007
- .sidebar-overlay.active {
1008
- opacity: 1;
1009
- visibility: visible;
1010
1026
  }
1011
1027
 
1012
1028
  /* Responsive Design */
@@ -1014,8 +1030,6 @@ tr:hover {
1014
1030
  .sidebar {
1015
1031
  transform: translateX(-100%);
1016
1032
  transition: transform var(--duration-normal) var(--easing-out);
1017
- z-index: 999;
1018
- box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1);
1019
1033
  }
1020
1034
 
1021
1035
  .sidebar.open {
@@ -1027,6 +1041,7 @@ tr:hover {
1027
1041
  }
1028
1042
 
1029
1043
  .content {
1044
+ margin-left: 0;
1030
1045
  padding: var(--space-6) var(--space-4);
1031
1046
  }
1032
1047
 
@@ -1434,6 +1449,7 @@ tr:hover {
1434
1449
  font-size: var(--text-lg);
1435
1450
  font-weight: var(--font-semibold);
1436
1451
  color: var(--color-text-primary);
1452
+ margin-bottom: var(--space-4);
1437
1453
  padding-bottom: var(--space-2);
1438
1454
  border-bottom: 2px solid var(--color-divider);
1439
1455
  }
@@ -1806,9 +1822,11 @@ tr:hover {
1806
1822
  .auth-box p {
1807
1823
  color: var(--color-text-secondary);
1808
1824
  text-align: center;
1825
+ margin-bottom: var(--space-4);
1809
1826
  }
1810
1827
 
1811
1828
  .form-group {
1829
+ margin-bottom: var(--space-4);
1812
1830
  }
1813
1831
 
1814
1832
  .form-group label {
package/assets/js/main.js CHANGED
@@ -511,30 +511,10 @@ function updateThemeIcon(theme) {
511
511
  // Mobile Menu Toggle
512
512
  const menuToggle = document.getElementById('menu-toggle');
513
513
  const sidebar = document.querySelector('.sidebar');
514
- const sidebarOverlay = document.querySelector('.sidebar-overlay');
515
514
 
516
515
  if (menuToggle) {
517
516
  menuToggle.addEventListener('click', () => {
518
517
  sidebar.classList.toggle('open');
519
- sidebarOverlay.classList.toggle('active');
520
- menuToggle.classList.toggle('active');
521
-
522
- // Prevent body scroll when menu is open
523
- if (sidebar.classList.contains('open')) {
524
- document.body.style.overflow = 'hidden';
525
- } else {
526
- document.body.style.overflow = '';
527
- }
528
- });
529
- }
530
-
531
- // Close sidebar when clicking overlay
532
- if (sidebarOverlay) {
533
- sidebarOverlay.addEventListener('click', () => {
534
- sidebar.classList.remove('open');
535
- sidebarOverlay.classList.remove('active');
536
- menuToggle.classList.remove('active');
537
- document.body.style.overflow = '';
538
518
  });
539
519
  }
540
520
 
@@ -542,17 +522,14 @@ if (sidebarOverlay) {
542
522
  // Only close when clicking outside the sidebar or the close button
543
523
  document.addEventListener('click', (e) => {
544
524
  // Check if we're on mobile
545
- if (window.innerWidth <= 1024) {
525
+ if (window.innerWidth <= 768) {
546
526
  const isClickInsideSidebar = sidebar && sidebar.contains(e.target);
547
527
  const isMenuToggle = e.target.closest('#menu-toggle');
548
- const isOverlay = e.target.closest('.sidebar-overlay');
528
+ const isNavItem = e.target.closest('.nav-item, .nav-title');
549
529
 
550
- // Close sidebar when clicking nav items on mobile
551
- if (e.target.closest('.nav-item') && sidebar?.classList.contains('open')) {
530
+ // Close sidebar only if clicking outside AND not on menu toggle AND not on nav items
531
+ if (!isClickInsideSidebar && !isMenuToggle && !isNavItem && sidebar?.classList.contains('open')) {
552
532
  sidebar.classList.remove('open');
553
- sidebarOverlay?.classList.remove('active');
554
- menuToggle?.classList.remove('active');
555
- document.body.style.overflow = '';
556
533
  }
557
534
  }
558
535
  });
@@ -689,9 +666,6 @@ document.addEventListener('keydown', (e) => {
689
666
  // Escape to close mobile menu
690
667
  if (e.key === 'Escape') {
691
668
  sidebar?.classList.remove('open');
692
- sidebarOverlay?.classList.remove('active');
693
- menuToggle?.classList.remove('active');
694
- document.body.style.overflow = '';
695
669
  }
696
670
  });
697
671
 
@@ -730,7 +704,7 @@ function initSidebarResize() {
730
704
  const savedWidth = localStorage.getItem('sidebarWidth');
731
705
  if (savedWidth && savedWidth >= 200 && savedWidth <= 500) {
732
706
  sidebar.style.width = `${savedWidth}px`;
733
- content.style.marginLeft = `${savedWidth}px`;
707
+ // Don't set margin-left - flexbox handles the layout
734
708
  }
735
709
 
736
710
  // Mouse down on resize handle
@@ -759,7 +733,7 @@ function initSidebarResize() {
759
733
  const constrainedWidth = Math.max(200, Math.min(500, width));
760
734
 
761
735
  sidebar.style.width = `${constrainedWidth}px`;
762
- content.style.marginLeft = `${constrainedWidth}px`;
736
+ // Don't set margin-left - flexbox handles the layout
763
737
 
764
738
  e.preventDefault();
765
739
  }
@@ -801,7 +775,7 @@ function initSidebarResize() {
801
775
  const constrainedWidth = Math.max(200, Math.min(500, width));
802
776
 
803
777
  sidebar.style.width = `${constrainedWidth}px`;
804
- content.style.marginLeft = `${constrainedWidth}px`;
778
+ // Don't set margin-left - flexbox handles the layout
805
779
 
806
780
  e.preventDefault();
807
781
  }
@@ -1085,7 +1059,6 @@ function exportToPDF() {
1085
1059
  const mainWrapper = document.querySelector('.main-wrapper');
1086
1060
 
1087
1061
  if (content) {
1088
- content.style.marginLeft = '0';
1089
1062
  content.style.padding = '20px';
1090
1063
  content.style.maxWidth = 'none';
1091
1064
  }
@@ -1200,7 +1173,6 @@ function exportToPDF() {
1200
1173
 
1201
1174
  // Restore content styles
1202
1175
  if (content) {
1203
- content.style.marginLeft = '';
1204
1176
  content.style.padding = '';
1205
1177
  content.style.maxWidth = '';
1206
1178
  }
@@ -1333,21 +1305,15 @@ function generateBreadcrumbs() {
1333
1305
  // Initialize tooltip positioning for navigation items
1334
1306
  function initTooltips() {
1335
1307
  const tooltipElements = document.querySelectorAll('[data-tooltip]');
1336
- console.log('[Tooltips] Found', tooltipElements.length, 'elements with tooltips');
1337
1308
 
1338
1309
  tooltipElements.forEach(element => {
1339
1310
  element.addEventListener('mouseenter', function(e) {
1340
1311
  const rect = element.getBoundingClientRect();
1312
+ const tooltip = window.getComputedStyle(element, '::after');
1341
1313
 
1342
1314
  // Position the tooltip using CSS variables
1343
1315
  element.style.setProperty('--tooltip-left', `${rect.right + 10}px`);
1344
1316
  element.style.setProperty('--tooltip-top', `${rect.top + rect.height / 2}px`);
1345
- console.log('[Tooltip] Positioned at:', rect.right + 10, rect.top + rect.height / 2);
1346
- });
1347
-
1348
- element.addEventListener('mouseleave', function(e) {
1349
- element.style.removeProperty('--tooltip-left');
1350
- element.style.removeProperty('--tooltip-top');
1351
1317
  });
1352
1318
  });
1353
1319
  }
Binary file
@@ -24,44 +24,6 @@ function escapeHtml(text) {
24
24
  return text.replace(/[&<>"']/g, m => map[m]);
25
25
  }
26
26
 
27
- // Extract summary from markdown content
28
- function extractSummary(content) {
29
- // Look for ## Overview or ## Summary section
30
- const overviewMatch = content.match(/##\s+(?:Overview|Summary)\s*\n([\s\S]*?)(?=\n##|$)/i);
31
- if (overviewMatch) {
32
- // Get first paragraph or up to 200 characters
33
- const text = overviewMatch[1].trim();
34
- const firstPara = text.split('\n\n')[0];
35
- return firstPara.length > 200 ? firstPara.substring(0, 200) + '...' : firstPara;
36
- }
37
-
38
- // Fallback: get first paragraph after title
39
- const lines = content.split('\n');
40
- let foundContent = false;
41
- let summary = '';
42
-
43
- for (const line of lines) {
44
- // Skip title lines
45
- if (line.startsWith('#')) continue;
46
- // Skip metadata lines
47
- if (line.includes('**Generated**:') || line.includes('**Status**:') || line.includes('**Verified**:')) continue;
48
- // Skip empty lines until we find content
49
- if (!foundContent && line.trim() === '') continue;
50
-
51
- if (line.trim()) {
52
- foundContent = true;
53
- summary += line + ' ';
54
- if (summary.length > 200) break;
55
- } else if (foundContent) {
56
- // Found end of first paragraph
57
- break;
58
- }
59
- }
60
-
61
- summary = summary.trim();
62
- return summary.length > 200 ? summary.substring(0, 200) + '...' : summary;
63
- }
64
-
65
27
  // Process markdown content
66
28
  function processMarkdownContent(content) {
67
29
  // Convert mermaid code blocks to mermaid divs with titles
@@ -121,6 +83,7 @@ function generateHTML(title, content, navigation, currentPath = '', config = {})
121
83
 
122
84
  <!-- Styles -->
123
85
  <link rel="stylesheet" href="/css/notion-style.css">
86
+ <link rel="stylesheet" href="/css/style.css">
124
87
 
125
88
  <!-- Favicon -->
126
89
  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📚</text></svg>">
@@ -178,12 +141,15 @@ function generateHTML(title, content, navigation, currentPath = '', config = {})
178
141
 
179
142
  <!-- Main Content -->
180
143
  <div class="main-wrapper">
181
- <!-- Sidebar Overlay -->
182
- <div class="sidebar-overlay"></div>
183
-
184
144
  <!-- Sidebar -->
185
145
  <aside class="sidebar">
186
146
  <div class="sidebar-header">
147
+ <div class="sidebar-breadcrumbs">
148
+ <a href="/index.html" class="sidebar-home-link">
149
+ <i class="fas fa-home"></i>
150
+ <span>Home</span>
151
+ </a>
152
+ </div>
187
153
  <div class="filter-box">
188
154
  <input type="text" placeholder="Filter items..." class="filter-input" id="nav-filter">
189
155
  <i class="fas fa-search filter-icon"></i>
@@ -232,7 +198,7 @@ const folderDescriptions = {
232
198
  };
233
199
 
234
200
  // Build navigation structure with rich functionality
235
- function buildNavigationStructure(files, currentFile, summaries = {}) {
201
+ function buildNavigationStructure(files, currentFile) {
236
202
  const tree = { files: [], folders: {} };
237
203
 
238
204
  files.forEach(file => {
@@ -279,7 +245,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
279
245
  };
280
246
 
281
247
  // Helper function to render a section
282
- const renderSection = (folderName, folderData, level = 0, parentPath = '', summaries = {}) => {
248
+ const renderSection = (folderName, folderData, level = 0, parentPath = '') => {
283
249
  const icons = {
284
250
  'root': 'fas fa-home',
285
251
  'product-roadmap': 'fas fa-road',
@@ -323,7 +289,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
323
289
 
324
290
  // Get folder description for tooltip
325
291
  const folderDescription = folderDescriptions[folderName] || '';
326
- const tooltipAttr = folderDescription ? `data-tooltip="${escapeHtml(folderDescription)}" title="${escapeHtml(folderDescription)}"` : '';
292
+ const tooltipAttr = folderDescription ? `data-tooltip="${escapeHtml(folderDescription)}"` : '';
327
293
 
328
294
  // Start all sections collapsed by default (JavaScript will expand sections containing active items)
329
295
  const hasActiveChild = checkActiveChild(folderData, currentFile);
@@ -356,12 +322,8 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
356
322
 
357
323
  const linkPath = '/' + file.urlPath;
358
324
 
359
- // Get summary for tooltip
360
- const summary = summaries[file.urlPath] || '';
361
- const tooltipAttr = summary ? `data-tooltip="${escapeHtml(summary)}"` : '';
362
-
363
325
  html += `
364
- <a href="${linkPath}" class="nav-item${isActive}" ${tooltipAttr}><i class="fas fa-file-alt"></i> ${title}</a>`;
326
+ <a href="${linkPath}" class="nav-item${isActive}"><i class="fas fa-file-alt"></i> ${title}</a>`;
365
327
  });
366
328
 
367
329
  html += `</div></div>`;
@@ -372,7 +334,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
372
334
  .forEach(subFolder => {
373
335
  // Build the path for the subfolder including current folder
374
336
  const currentPath = parentPath ? `${parentPath}-${folderName}` : folderName;
375
- html += renderSection(subFolder, folderData.folders[subFolder], level + 1, currentPath, summaries);
337
+ html += renderSection(subFolder, folderData.folders[subFolder], level + 1, currentPath);
376
338
  });
377
339
 
378
340
  return html;
@@ -383,14 +345,14 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
383
345
 
384
346
  if (!hasFolders) {
385
347
  // Generate simple flat navigation for all files in root
386
- return renderSection('root', { files: tree.files, folders: {} }, 0, '', summaries);
348
+ return renderSection('root', { files: tree.files, folders: {} }, 0);
387
349
  } else {
388
350
  // Generate hierarchical navigation
389
351
  let nav = '';
390
352
 
391
353
  // Render root files first
392
354
  if (tree.files.length > 0) {
393
- nav += renderSection('root', { files: tree.files, folders: {} }, 0, '', summaries);
355
+ nav += renderSection('root', { files: tree.files, folders: {} }, 0);
394
356
  }
395
357
 
396
358
  // Add other top-level folders in logical order
@@ -413,7 +375,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
413
375
 
414
376
  folderOrder.forEach(folderName => {
415
377
  if (tree.folders[folderName]) {
416
- nav += renderSection(folderName, tree.folders[folderName], 1, '', summaries);
378
+ nav += renderSection(folderName, tree.folders[folderName], 1);
417
379
  delete tree.folders[folderName]; // Remove so we don't render it again
418
380
  }
419
381
  });
@@ -422,7 +384,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
422
384
  Object.keys(tree.folders)
423
385
  .sort()
424
386
  .forEach(folderName => {
425
- nav += renderSection(folderName, tree.folders[folderName], 1, '', summaries);
387
+ nav += renderSection(folderName, tree.folders[folderName], 1);
426
388
  });
427
389
 
428
390
  return nav;
@@ -430,7 +392,7 @@ function buildNavigationStructure(files, currentFile, summaries = {}) {
430
392
  }
431
393
 
432
394
  // Process single markdown file
433
- async function processMarkdownFile(filePath, outputPath, allFiles, config, summaries = {}) {
395
+ async function processMarkdownFile(filePath, outputPath, allFiles, config) {
434
396
  const content = await fs.readFile(filePath, 'utf-8');
435
397
  const fileName = path.basename(filePath, '.md');
436
398
  const relativePath = path.relative(config.docsDir, filePath);
@@ -444,7 +406,7 @@ async function processMarkdownFile(filePath, outputPath, allFiles, config, summa
444
406
  const htmlContent = processMarkdownContent(content);
445
407
 
446
408
  // Build navigation
447
- const navigation = buildNavigationStructure(allFiles, urlPath, summaries);
409
+ const navigation = buildNavigationStructure(allFiles, urlPath);
448
410
 
449
411
  // Generate full HTML
450
412
  const html = generateHTML(title, htmlContent, navigation, urlPath, config);
@@ -512,21 +474,10 @@ async function buildDocumentation(config) {
512
474
  });
513
475
  }
514
476
 
515
- // First pass: collect summaries
516
- console.log(chalk.blue('📊 Extracting summaries...'));
517
- const summaries = {};
518
- for (const file of files) {
519
- const content = await fs.readFile(file.path, 'utf-8');
520
- const summary = extractSummary(content);
521
- if (summary) {
522
- summaries[file.urlPath] = summary;
523
- }
524
- }
525
-
526
477
  console.log(chalk.blue('📝 Processing files...'));
527
478
  for (const file of files) {
528
479
  const outputPath = path.join(outputDir, file.urlPath);
529
- await processMarkdownFile(file.path, outputPath, files, config, summaries);
480
+ await processMarkdownFile(file.path, outputPath, files, config);
530
481
  console.log(chalk.green(`✅ Generated: ${outputPath}`));
531
482
  }
532
483
 
@@ -873,6 +824,7 @@ async function createDefaultIndexPage(outputDir, config, version) {
873
824
  <title>Welcome to ${siteName}</title>
874
825
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
875
826
  <link rel="stylesheet" href="/css/notion-style.css">
827
+ <link rel="stylesheet" href="/css/style.css">
876
828
  <style>
877
829
  .welcome-container {
878
830
  max-width: 800px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowcode/doc-builder",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
5
5
  "main": "index.js",
6
6
  "bin": {