@knowcode/doc-builder 1.3.15 → 1.4.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,31 @@ 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.0] - 2025-07-20
9
+
10
+ ### Added
11
+ - **Tooltip functionality restored** - Added summary extraction from markdown files
12
+ - Tooltips now appear on hover for navigation items showing content preview
13
+ - `extractSummary()` function looks for Overview/Summary sections or first paragraph
14
+ - Summaries are collected in first pass and added as `data-tooltip` attributes
15
+
16
+ ### Fixed
17
+ - **CSS layout restored to match original** - Reverted all layout changes to original values
18
+ - Header height restored to 64px (was 56px)
19
+ - Main wrapper uses margin-top approach (not absolute positioning)
20
+ - Content uses original padding values (var(--space-6) var(--space-8))
21
+ - Content-inner uses centered layout (margin: 0 auto)
22
+ - Navigation padding restored to all sides
23
+
24
+ ### Removed
25
+ - Removed unused style.css file - only notion-style.css is used
26
+ - Eliminated dual CSS file confusion
27
+
28
+ ### Technical Details
29
+ - Summary extraction matches original build.js implementation
30
+ - Navigation generation passes summaries through recursive renderSection calls
31
+ - Tooltips use same CSS implementation as original with fixed positioning
32
+
8
33
  ## [1.3.14] - 2025-07-20
9
34
 
10
35
  ### Fixed
@@ -125,7 +125,7 @@
125
125
  /* Layout */
126
126
  --container-padding-mobile: 20px;
127
127
  --container-padding-desktop: 40px;
128
- --header-height: 56px;
128
+ --header-height: 64px;
129
129
  --breadcrumb-height: 40px;
130
130
  --sidebar-width: 280px;
131
131
  }
