boltdocs 1.6.0 → 1.7.1

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.
Files changed (57) hide show
  1. package/dist/{SearchDialog-J3KNRGNO.mjs → SearchDialog-6Z7CUAYJ.mjs} +8 -1
  2. package/dist/{SearchDialog-3QICRMWF.css → SearchDialog-GOZ6X53X.css} +385 -113
  3. package/dist/{chunk-HSPDIRTW.mjs → chunk-SFVOGJ2W.mjs} +955 -737
  4. package/dist/client/index.css +385 -113
  5. package/dist/client/index.d.mts +19 -7
  6. package/dist/client/index.d.ts +19 -7
  7. package/dist/client/index.js +964 -577
  8. package/dist/client/index.mjs +118 -1
  9. package/dist/client/ssr.css +385 -113
  10. package/dist/client/ssr.d.mts +3 -1
  11. package/dist/client/ssr.d.ts +3 -1
  12. package/dist/client/ssr.js +743 -474
  13. package/dist/client/ssr.mjs +3 -2
  14. package/dist/{config-DkZg5aCf.d.ts → config-D68h41CA.d.mts} +21 -2
  15. package/dist/{config-DkZg5aCf.d.mts → config-D68h41CA.d.ts} +21 -2
  16. package/dist/node/index.d.mts +12 -2
  17. package/dist/node/index.d.ts +12 -2
  18. package/dist/node/index.js +48 -21
  19. package/dist/node/index.mjs +48 -21
  20. package/dist/{types-DGIo1VKD.d.mts → types-BbceAHA0.d.mts} +15 -0
  21. package/dist/{types-DGIo1VKD.d.ts → types-BbceAHA0.d.ts} +15 -0
  22. package/package.json +1 -1
  23. package/src/client/app/index.tsx +16 -11
  24. package/src/client/index.ts +2 -0
  25. package/src/client/ssr.tsx +4 -1
  26. package/src/client/theme/components/mdx/Table.tsx +151 -0
  27. package/src/client/theme/components/mdx/index.ts +3 -0
  28. package/src/client/theme/components/mdx/mdx-components.css +128 -0
  29. package/src/client/theme/styles/markdown.css +8 -3
  30. package/src/client/theme/styles/variables.css +34 -9
  31. package/src/client/theme/ui/ErrorBoundary/ErrorBoundary.tsx +46 -0
  32. package/src/client/theme/ui/ErrorBoundary/index.ts +1 -0
  33. package/src/client/theme/ui/Layout/Layout.tsx +10 -11
  34. package/src/client/theme/ui/Layout/base.css +15 -3
  35. package/src/client/theme/ui/Link/Link.tsx +2 -2
  36. package/src/client/theme/ui/Link/LinkPreview.tsx +9 -14
  37. package/src/client/theme/ui/Link/link-preview.css +30 -27
  38. package/src/client/theme/ui/Navbar/Navbar.tsx +65 -17
  39. package/src/client/theme/ui/Navbar/Tabs.tsx +99 -0
  40. package/src/client/theme/ui/Navbar/navbar.css +119 -5
  41. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +66 -57
  42. package/src/client/theme/ui/OnThisPage/toc.css +30 -10
  43. package/src/client/theme/ui/ProgressBar/ProgressBar.css +17 -0
  44. package/src/client/theme/ui/ProgressBar/ProgressBar.tsx +51 -0
  45. package/src/client/theme/ui/ProgressBar/index.ts +1 -0
  46. package/src/client/theme/ui/SearchDialog/SearchDialog.tsx +11 -1
  47. package/src/client/theme/ui/Sidebar/Sidebar.tsx +97 -57
  48. package/src/client/theme/ui/Sidebar/sidebar.css +61 -67
  49. package/src/client/types.ts +12 -0
  50. package/src/node/config.ts +19 -1
  51. package/src/node/plugin/entry.ts +5 -1
  52. package/src/node/plugin/index.ts +2 -1
  53. package/src/node/routes/index.ts +13 -1
  54. package/src/node/routes/parser.ts +32 -7
  55. package/src/node/routes/types.ts +11 -1
  56. package/src/node/ssg/index.ts +2 -1
  57. package/src/node/ssg/options.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AppShell
