boltdocs 1.6.0 → 1.7.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.
Files changed (51) hide show
  1. package/dist/{SearchDialog-3QICRMWF.css → SearchDialog-UOAW6IR3.css} +270 -113
  2. package/dist/{SearchDialog-J3KNRGNO.mjs → SearchDialog-YOXMFGH6.mjs} +1 -1
  3. package/dist/{chunk-HSPDIRTW.mjs → chunk-MULKZFVN.mjs} +872 -758
  4. package/dist/client/index.css +270 -113
  5. package/dist/client/index.d.mts +21 -7
  6. package/dist/client/index.d.ts +21 -7
  7. package/dist/client/index.js +637 -499
  8. package/dist/client/index.mjs +17 -1
  9. package/dist/client/ssr.css +270 -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 +533 -412
  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 +10 -2
  17. package/dist/node/index.d.ts +10 -2
  18. package/dist/node/index.js +45 -21
  19. package/dist/node/index.mjs +45 -21
  20. package/dist/{types-DGIo1VKD.d.mts → types-CviV0GbX.d.mts} +13 -0
  21. package/dist/{types-DGIo1VKD.d.ts → types-CviV0GbX.d.ts} +13 -0
  22. package/package.json +1 -1
  23. package/src/client/app/index.tsx +8 -4
  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 +53 -0
  27. package/src/client/theme/components/mdx/index.ts +3 -0
  28. package/src/client/theme/components/mdx/mdx-components.css +49 -0
  29. package/src/client/theme/styles/markdown.css +8 -3
  30. package/src/client/theme/styles/variables.css +10 -9
  31. package/src/client/theme/ui/Layout/Layout.tsx +2 -10
  32. package/src/client/theme/ui/Layout/base.css +15 -3
  33. package/src/client/theme/ui/Link/Link.tsx +2 -2
  34. package/src/client/theme/ui/Link/LinkPreview.tsx +9 -14
  35. package/src/client/theme/ui/Link/link-preview.css +30 -27
  36. package/src/client/theme/ui/Navbar/Navbar.tsx +65 -17
  37. package/src/client/theme/ui/Navbar/Tabs.tsx +74 -0
  38. package/src/client/theme/ui/Navbar/navbar.css +111 -5
  39. package/src/client/theme/ui/OnThisPage/OnThisPage.tsx +65 -49
  40. package/src/client/theme/ui/OnThisPage/toc.css +30 -10
  41. package/src/client/theme/ui/Sidebar/Sidebar.tsx +97 -57
  42. package/src/client/theme/ui/Sidebar/sidebar.css +61 -67
  43. package/src/client/types.ts +10 -0
  44. package/src/node/config.ts +19 -1
  45. package/src/node/plugin/entry.ts +5 -1
  46. package/src/node/plugin/index.ts +2 -1
  47. package/src/node/routes/index.ts +12 -1
  48. package/src/node/routes/parser.ts +21 -7
  49. package/src/node/routes/types.ts +9 -1
  50. package/src/node/ssg/index.ts +2 -1
  51. 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-MULKZFVN.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,10 @@ 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;
75
83
  }
76
84
 
77
85
  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,10 @@ 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;
75
83
  }
76
84
 
77
85
  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,16 @@ 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;
523
532
  return {
524
533
  route: {
525
534
  path: finalPath,
@@ -531,15 +540,17 @@ function parseDocFile(file, docsDir, basePath, config) {
531
540
  headings,
532
541
  locale,
533
542
  version,
534
- badge: sanitizedBadge
543
+ badge: sanitizedBadge,
544
+ icon,
545
+ tab: inferredTab
535
546
  },
536
547
  relativeDir: cleanDirName,
537
548
  isGroupIndex,
549
+ inferredTab,
538
550
  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)
551
+ title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
552
+ position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0),
553
+ icon
543
554
  } : void 0,
544
555
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
545
556
  };
@@ -608,13 +619,17 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
608
619
  if (!groupMeta.has(p.relativeDir)) {
609
620
  groupMeta.set(p.relativeDir, {
610
621
  title: capitalize(p.relativeDir),
611
- position: p.inferredGroupPosition
622
+ position: p.inferredGroupPosition,
623
+ icon: p.route.icon
612
624
  });
613
625
  } else {
614
626
  const entry = groupMeta.get(p.relativeDir);
615
627
  if (entry.position === void 0 && p.inferredGroupPosition !== void 0) {
616
628
  entry.position = p.inferredGroupPosition;
617
629
  }
630
+ if (!entry.icon && p.route.icon) {
631
+ entry.icon = p.route.icon;
632
+ }
618
633
  }