@@ -469,17 +469,15 @@ pre code {
469
469
 
470
470
  /* Main Wrapper */
471
471
  .main-wrapper {
472
- position: absolute;
473
- top: calc(var(--header-height) + var(--breadcrumb-height));
474
- left: 0;
475
- right: 0;
476
- bottom: 0;
477
472
  display: flex;
473
+ height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
474
+ margin-top: calc(var(--header-height) + var(--breadcrumb-height));
478
475
  overflow: hidden;
479
476
  }
480
477
 
481
478
  .main-wrapper.banner-visible {
482
- top: calc(var(--header-height) + var(--breadcrumb-height) + 3.5rem);
479
+ height: calc(100vh - var(--header-height) - var(--breadcrumb-height) - 3.5rem);
480
+ margin-top: calc(var(--header-height) + var(--breadcrumb-height) + 3.5rem);
483
481
  }
484
482
 
485
483
  /* Sidebar */
@@ -530,7 +528,7 @@ pre code {
530
528
  /* Navigation */
531
529
  .navigation {
532
530
  flex: 1;
533
- padding: 0 var(--space-2) var(--space-2) var(--space-2); /* No top padding */
531
+ padding: var(--space-2);
534
532
  overflow-y: auto;
535
533
  overflow-x: visible;
536
534
 
@@ -690,7 +688,7 @@ pre code {
690
688
  .content {
691
689
  flex: 1;
692
690
  margin-left: var(--sidebar-width);
693
- padding: var(--space-2) var(--space-8) var(--space-4); /* Reduced top padding */
691
+ padding: var(--space-6) var(--space-8);
694
692
  overflow-y: auto;
695
693
  background: var(--color-bg-default);
696
694
  transition: margin-left var(--duration-normal);
@@ -698,7 +696,7 @@ pre code {
698
696
 
699
697
  .content-inner {
700
698
  max-width: 65rem;
701
- margin: 0; /* Left aligned instead of centered */
699
+ margin: 0 auto;
702
700
  }
703
701
 
704
702
  /* Tables */
@@ -1032,7 +1030,7 @@ tr:hover {
1032
1030
 
1033
1031
  .content {
1034
1032
  margin-left: 0 !important;
1035
- padding: var(--space-2) var(--space-4) var(--space-4); /* Reduced top padding */
1033
+ padding: var(--space-6) var(--space-4);
1036
1034
  max-width: 100%;
1037
1035
  }
1038
1036
 
@@ -1660,9 +1658,8 @@ tr:hover {
1660
1658
  }
1661
1659
 
1662
1660
  .main-wrapper {
1663
- position: relative; /* Change back to relative for mobile */
1664
- top: 0;
1665
1661
  flex-direction: column;
1662
+ height: auto;
1666
1663
  min-height: calc(100vh - var(--header-height) - var(--breadcrumb-height));
1667
1664
  }
1668
1665
 
@@ -1675,7 +1672,7 @@ tr:hover {
1675
1672
 
1676
1673
  .content {
1677
1674
  margin-left: 0 !important;
1678
- padding: var(--space-2) var(--space-4) var(--space-4); /* Reduced top padding */
1675
+ padding: var(--space-6) var(--space-4);
1679
1676
  max-width: 100%;
1680
1677
  }
1681
1678
 
Binary file
@@ -24,6 +24,44 @@ 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
+
27
65
  // Process markdown content
28
66
  function processMarkdownContent(content) {
29
67
  // Convert mermaid code blocks to mermaid divs with titles
@@ -194,7 +232,7 @@ const folderDescriptions = {
194
232
  };
195
233
 
196
234
  // Build navigation structure with rich functionality
197
- function buildNavigationStructure(files, currentFile) {
235
+ function buildNavigationStructure(files, currentFile, summaries = {}) {
198
236
  const tree = { files: [], folders: {} };
199
237
 
200
238
  files.forEach(file => {
@@ -241,7 +279,7 @@ function buildNavigationStructure(files, currentFile) {
241
279
  };
242
280
 
243
281
  // Helper function to render a section
244
- const renderSection = (folderName, folderData, level = 0, parentPath = '') => {
282
+ const renderSection = (folderName, folderData, level = 0, parentPath = '', summaries = {}) => {
245
283
  const icons = {
246
284
  'root': 'fas fa-home',
247
285
  'product-roadmap': 'fas fa-road',
@@ -318,8 +356,12 @@ function buildNavigationStructure(files, currentFile) {
318
356
 
319
357
  const linkPath = '/' + file.urlPath;
320
358
 
359
+ // Get summary for tooltip
360
+ const summary = summaries[file.urlPath] || '';
361
+ const tooltipAttr = summary ? `data-tooltip="${escapeHtml(summary)}"` : '';
362
+
321
363
  html += `
322
- <a href="${linkPath}" class="nav-item${isActive}"><i class="fas fa-file-alt"></i> ${title}</a>`;
364
+ <a href="${linkPath}" class="nav-item${isActive}" ${tooltipAttr}><i class="fas fa-file-alt"></i> ${title}</a>`;
323
365
  });
324
366
 
325
367
  html += `</div></div>`;
@@ -330,7 +372,7 @@ function buildNavigationStructure(files, currentFile) {
330
372
  .forEach(subFolder => {
331
373
  // Build the path for the subfolder including current folder
332
374
  const currentPath = parentPath ? `${parentPath}-${folderName}` : folderName;
333
- html += renderSection(subFolder, folderData.folders[subFolder], level + 1, currentPath);
375
+ html += renderSection(subFolder, folderData.folders[subFolder], level + 1, currentPath, summaries);
334
376
  });
335
377
 
336
378
  return html;
@@ -341,14 +383,14 @@ function buildNavigationStructure(files, currentFile) {
341
383
 
342
384
  if (!hasFolders) {
343
385
  // Generate simple flat navigation for all files in root
344
- return renderSection('root', { files: tree.files, folders: {} }, 0);
386
+ return renderSection('root', { files: tree.files, folders: {} }, 0, '', summaries);
345
387
  } else {
346
388
  // Generate hierarchical navigation
347
389
  let nav = '';
348
390
 
349
391
  // Render root files first
350
392
  if (tree.files.length > 0) {
351
- nav += renderSection('root', { files: tree.files, folders: {} }, 0);
393
+ nav += renderSection('root', { files: tree.files, folders: {} }, 0, '', summaries);
352
394
  }
353
395
 
354
396
  // Add other top-level folders in logical order
@@ -371,7 +413,7 @@ function buildNavigationStructure(files, currentFile) {
371
413
 
372
414
  folderOrder.forEach(folderName => {
373
415
  if (tree.folders[folderName]) {
374
- nav += renderSection(folderName, tree.folders[folderName], 1);
416
+ nav += renderSection(folderName, tree.folders[folderName], 1, '', summaries);
375
417
  delete tree.folders[folderName]; // Remove so we don't render it again
376
418
  }
377
419
  });
@@ -380,7 +422,7 @@ function buildNavigationStructure(files, currentFile) {
380
422
  Object.keys(tree.folders)
381
423
  .sort()
382
424
  .forEach(folderName => {
383
- nav += renderSection(folderName, tree.folders[folderName], 1);
425
+ nav += renderSection(folderName, tree.folders[folderName], 1, '', summaries);
384
426
  });
385
427
 
386
428
  return nav;
@@ -388,7 +430,7 @@ function buildNavigationStructure(files, currentFile) {
388
430
  }
389
431
 
390
432
  // Process single markdown file
391
- async function processMarkdownFile(filePath, outputPath, allFiles, config) {
433
+ async function processMarkdownFile(filePath, outputPath, allFiles, config, summaries = {}) {
392
434
  const content = await fs.readFile(filePath, 'utf-8');
393
435
  const fileName = path.basename(filePath, '.md');
394
436
  const relativePath = path.relative(config.docsDir, filePath);
@@ -402,7 +444,7 @@ async function processMarkdownFile(filePath, outputPath, allFiles, config) {
402
444
  const htmlContent = processMarkdownContent(content);
403
445
 
404
446
  // Build navigation
405
- const navigation = buildNavigationStructure(allFiles, urlPath);
447
+ const navigation = buildNavigationStructure(allFiles, urlPath, summaries);
406
448
 
407
449
  // Generate full HTML
408
450
  const html = generateHTML(title, htmlContent, navigation, urlPath, config);
@@ -470,10 +512,21 @@ async function buildDocumentation(config) {
470
512
  });
471
513
  }
472
514
 
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
+
473
526
  console.log(chalk.blue('📝 Processing files...'));
474
527
  for (const file of files) {
475
528
  const outputPath = path.join(outputDir, file.urlPath);
476
- await processMarkdownFile(file.path, outputPath, files, config);
529
+ await processMarkdownFile(file.path, outputPath, files, config, summaries);
477
530
  console.log(chalk.green(`✅ Generated: ${outputPath}`));
478
531
  }
479
532
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knowcode/doc-builder",
3
- "version": "1.3.15",
3
+ "version": "1.4.0",
4
4
  "description": "Reusable documentation builder for markdown-based sites with Vercel deployment support",
5
5
  "main": "index.js",
6
6
  "bin": {