3
- } from "../chunk-HSPDIRTW.mjs";
3
+ } from "../chunk-SFVOGJ2W.mjs";
4
4
  import "../chunk-FMTOYQLO.mjs";
5
5
 
6
6
  // src/client/ssr.tsx
@@ -9,7 +9,7 @@ import ReactDOMServer from "react-dom/server";
9
9
  import { StaticRouter } from "react-router-dom/server";
10
10
  import { jsx } from "react/jsx-runtime";
11
11
  async function render(options) {
12
- const { path, routes, config, modules, homePage } = options;
12
+ const { path, routes, config, modules, homePage, docsDirName } = options;
13
13
  const resolvedModules = {};
14
14
  for (const [key, mod] of Object.entries(modules)) {
15
15
  resolvedModules[key] = () => Promise.resolve(mod);
@@ -20,6 +20,7 @@ async function render(options) {
20
20
  {
21
21
  initialRoutes: routes,
22
22
  initialConfig: config,
23
+ docsDirName,
23
24
  modules: resolvedModules,
24
25
  homePage
25
26
  }
@@ -28,8 +28,18 @@ interface BoltdocsThemeConfig {
28
28
  logo?: string;
29
29
  /** Items to display in the top navigation bar */
30
30
  navbar?: Array<{
31
- text: string;
32
- link: string;
31
+ /** Text to display (alias for text) */
32
+ label?: string;
33
+ /** Text to display */
34
+ text?: string;
35
+ /** URL path or external link (alias for link) */
36
+ to?: string;
37
+ /** URL path or external link (alias for link) */
38
+ href?: string;
39
+ /** URL path or external link */
40
+ link?: string;
41
+ /** Alignment of the item in the navbar */
42
+ position?: "left" | "right";
33
43
  }>;
34
44
  /** Items to display in the sidebar, organized optionally by group URLs */
35
45
  sidebar?: Record<string, Array<{
@@ -67,6 +77,15 @@ interface BoltdocsThemeConfig {
67
77
  className?: string;
68
78
  style?: any;
69
79
  };
80
+ /**
81
+ * Top-level tabs for organizing documentation groups.
82
+ * Tab discovery uses the (tab-id) directory syntax.
83
+ */
84
+ tabs?: Array<{
85
+ id: string;
86
+ text: string;
87
+ icon?: string;
88
+ }>;
70
89
  /**
71
90
  * The syntax highlighting theme for code blocks.
72
91
  * Supports any Shiki theme name (e.g., 'github-dark', 'one-dark-pro', 'aurora-x').
@@ -28,8 +28,18 @@ interface BoltdocsThemeConfig {
28
28
  logo?: string;
29
29
  /** Items to display in the top navigation bar */
30
30
  navbar?: Array<{
31
- text: string;
32
- link: string;
31
+ /** Text to display (alias for text) */
32
+ label?: string;
33
+ /** Text to display */
34
+ text?: string;
35
+ /** URL path or external link (alias for link) */
36
+ to?: string;
37
+ /** URL path or external link (alias for link) */
38
+ href?: string;
39
+ /** URL path or external link */
40
+ link?: string;
41
+ /** Alignment of the item in the navbar */
42
+ position?: "left" | "right";
33
43
  }>;
34
44
  /** Items to display in the sidebar, organized optionally by group URLs */
35
45
  sidebar?: Record<string, Array<{
@@ -67,6 +77,15 @@ interface BoltdocsThemeConfig {
67
77
  className?: string;
68
78
  style?: any;
69
79
  };
80
+ /**
81
+ * Top-level tabs for organizing documentation groups.
82
+ * Tab discovery uses the (tab-id) directory syntax.
83
+ */
84
+ tabs?: Array<{
85
+ id: string;
86
+ text: string;
87
+ icon?: string;
88
+ }>;
70
89
  /**
71
90
  * The syntax highlighting theme for code blocks.
72
91
  * Supports any Shiki theme name (e.g., 'github-dark', 'one-dark-pro', 'aurora-x').
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from 'vite';
2
- import { B as BoltdocsConfig } from '../config-DkZg5aCf.mjs';
3
- export { a as BoltdocsThemeConfig } from '../config-DkZg5aCf.mjs';
2
+ import { B as BoltdocsConfig } from '../config-D68h41CA.mjs';
3
+ export { a as BoltdocsThemeConfig } from '../config-D68h41CA.mjs';
4
4
 
5
5
  /**
6
6
  * Configuration options specifically for the Boltdocs Vite plugin.
@@ -20,6 +20,8 @@ interface BoltdocsPluginOptions {
20
20
  interface SSGOptions {
21
21
  /** The root directory containing markdown documentation files */
22
22
  docsDir: string;
23
+ /** The name of the documentation directory (e.g. 'docs') */
24
+ docsDirName: string;
23
25
  /** The output directory where Vite placed the compiled `index.html` and assets */
24
26
  outDir: string;
25
27
  /** Pre-resolved config (avoids re-resolving during the SSG phase) */
@@ -57,6 +59,8 @@ interface RouteMeta {
57
59
  groupTitle?: string;
58
60
  /** Optional explicit position for ordering the group itself */
59
61
  groupPosition?: number;
62
+ /** Optional icon for the route's group */
63
+ groupIcon?: string;
60
64
  /** Extracted markdown headings for search indexing */
61
65
  headings?: {
62
66
  level: number;
@@ -72,6 +76,12 @@ interface RouteMeta {
72
76
  text: string;
73
77
  expires?: string;
74
78
  };
79
+ /** Optional icon to display (Lucide icon name or raw SVG) */
80
+ icon?: string;
81
+ /** The tab this route belongs to, if tabs are configured */
82
+ tab?: string;
83
+ /** The extracted plain-text content of the page for search indexing */
84
+ _content?: string;
75
85
  }
76
86
 
77
87
  declare function boltdocs(options?: BoltdocsPluginOptions): Promise<Plugin[]>;
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from 'vite';
2
- import { B as BoltdocsConfig } from '../config-DkZg5aCf.js';
3
- export { a as BoltdocsThemeConfig } from '../config-DkZg5aCf.js';
2
+ import { B as BoltdocsConfig } from '../config-D68h41CA.js';
3
+ export { a as BoltdocsThemeConfig } from '../config-D68h41CA.js';
4
4
 
5
5
  /**
6
6
  * Configuration options specifically for the Boltdocs Vite plugin.
@@ -20,6 +20,8 @@ interface BoltdocsPluginOptions {
20
20
  interface SSGOptions {
21
21
  /** The root directory containing markdown documentation files */
22
22
  docsDir: string;
23
+ /** The name of the documentation directory (e.g. 'docs') */
24
+ docsDirName: string;
23
25
  /** The output directory where Vite placed the compiled `index.html` and assets */
24
26
  outDir: string;
25
27
  /** Pre-resolved config (avoids re-resolving during the SSG phase) */
@@ -57,6 +59,8 @@ interface RouteMeta {
57
59
  groupTitle?: string;
58
60
  /** Optional explicit position for ordering the group itself */
59
61
  groupPosition?: number;
62
+ /** Optional icon for the route's group */
63
+ groupIcon?: string;
60
64
  /** Extracted markdown headings for search indexing */
61
65
  headings?: {
62
66
  level: number;
@@ -72,6 +76,12 @@ interface RouteMeta {
72
76
  text: string;
73
77
  expires?: string;
74
78
  };
79
+ /** Optional icon to display (Lucide icon name or raw SVG) */
80
+ icon?: string;
81
+ /** The tab this route belongs to, if tabs are configured */
82
+ tab?: string;
83
+ /** The extracted plain-text content of the page for search indexing */
84
+ _content?: string;
75
85
  }
76
86
 
77
87
  declare function boltdocs(options?: BoltdocsPluginOptions): Promise<Plugin[]>;
@@ -478,6 +478,14 @@ function parseDocFile(file, docsDir, basePath, config) {
478
478
  parts = parts.slice(1);
479
479
  }
480
480
  }
481
+ let inferredTab;
482
+ if (parts.length > 0) {
483
+ const tabMatch = parts[0].match(/^\((.+)\)$/);
484
+ if (tabMatch) {
485
+ inferredTab = tabMatch[1].toLowerCase();
486
+ parts = parts.slice(1);
487
+ }
488
+ }
481
489
  const cleanRelativePath = parts.join("/");
482
490
  let cleanRoutePath;
483
491
  if (data.permalink) {
@@ -511,15 +519,17 @@ function parseDocFile(file, docsDir, basePath, config) {
511
519
  const level = match[1].length;
512
520
  const text = match[2].replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").trim();
513
521
  const id = slugger.slug(text);
514
- headings.push({ level, text: escapeHtml(text), id });
522
+ headings.push({ level, text, id });
515
523
  }
516
- const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
517
- let sanitizedDescription = data.description ? escapeHtml(data.description) : "";
524
+ const sanitizedTitle = data.title ? data.title : inferredTitle;
525
+ let sanitizedDescription = data.description ? data.description : "";
518
526
  if (!sanitizedDescription && content) {
519
527
  const summary = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim().slice(0, 160);
520
- sanitizedDescription = escapeHtml(summary);
528
+ sanitizedDescription = summary;
521
529
  }
522
- const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
530
+ const sanitizedBadge = data.badge ? data.badge : void 0;
531
+ const icon = data.icon ? String(data.icon) : void 0;
532
+ const plainText = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/<[^>]+>/g, "").replace(/\{[^\}]+\}/g, "").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim();
523
533
  return {
524
534
  route: {
525
535
  path: finalPath,
@@ -531,15 +541,18 @@ function parseDocFile(file, docsDir, basePath, config) {
531
541
  headings,
532
542
  locale,
533
543
  version,
534
- badge: sanitizedBadge
544
+ badge: sanitizedBadge,
545
+ icon,
546
+ tab: inferredTab,
547
+ _content: plainText
535
548
  },
536
549
  relativeDir: cleanDirName,
537
550
  isGroupIndex,
551
+ inferredTab,
538
552
  groupMeta: isGroupIndex ? {
539
- title: escapeHtml(
540
- data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : "")
541
- ),
542
- position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0)
553
+ title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
554
+ position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0),
555
+ icon
543
556
  } : void 0,
544
557
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
545
558
  };
@@ -575,6 +588,7 @@ function compareByGroupPosition(a, b) {
575
588
  // src/node/routes/index.ts
576
589
  async function generateRoutes(docsDir, config, basePath = "/docs") {
577
590
  docCache.load();
591
+ docCache.invalidateAll();
578
592
  const files = await (0, import_fast_glob.default)(["**/*.md", "**/*.mdx"], {
579
593
  cwd: docsDir,
580
594
  absolute: true
@@ -608,13 +622,17 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
608
622
  if (!groupMeta.has(p.relativeDir)) {
609
623
  groupMeta.set(p.relativeDir, {
610
624
  title: capitalize(p.relativeDir),
611
- position: p.inferredGroupPosition
625
+ position: p.inferredGroupPosition,
626
+ icon: p.route.icon
612
627
  });
613
628
  } else {
614
629
  const entry = groupMeta.get(p.relativeDir);
615
630
  if (entry.position === void 0 && p.inferredGroupPosition !== void 0) {
616
631
  entry.position = p.inferredGroupPosition;
617
632
  }
633
+ if (!entry.icon && p.route.icon) {
634
+ entry.icon = p.route.icon;
635
+ }
618
636
  }
619
637
  }
620
638
  if (p.isGroupIndex && p.relativeDir && p.groupMeta) {
@@ -623,6 +641,9 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
623
641
  if (p.groupMeta.position !== void 0) {
624
642
  entry.position = p.groupMeta.position;
625
643
  }
644
+ if (p.groupMeta.icon) {
645
+ entry.icon = p.groupMeta.icon;
646
+ }
626
647
  }
627
648
  }
628
649
  const routes = parsed.map((p) => {
@@ -632,7 +653,8 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
632
653
  ...p.route,
633
654
  group: dir,
634
655
  groupTitle: meta?.title || (dir ? capitalize(dir) : void 0),
635
- groupPosition: meta?.position
656
+ groupPosition: meta?.position,
657
+ groupIcon: meta?.icon
636
658
  };
637
659
  });
638
660
  if (config?.i18n) {
@@ -797,7 +819,7 @@ var _filename = (0, import_url2.fileURLToPath)(import_meta2.url);
797
819
  var _dirname = import_path4.default.dirname(_filename);
798
820
  var _require = (0, import_module.createRequire)(import_meta2.url);
799
821
  async function generateStaticPages(options) {
800
- const { docsDir, outDir, config } = options;
822
+ const { docsDir, docsDirName, outDir, config } = options;
801
823
  const routes = await generateRoutes(docsDir, config);
802
824
  const siteTitle = config?.themeConfig?.title || "Boltdocs";
803
825
  const siteDescription = config?.themeConfig?.description || "";
@@ -829,6 +851,7 @@ async function generateStaticPages(options) {
829
851
  path: route.path,
830
852
  routes,
831
853
  config: config || {},
854
+ docsDirName,
832
855
  modules: fakeModules,
833
856
  homePage: void 0
834
857
  // No custom home page for now
@@ -863,10 +886,11 @@ async function generateStaticPages(options) {
863
886
 
864
887
  // src/node/plugin/index.ts
865
888
  init_utils();
866
- var import_path5 = __toESM(require("path"));
889
+ var import_path6 = __toESM(require("path"));
867
890
 
868
891
  // src/node/plugin/entry.ts
869
892
  init_utils();
893
+ var import_path5 = __toESM(require("path"));
870
894
  function generateEntryCode(options, config) {
871
895
  const homeImport = options.homePage ? `import HomePage from '${normalizePath(options.homePage)}';` : "";
872
896
  const homeOption = options.homePage ? "homePage: HomePage," : "";
@@ -875,11 +899,12 @@ function generateEntryCode(options, config) {
875
899
  const componentImports = pluginComponents.map(
876
900
  ([
877
901
  name,
878
- path6
879
- ]) => `import * as _comp_${name} from '${normalizePath(path6)}';
902
+ path7
903
+ ]) => `import * as _comp_${name} from '${normalizePath(path7)}';
880
904
  const ${name} = _comp_${name}.default || _comp_${name}['${name}'] || _comp_${name};`
881
905
  ).join("\n");
882
906
  const componentMap = pluginComponents.map(([name]) => name).join(", ");
907
+ const docsDirName = import_path5.default.basename(options.docsDir || "docs");
883
908
  return `
884
909
  import { createBoltdocsApp as _createApp } from 'boltdocs/client';
885
910
  import 'boltdocs/style.css';
@@ -892,8 +917,9 @@ ${componentImports}
892
917
  _createApp({
893
918
  target: '#root',
894
919
  routes: _routes,
920
+ docsDirName: '${docsDirName}',
895
921
  config: _config,
896
- modules: import.meta.glob('/docs/**/*.{md,mdx}'),
922
+ modules: import.meta.glob('/${docsDirName}/**/*.{md,mdx}'),
897
923
  hot: import.meta.hot,
898
924
  ${homeOption}
899
925
  components: { ${componentMap} },
@@ -946,7 +972,7 @@ ${themeScript} </head>`);
946
972
 
947
973
  // src/node/plugin/index.ts
948
974
  function boltdocsPlugin(options = {}, passedConfig) {
949
- const docsDir = import_path5.default.resolve(process.cwd(), options.docsDir || "docs");
975
+ const docsDir = import_path6.default.resolve(process.cwd(), options.docsDir || "docs");
950
976
  const normalizedDocsDir = normalizePath(docsDir);
951
977
  let config = passedConfig;
952
978
  let viteConfig;
@@ -976,7 +1002,7 @@ function boltdocsPlugin(options = {}, passedConfig) {
976
1002
  },
977
1003
  configureServer(server) {
978
1004
  const configPaths = CONFIG_FILES.map(
979
- (c) => import_path5.default.resolve(process.cwd(), c)
1005
+ (c) => import_path6.default.resolve(process.cwd(), c)
980
1006
  );
981
1007
  server.watcher.add(configPaths);
982
1008
  const handleFileEvent = async (file, type) => {
@@ -1039,8 +1065,9 @@ function boltdocsPlugin(options = {}, passedConfig) {
1039
1065
  },
1040
1066
  async closeBundle() {
1041
1067
  if (!isBuild) return;
1042
- const outDir = viteConfig?.build?.outDir ? import_path5.default.resolve(viteConfig.root, viteConfig.build.outDir) : import_path5.default.resolve(process.cwd(), "dist");
1043
- await generateStaticPages({ docsDir, outDir, config });
1068
+ const outDir = viteConfig?.build?.outDir ? import_path6.default.resolve(viteConfig.root, viteConfig.build.outDir) : import_path6.default.resolve(process.cwd(), "dist");
1069
+ const docsDirName = import_path6.default.basename(docsDir || "docs");
1070
+ await generateStaticPages({ docsDir, docsDirName, outDir, config });
1044
1071
  const { flushCache: flushCache2 } = await Promise.resolve().then(() => (init_cache(), cache_exports));
1045
1072
  await flushCache2();
1046
1073
  }
@@ -60,6 +60,14 @@ function parseDocFile(file, docsDir, basePath, config) {
60
60
  parts = parts.slice(1);
61
61
  }
62
62
  }
63
+ let inferredTab;
64
+ if (parts.length > 0) {
65
+ const tabMatch = parts[0].match(/^\((.+)\)$/);
66
+ if (tabMatch) {
67
+ inferredTab = tabMatch[1].toLowerCase();
68
+ parts = parts.slice(1);
69
+ }
70
+ }
63
71
  const cleanRelativePath = parts.join("/");
64
72
  let cleanRoutePath;
65
73
  if (data.permalink) {
@@ -93,15 +101,17 @@ function parseDocFile(file, docsDir, basePath, config) {
93
101
  const level = match[1].length;
94
102
  const text = match[2].replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").trim();
95
103
  const id = slugger.slug(text);
96
- headings.push({ level, text: escapeHtml(text), id });
104
+ headings.push({ level, text, id });
97
105
  }
98
- const sanitizedTitle = data.title ? escapeHtml(data.title) : inferredTitle;
99
- let sanitizedDescription = data.description ? escapeHtml(data.description) : "";
106
+ const sanitizedTitle = data.title ? data.title : inferredTitle;
107
+ let sanitizedDescription = data.description ? data.description : "";
100
108
  if (!sanitizedDescription && content) {
101
109
  const summary = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim().slice(0, 160);
102
- sanitizedDescription = escapeHtml(summary);
110
+ sanitizedDescription = summary;
103
111
  }
104
- const sanitizedBadge = data.badge ? escapeHtml(data.badge) : void 0;
112
+ const sanitizedBadge = data.badge ? data.badge : void 0;
113
+ const icon = data.icon ? String(data.icon) : void 0;
114
+ const plainText = content.replace(/^#+.*$/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1").replace(/<[^>]+>/g, "").replace(/\{[^\}]+\}/g, "").replace(/[_*`]/g, "").replace(/\n+/g, " ").trim();
105
115
  return {
106
116
  route: {
107
117
  path: finalPath,
@@ -113,15 +123,18 @@ function parseDocFile(file, docsDir, basePath, config) {
113
123
  headings,
114
124
  locale,
115
125
  version,
116
- badge: sanitizedBadge
126
+ badge: sanitizedBadge,
127
+ icon,
128
+ tab: inferredTab,
129
+ _content: plainText
117
130
  },
118
131
  relativeDir: cleanDirName,
119
132
  isGroupIndex,
133
+ inferredTab,
120
134
  groupMeta: isGroupIndex ? {
121
- title: escapeHtml(
122
- data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : "")
123
- ),
124
- position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0)
135
+ title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
136
+ position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0),
137
+ icon
125
138
  } : void 0,
126
139
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
127
140
  };
@@ -157,6 +170,7 @@ function compareByGroupPosition(a, b) {
157
170
  // src/node/routes/index.ts
158
171
  async function generateRoutes(docsDir, config, basePath = "/docs") {
159
172
  docCache.load();
173
+ docCache.invalidateAll();
160
174
  const files = await fastGlob(["**/*.md", "**/*.mdx"], {
161
175
  cwd: docsDir,
162
176
  absolute: true
@@ -190,13 +204,17 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
190
204
  if (!groupMeta.has(p.relativeDir)) {
191
205
  groupMeta.set(p.relativeDir, {
192
206
  title: capitalize(p.relativeDir),
193
- position: p.inferredGroupPosition
207
+ position: p.inferredGroupPosition,
208
+ icon: p.route.icon
194
209
  });
195
210
  } else {
196
211
  const entry = groupMeta.get(p.relativeDir);
197
212
  if (entry.position === void 0 && p.inferredGroupPosition !== void 0) {
198
213
  entry.position = p.inferredGroupPosition;
199
214
  }
215
+ if (!entry.icon && p.route.icon) {
216
+ entry.icon = p.route.icon;
217
+ }
200
218
  }
201
219
  }
202
220
  if (p.isGroupIndex && p.relativeDir && p.groupMeta) {
@@ -205,6 +223,9 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
205
223
  if (p.groupMeta.position !== void 0) {
206
224
  entry.position = p.groupMeta.position;
207
225
  }
226
+ if (p.groupMeta.icon) {
227
+ entry.icon = p.groupMeta.icon;
228
+ }
208
229
  }
209
230
  }
210
231
  const routes = parsed.map((p) => {
@@ -214,7 +235,8 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
214
235
  ...p.route,
215
236
  group: dir,
216
237
  groupTitle: meta?.title || (dir ? capitalize(dir) : void 0),
217
- groupPosition: meta?.position
238
+ groupPosition: meta?.position,
239
+ groupIcon: meta?.icon
218
240
  };
219
241
  });
220
242
  if (config?.i18n) {
@@ -375,7 +397,7 @@ var _filename = fileURLToPath(import.meta.url);
375
397
  var _dirname = path3.dirname(_filename);
376
398
  var _require = createRequire(import.meta.url);
377
399
  async function generateStaticPages(options) {
378
- const { docsDir, outDir, config } = options;
400
+ const { docsDir, docsDirName, outDir, config } = options;
379
401
  const routes = await generateRoutes(docsDir, config);
380
402
  const siteTitle = config?.themeConfig?.title || "Boltdocs";
381
403
  const siteDescription = config?.themeConfig?.description || "";
@@ -407,6 +429,7 @@ async function generateStaticPages(options) {
407
429
  path: route.path,
408
430
  routes,
409
431
  config: config || {},
432
+ docsDirName,
410
433
  modules: fakeModules,
411
434
  homePage: void 0
412
435
  // No custom home page for now
@@ -440,9 +463,10 @@ async function generateStaticPages(options) {
440
463
  }
441
464
 
442
465
  // src/node/plugin/index.ts
443
- import path4 from "path";
466
+ import path5 from "path";
444
467
 
445
468
  // src/node/plugin/entry.ts
469
+ import path4 from "path";
446
470
  function generateEntryCode(options, config) {
447
471
  const homeImport = options.homePage ? `import HomePage from '${normalizePath(options.homePage)}';` : "";
448
472
  const homeOption = options.homePage ? "homePage: HomePage," : "";
@@ -451,11 +475,12 @@ function generateEntryCode(options, config) {
451
475
  const componentImports = pluginComponents.map(
452
476
  ([
453
477
  name,
454
- path5
455
- ]) => `import * as _comp_${name} from '${normalizePath(path5)}';
478
+ path6
479
+ ]) => `import * as _comp_${name} from '${normalizePath(path6)}';
456
480
  const ${name} = _comp_${name}.default || _comp_${name}['${name}'] || _comp_${name};`
457
481
  ).join("\n");
458
482
  const componentMap = pluginComponents.map(([name]) => name).join(", ");
483
+ const docsDirName = path4.basename(options.docsDir || "docs");
459
484
  return `
460
485
  import { createBoltdocsApp as _createApp } from 'boltdocs/client';
461
486
  import 'boltdocs/style.css';
@@ -468,8 +493,9 @@ ${componentImports}
468
493
  _createApp({
469
494
  target: '#root',
470
495
  routes: _routes,
496
+ docsDirName: '${docsDirName}',
471
497
  config: _config,
472
- modules: import.meta.glob('/docs/**/*.{md,mdx}'),
498
+ modules: import.meta.glob('/${docsDirName}/**/*.{md,mdx}'),
473
499
  hot: import.meta.hot,
474
500
  ${homeOption}
475
501
  components: { ${componentMap} },
@@ -522,7 +548,7 @@ ${themeScript} </head>`);
522
548
 
523
549
  // src/node/plugin/index.ts
524
550
  function boltdocsPlugin(options = {}, passedConfig) {
525
- const docsDir = path4.resolve(process.cwd(), options.docsDir || "docs");
551
+ const docsDir = path5.resolve(process.cwd(), options.docsDir || "docs");
526
552
  const normalizedDocsDir = normalizePath(docsDir);
527
553
  let config = passedConfig;
528
554
  let viteConfig;
@@ -552,7 +578,7 @@ function boltdocsPlugin(options = {}, passedConfig) {
552
578
  },
553
579
  configureServer(server) {
554
580
  const configPaths = CONFIG_FILES.map(
555
- (c) => path4.resolve(process.cwd(), c)
581
+ (c) => path5.resolve(process.cwd(), c)
556
582
  );
557
583
  server.watcher.add(configPaths);
558
584
  const handleFileEvent = async (file, type) => {
@@ -615,8 +641,9 @@ function boltdocsPlugin(options = {}, passedConfig) {
615
641
  },
616
642
  async closeBundle() {
617
643
  if (!isBuild) return;
618
- const outDir = viteConfig?.build?.outDir ? path4.resolve(viteConfig.root, viteConfig.build.outDir) : path4.resolve(process.cwd(), "dist");
619
- await generateStaticPages({ docsDir, outDir, config });
644
+ const outDir = viteConfig?.build?.outDir ? path5.resolve(viteConfig.root, viteConfig.build.outDir) : path5.resolve(process.cwd(), "dist");
645
+ const docsDirName = path5.basename(docsDir || "docs");
646
+ await generateStaticPages({ docsDir, docsDirName, outDir, config });
620
647
  const { flushCache } = await import("../cache-KNL5B4EE.mjs");
621
648
  await flushCache();
622
649
  }
@@ -33,6 +33,19 @@ interface ComponentRoute {
33
33
  locale?: string;
34
34
  /** The version this route belongs to, if versioning is configured */
35
35
  version?: string;
36
+ /** Optional icon to display (Lucide icon name or raw SVG) */
37
+ icon?: string;
38
+ /** The tab this route belongs to, if tabs are configured */
39
+ tab?: string;
40
+ /** Optional badge to display next to the sidebar item */
41
+ badge?: string | {
42
+ text: string;
43
+ expires?: string;
44
+ };
45
+ /** Optional icon for the route's group */
46
+ groupIcon?: string;
47
+ /** The extracted plain-text content of the page for search indexing */
48
+ _content?: string;
36
49
  }
37
50
  /**
38
51
  * Configuration options for initializing the Boltdocs client app.
@@ -42,6 +55,8 @@ interface CreateBoltdocsAppOptions {
42
55
  target: string;
43
56
  /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
44
57
  routes: ComponentRoute[];
58
+ /** The name of the documentation directory (e.g. 'docs') */
59
+ docsDirName: string;
45
60
  /** Site configuration (`virtual:boltdocs-config`) */
46
61
  config: any;
47
62
  /** Dynamic import mapping from `import.meta.glob` for the documentation pages */
@@ -33,6 +33,19 @@ interface ComponentRoute {
33
33
  locale?: string;
34
34
  /** The version this route belongs to, if versioning is configured */
35
35
  version?: string;
36
+ /** Optional icon to display (Lucide icon name or raw SVG) */
37
+ icon?: string;
38
+ /** The tab this route belongs to, if tabs are configured */
39
+ tab?: string;
40
+ /** Optional badge to display next to the sidebar item */
41
+ badge?: string | {
42
+ text: string;
43
+ expires?: string;
44
+ };
45
+ /** Optional icon for the route's group */
46
+ groupIcon?: string;
47
+ /** The extracted plain-text content of the page for search indexing */
48
+ _content?: string;
36
49
  }
37
50
  /**
38
51
  * Configuration options for initializing the Boltdocs client app.
@@ -42,6 +55,8 @@ interface CreateBoltdocsAppOptions {
42
55
  target: string;
43
56
  /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
44
57
  routes: ComponentRoute[];
58
+ /** The name of the documentation directory (e.g. 'docs') */
59
+ docsDirName: string;
45
60
  /** Site configuration (`virtual:boltdocs-config`) */
46
61
  config: any;
47
62
  /** Dynamic import mapping from `import.meta.glob` for the documentation pages */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "boltdocs",
3
- "version": "1.6.0",
3
+ "version": "1.7.1",
4
4
  "description": "A lightweight documentation generator for React projects.",
5
5
  "main": "dist/node/index.js",
6
6
  "module": "dist/node/index.mjs",