619
634
  }
620
635
  if (p.isGroupIndex && p.relativeDir && p.groupMeta) {
@@ -623,6 +638,9 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
623
638
  if (p.groupMeta.position !== void 0) {
624
639
  entry.position = p.groupMeta.position;
625
640
  }
641
+ if (p.groupMeta.icon) {
642
+ entry.icon = p.groupMeta.icon;
643
+ }
626
644
  }
627
645
  }
628
646
  const routes = parsed.map((p) => {
@@ -632,7 +650,8 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
632
650
  ...p.route,
633
651
  group: dir,
634
652
  groupTitle: meta?.title || (dir ? capitalize(dir) : void 0),
635
- groupPosition: meta?.position
653
+ groupPosition: meta?.position,
654
+ groupIcon: meta?.icon
636
655
  };
637
656
  });
638
657
  if (config?.i18n) {
@@ -797,7 +816,7 @@ var _filename = (0, import_url2.fileURLToPath)(import_meta2.url);
797
816
  var _dirname = import_path4.default.dirname(_filename);
798
817
  var _require = (0, import_module.createRequire)(import_meta2.url);
799
818
  async function generateStaticPages(options) {
800
- const { docsDir, outDir, config } = options;
819
+ const { docsDir, docsDirName, outDir, config } = options;
801
820
  const routes = await generateRoutes(docsDir, config);
802
821
  const siteTitle = config?.themeConfig?.title || "Boltdocs";
803
822
  const siteDescription = config?.themeConfig?.description || "";
@@ -829,6 +848,7 @@ async function generateStaticPages(options) {
829
848
  path: route.path,
830
849
  routes,
831
850
  config: config || {},
851
+ docsDirName,
832
852
  modules: fakeModules,
833
853
  homePage: void 0
834
854
  // No custom home page for now
@@ -863,10 +883,11 @@ async function generateStaticPages(options) {
863
883
 
864
884
  // src/node/plugin/index.ts
865
885
  init_utils();
866
- var import_path5 = __toESM(require("path"));
886
+ var import_path6 = __toESM(require("path"));
867
887
 
868
888
  // src/node/plugin/entry.ts
869
889
  init_utils();
890
+ var import_path5 = __toESM(require("path"));
870
891
  function generateEntryCode(options, config) {
871
892
  const homeImport = options.homePage ? `import HomePage from '${normalizePath(options.homePage)}';` : "";
872
893
  const homeOption = options.homePage ? "homePage: HomePage," : "";
@@ -875,11 +896,12 @@ function generateEntryCode(options, config) {
875
896
  const componentImports = pluginComponents.map(
876
897
  ([
877
898
  name,
878
- path6
879
- ]) => `import * as _comp_${name} from '${normalizePath(path6)}';
899
+ path7
900
+ ]) => `import * as _comp_${name} from '${normalizePath(path7)}';
880
901
  const ${name} = _comp_${name}.default || _comp_${name}['${name}'] || _comp_${name};`
881
902
  ).join("\n");
882
903
  const componentMap = pluginComponents.map(([name]) => name).join(", ");
904
+ const docsDirName = import_path5.default.basename(options.docsDir || "docs");
883
905
  return `
884
906
  import { createBoltdocsApp as _createApp } from 'boltdocs/client';
885
907
  import 'boltdocs/style.css';
@@ -892,8 +914,9 @@ ${componentImports}
892
914
  _createApp({
893
915
  target: '#root',
894
916
  routes: _routes,
917
+ docsDirName: '${docsDirName}',
895
918
  config: _config,
896
- modules: import.meta.glob('/docs/**/*.{md,mdx}'),
919
+ modules: import.meta.glob('/${docsDirName}/**/*.{md,mdx}'),
897
920
  hot: import.meta.hot,
898
921
  ${homeOption}
899
922
  components: { ${componentMap} },
@@ -946,7 +969,7 @@ ${themeScript} </head>`);
946
969
 
947
970
  // src/node/plugin/index.ts
948
971
  function boltdocsPlugin(options = {}, passedConfig) {
949
- const docsDir = import_path5.default.resolve(process.cwd(), options.docsDir || "docs");
972
+ const docsDir = import_path6.default.resolve(process.cwd(), options.docsDir || "docs");
950
973
  const normalizedDocsDir = normalizePath(docsDir);
951
974
  let config = passedConfig;
952
975
  let viteConfig;
@@ -976,7 +999,7 @@ function boltdocsPlugin(options = {}, passedConfig) {
976
999
  },
977
1000
  configureServer(server) {
978
1001
  const configPaths = CONFIG_FILES.map(
979
- (c) => import_path5.default.resolve(process.cwd(), c)
1002
+ (c) => import_path6.default.resolve(process.cwd(), c)
980
1003
  );
981
1004
  server.watcher.add(configPaths);
982
1005
  const handleFileEvent = async (file, type) => {
@@ -1039,8 +1062,9 @@ function boltdocsPlugin(options = {}, passedConfig) {
1039
1062
  },
1040
1063
  async closeBundle() {
1041
1064
  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 });
1065
+ const outDir = viteConfig?.build?.outDir ? import_path6.default.resolve(viteConfig.root, viteConfig.build.outDir) : import_path6.default.resolve(process.cwd(), "dist");
1066
+ const docsDirName = import_path6.default.basename(docsDir || "docs");
1067
+ await generateStaticPages({ docsDir, docsDirName, outDir, config });
1044
1068
  const { flushCache: flushCache2 } = await Promise.resolve().then(() => (init_cache(), cache_exports));
1045
1069
  await flushCache2();
1046
1070
  }
@@ -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,16 @@ 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;
105
114
  return {
106
115
  route: {
107
116
  path: finalPath,
@@ -113,15 +122,17 @@ function parseDocFile(file, docsDir, basePath, config) {
113
122
  headings,
114
123
  locale,
115
124
  version,
116
- badge: sanitizedBadge
125
+ badge: sanitizedBadge,
126
+ icon,
127
+ tab: inferredTab
117
128
  },
118
129
  relativeDir: cleanDirName,
119
130
  isGroupIndex,
131
+ inferredTab,
120
132
  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)
133
+ title: data.groupTitle || data.title || (cleanDirName ? capitalize(cleanDirName) : ""),
134
+ position: data.groupPosition ?? data.sidebarPosition ?? (rawDirName ? extractNumberPrefix(rawDirName) : void 0),
135
+ icon
125
136
  } : void 0,
126
137
  inferredGroupPosition: rawDirName ? extractNumberPrefix(rawDirName) : void 0
127
138
  };
@@ -190,13 +201,17 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
190
201
  if (!groupMeta.has(p.relativeDir)) {
191
202
  groupMeta.set(p.relativeDir, {
192
203
  title: capitalize(p.relativeDir),
193
- position: p.inferredGroupPosition
204
+ position: p.inferredGroupPosition,
205
+ icon: p.route.icon
194
206
  });
195
207
  } else {
196
208
  const entry = groupMeta.get(p.relativeDir);
197
209
  if (entry.position === void 0 && p.inferredGroupPosition !== void 0) {
198
210
  entry.position = p.inferredGroupPosition;
199
211
  }
212
+ if (!entry.icon && p.route.icon) {
213
+ entry.icon = p.route.icon;
214
+ }
200
215
  }
201
216
  }
202
217
  if (p.isGroupIndex && p.relativeDir && p.groupMeta) {
@@ -205,6 +220,9 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
205
220
  if (p.groupMeta.position !== void 0) {
206
221
  entry.position = p.groupMeta.position;
207
222
  }
223
+ if (p.groupMeta.icon) {
224
+ entry.icon = p.groupMeta.icon;
225
+ }
208
226
  }
209
227
  }
210
228
  const routes = parsed.map((p) => {
@@ -214,7 +232,8 @@ async function generateRoutes(docsDir, config, basePath = "/docs") {
214
232
  ...p.route,
215
233
  group: dir,
216
234
  groupTitle: meta?.title || (dir ? capitalize(dir) : void 0),
217
- groupPosition: meta?.position
235
+ groupPosition: meta?.position,
236
+ groupIcon: meta?.icon
218
237
  };
219
238
  });
220
239
  if (config?.i18n) {
@@ -375,7 +394,7 @@ var _filename = fileURLToPath(import.meta.url);
375
394
  var _dirname = path3.dirname(_filename);
376
395
  var _require = createRequire(import.meta.url);
377
396
  async function generateStaticPages(options) {
378
- const { docsDir, outDir, config } = options;
397
+ const { docsDir, docsDirName, outDir, config } = options;
379
398
  const routes = await generateRoutes(docsDir, config);
380
399
  const siteTitle = config?.themeConfig?.title || "Boltdocs";
381
400
  const siteDescription = config?.themeConfig?.description || "";
@@ -407,6 +426,7 @@ async function generateStaticPages(options) {
407
426
  path: route.path,
408
427
  routes,
409
428
  config: config || {},
429
+ docsDirName,
410
430
  modules: fakeModules,
411
431
  homePage: void 0
412
432
  // No custom home page for now
@@ -440,9 +460,10 @@ async function generateStaticPages(options) {
440
460
  }
441
461
 
442
462
  // src/node/plugin/index.ts
443
- import path4 from "path";
463
+ import path5 from "path";
444
464
 
445
465
  // src/node/plugin/entry.ts
466
+ import path4 from "path";
446
467
  function generateEntryCode(options, config) {
447
468
  const homeImport = options.homePage ? `import HomePage from '${normalizePath(options.homePage)}';` : "";
448
469
  const homeOption = options.homePage ? "homePage: HomePage," : "";
@@ -451,11 +472,12 @@ function generateEntryCode(options, config) {
451
472
  const componentImports = pluginComponents.map(
452
473
  ([
453
474
  name,
454
- path5
455
- ]) => `import * as _comp_${name} from '${normalizePath(path5)}';
475
+ path6
476
+ ]) => `import * as _comp_${name} from '${normalizePath(path6)}';
456
477
  const ${name} = _comp_${name}.default || _comp_${name}['${name}'] || _comp_${name};`
457
478
  ).join("\n");
458
479
  const componentMap = pluginComponents.map(([name]) => name).join(", ");
480
+ const docsDirName = path4.basename(options.docsDir || "docs");
459
481
  return `
460
482
  import { createBoltdocsApp as _createApp } from 'boltdocs/client';
461
483
  import 'boltdocs/style.css';
@@ -468,8 +490,9 @@ ${componentImports}
468
490
  _createApp({
469
491
  target: '#root',
470
492
  routes: _routes,
493
+ docsDirName: '${docsDirName}',
471
494
  config: _config,
472
- modules: import.meta.glob('/docs/**/*.{md,mdx}'),
495
+ modules: import.meta.glob('/${docsDirName}/**/*.{md,mdx}'),
473
496
  hot: import.meta.hot,
474
497
  ${homeOption}
475
498
  components: { ${componentMap} },
@@ -522,7 +545,7 @@ ${themeScript} </head>`);
522
545
 
523
546
  // src/node/plugin/index.ts
524
547
  function boltdocsPlugin(options = {}, passedConfig) {
525
- const docsDir = path4.resolve(process.cwd(), options.docsDir || "docs");
548
+ const docsDir = path5.resolve(process.cwd(), options.docsDir || "docs");
526
549
  const normalizedDocsDir = normalizePath(docsDir);
527
550
  let config = passedConfig;
528
551
  let viteConfig;
@@ -552,7 +575,7 @@ function boltdocsPlugin(options = {}, passedConfig) {
552
575
  },
553
576
  configureServer(server) {
554
577
  const configPaths = CONFIG_FILES.map(
555
- (c) => path4.resolve(process.cwd(), c)
578
+ (c) => path5.resolve(process.cwd(), c)
556
579
  );
557
580
  server.watcher.add(configPaths);
558
581
  const handleFileEvent = async (file, type) => {
@@ -615,8 +638,9 @@ function boltdocsPlugin(options = {}, passedConfig) {
615
638
  },
616
639
  async closeBundle() {
617
640
  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 });
641
+ const outDir = viteConfig?.build?.outDir ? path5.resolve(viteConfig.root, viteConfig.build.outDir) : path5.resolve(process.cwd(), "dist");
642
+ const docsDirName = path5.basename(docsDir || "docs");
643
+ await generateStaticPages({ docsDir, docsDirName, outDir, config });
620
644
  const { flushCache } = await import("../cache-KNL5B4EE.mjs");
621
645
  await flushCache();
622
646
  }
@@ -33,6 +33,17 @@ 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;
36
47
  }
37
48
  /**
38
49
  * Configuration options for initializing the Boltdocs client app.
@@ -42,6 +53,8 @@ interface CreateBoltdocsAppOptions {
42
53
  target: string;
43
54
  /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
44
55
  routes: ComponentRoute[];
56
+ /** The name of the documentation directory (e.g. 'docs') */
57
+ docsDirName: string;
45
58
  /** Site configuration (`virtual:boltdocs-config`) */
46
59
  config: any;
47
60
  /** Dynamic import mapping from `import.meta.glob` for the documentation pages */
@@ -33,6 +33,17 @@ 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;
36
47
  }
37
48
  /**
38
49
  * Configuration options for initializing the Boltdocs client app.
@@ -42,6 +53,8 @@ interface CreateBoltdocsAppOptions {
42
53
  target: string;
43
54
  /** Initial routes generated by the Vite plugin (`virtual:boltdocs-routes`) */
44
55
  routes: ComponentRoute[];
56
+ /** The name of the documentation directory (e.g. 'docs') */
57
+ docsDirName: string;
45
58
  /** Site configuration (`virtual:boltdocs-config`) */
46
59
  config: any;
47
60
  /** 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.0",
4
4
  "description": "A lightweight documentation generator for React projects.",
5
5
  "main": "dist/node/index.js",
6
6
  "module": "dist/node/index.mjs",
@@ -89,6 +89,7 @@ const mdxComponents = {
89
89
  export function AppShell({
90
90
  initialRoutes,
91
91
  initialConfig,
92
+ docsDirName,
92
93
  modules,
93
94
  hot,
94
95
  homePage: HomePage,
@@ -96,6 +97,7 @@ export function AppShell({
96
97
  }: {
97
98
  initialRoutes: ComponentRoute[];
98
99
  initialConfig: any;
100
+ docsDirName: string;
99
101
  modules: Record<string, () => Promise<any>>;
100
102
  hot?: any;
101
103
  homePage?: React.ComponentType;
@@ -110,8 +112,8 @@ export function AppShell({
110
112
  (route) => !(HomePage && (route.path === "/" || route.path === "")),
111
113
  )
112
114
  .map((route) => {
113
- const loaderKey = Object.keys(modules).find((k) =>
114
- k.endsWith("/" + route.filePath),
115
+ const loaderKey = Object.keys(modules).find(
116
+ (k) => k === `/${docsDirName}/${route.filePath}`,
115
117
  );
116
118
  const loader = loaderKey ? modules[loaderKey] : null;
117
119
 
@@ -142,7 +144,7 @@ export function AppShell({
142
144
  // Sync resolved routes when info or modules change
143
145
  useEffect(() => {
144
146
  setResolvedRoutes(resolveRoutes(routesInfo));
145
- }, [routesInfo, modules]);
147
+ }, [routesInfo, modules, docsDirName]);
146
148
 
147
149
  return (
148
150
  <ConfigContext.Provider value={config}>
@@ -302,7 +304,8 @@ function MdxPage({
302
304
  * ```
303
305
  */
304
306
  export function createBoltdocsApp(options: CreateBoltdocsAppOptions) {
305
- const { target, routes, config, modules, hot, homePage } = options;
307
+ const { target, routes, docsDirName, config, modules, hot, homePage } =
308
+ options;
306
309
  const container = document.querySelector(target);
307
310
  if (!container) {
308
311
  throw new Error(
@@ -316,6 +319,7 @@ export function createBoltdocsApp(options: CreateBoltdocsAppOptions) {
316
319
  <AppShell
317
320
  initialRoutes={routes}
318
321
  initialConfig={config}
322
+ docsDirName={docsDirName}
319
323
  modules={modules}
320
324
  hot={hot}
321
325
  homePage={homePage}
@@ -29,6 +29,7 @@ export {
29
29
  InfoBox,
30
30
  List,
31
31
  FileTree,
32
+ Table,
32
33
  } from "./theme/components/mdx";
33
34
  export type {
34
35
  ButtonProps,
@@ -40,4 +41,5 @@ export type {
40
41
  AdmonitionProps,
41
42
  ListProps,
42
43
  FileTreeProps,
44
+ TableProps,
43
45
  } from "./theme/components/mdx";