react-docs-module 0.1.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 (100) hide show
  1. package/README.md +279 -0
  2. package/ai-chat.tsx +222 -0
  3. package/chat-api.ts +90 -0
  4. package/cn.ts +15 -0
  5. package/config.ts +29 -0
  6. package/dist/ai-chat.d.ts +12 -0
  7. package/dist/ai-chat.js +72 -0
  8. package/dist/ai-chat.js.map +1 -0
  9. package/dist/chat-api.d.ts +16 -0
  10. package/dist/chat-api.js +62 -0
  11. package/dist/chat-api.js.map +1 -0
  12. package/dist/cn.d.ts +4 -0
  13. package/dist/cn.js +14 -0
  14. package/dist/cn.js.map +1 -0
  15. package/dist/config.d.ts +14 -0
  16. package/dist/config.js +15 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/doc-pagination.d.ts +13 -0
  19. package/dist/doc-pagination.js +8 -0
  20. package/dist/doc-pagination.js.map +1 -0
  21. package/dist/docs-index.d.ts +7 -0
  22. package/dist/docs-index.js +11 -0
  23. package/dist/docs-index.js.map +1 -0
  24. package/dist/docs-page.d.ts +15 -0
  25. package/dist/docs-page.js +38 -0
  26. package/dist/docs-page.js.map +1 -0
  27. package/dist/docs-sidebar.d.ts +18 -0
  28. package/dist/docs-sidebar.d.ts.map +1 -0
  29. package/dist/docs-sidebar.js +27 -0
  30. package/dist/docs-sidebar.js.map +1 -0
  31. package/dist/documentation-layout.d.ts +15 -0
  32. package/dist/documentation-layout.js +20 -0
  33. package/dist/documentation-layout.js.map +1 -0
  34. package/dist/heading.d.ts +10 -0
  35. package/dist/heading.js +16 -0
  36. package/dist/heading.js.map +1 -0
  37. package/dist/index.d.ts +18 -0
  38. package/dist/index.js +19 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/mdx/callouts.d.ts +8 -0
  41. package/dist/mdx/callouts.js +8 -0
  42. package/dist/mdx/callouts.js.map +1 -0
  43. package/dist/mdx/code-block.d.ts +8 -0
  44. package/dist/mdx/code-block.js +29 -0
  45. package/dist/mdx/code-block.js.map +1 -0
  46. package/dist/mdx/components.d.ts +13 -0
  47. package/dist/mdx/components.js +21 -0
  48. package/dist/mdx/components.js.map +1 -0
  49. package/dist/mdx.d.ts +20 -0
  50. package/dist/mdx.js +109 -0
  51. package/dist/mdx.js.map +1 -0
  52. package/dist/search-index.d.ts +10 -0
  53. package/dist/search-index.js +38 -0
  54. package/dist/search-index.js.map +1 -0
  55. package/dist/search.d.ts +6 -0
  56. package/dist/search.js +142 -0
  57. package/dist/search.js.map +1 -0
  58. package/dist/table-of-contents-provider.d.ts +4 -0
  59. package/dist/table-of-contents-provider.js +30 -0
  60. package/dist/table-of-contents-provider.js.map +1 -0
  61. package/dist/table-of-contents.d.ts +11 -0
  62. package/dist/table-of-contents.js +9 -0
  63. package/dist/table-of-contents.js.map +1 -0
  64. package/dist/theme-context.d.ts +20 -0
  65. package/dist/theme-context.js +28 -0
  66. package/dist/theme-context.js.map +1 -0
  67. package/dist/tsconfig.tsbuildinfo +1 -0
  68. package/dist/ui/button.d.ts +12 -0
  69. package/dist/ui/button.js +34 -0
  70. package/dist/ui/button.js.map +1 -0
  71. package/dist/ui/dialog.d.ts +17 -0
  72. package/dist/ui/dialog.js +22 -0
  73. package/dist/ui/dialog.js.map +1 -0
  74. package/dist/ui/input.d.ts +4 -0
  75. package/dist/ui/input.js +9 -0
  76. package/dist/ui/input.js.map +1 -0
  77. package/dist/util.d.ts +59 -0
  78. package/dist/util.js +96 -0
  79. package/dist/util.js.map +1 -0
  80. package/doc-pagination.tsx +67 -0
  81. package/docs-index.tsx +17 -0
  82. package/docs-page.tsx +68 -0
  83. package/docs-sidebar.tsx +165 -0
  84. package/documentation-layout.tsx +99 -0
  85. package/heading.tsx +63 -0
  86. package/index.ts +28 -0
  87. package/mdx/callouts.tsx +29 -0
  88. package/mdx/code-block.tsx +89 -0
  89. package/mdx/components.tsx +55 -0
  90. package/mdx.ts +138 -0
  91. package/package.json +99 -0
  92. package/search-index.ts +52 -0
  93. package/search.tsx +273 -0
  94. package/table-of-contents-provider.tsx +43 -0
  95. package/table-of-contents.tsx +44 -0
  96. package/theme-context.tsx +57 -0
  97. package/ui/button.tsx +56 -0
  98. package/ui/dialog.tsx +108 -0
  99. package/ui/input.tsx +22 -0
  100. package/util.ts +169 -0
@@ -0,0 +1,99 @@
1
+ import { DocsSidebar } from "./docs-sidebar";
2
+ import { TableOfContents } from "./table-of-contents";
3
+ import { Search } from "./search";
4
+ import { DocPagination } from "./doc-pagination";
5
+ import { ReactDocsConfig } from "./config";
6
+ import { DocsThemeProvider } from "./theme-context";
7
+ import { getDocsSidebar } from "./util";
8
+
9
+ interface Props {
10
+ config: ReactDocsConfig;
11
+ navigation: any;
12
+ children: React.ReactNode;
13
+ currentPath: string;
14
+ headings: {
15
+ id: string;
16
+ text: string;
17
+ level: number;
18
+ }[];
19
+ }
20
+
21
+ export function DocumentationLayout({
22
+ config,
23
+ navigation,
24
+ children,
25
+ currentPath,
26
+ headings,
27
+ }: Props) {
28
+ // Get docs configuration for theming
29
+ const { config: docsConfig } = getDocsSidebar(config);
30
+
31
+ // Find current page and get prev/next
32
+ const allPages = navigation.flatMap((section: any) => section.pages);
33
+ const currentPageIndex = allPages.findIndex(
34
+ (page: any) => config.basePath + `/${page.slug}` === currentPath
35
+ );
36
+ const prevPage =
37
+ currentPageIndex > 0 ? allPages[currentPageIndex - 1] : undefined;
38
+ const nextPage =
39
+ currentPageIndex < allPages.length - 1
40
+ ? allPages[currentPageIndex + 1]
41
+ : undefined;
42
+
43
+ return (
44
+ <DocsThemeProvider config={docsConfig}>
45
+ <div className="max-w-screen-xl mx-auto px-4">
46
+ <div className="flex gap-8">
47
+ {/* Left Sidebar */}
48
+ <div className="w-64 flex-shrink-0 hidden md:block">
49
+ <div className="sticky top-0">
50
+ <div className="py-4">
51
+ <DocsSidebar
52
+ config={config}
53
+ navigation={navigation}
54
+ currentPath={currentPath}
55
+ />
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <div className="flex-1 min-w-0">
61
+ {/* Sticky Search Container */}
62
+ <div className="sticky top-0 z-50 bg-gray-900/95 border-b border-gray-800">
63
+ <div className="py-4">
64
+ <div className="flex items-center gap-1">
65
+ <div className="md:hidden">
66
+ <DocsSidebar
67
+ config={config}
68
+ navigation={navigation}
69
+ currentPath={currentPath}
70
+ />
71
+ </div>
72
+ <div className="flex-1 md:max-w-2xl md:mx-auto">
73
+ <Search config={config} />
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+
79
+ <main className="py-8 max-w-2xl mx-auto">
80
+ <div className="relative z-0">
81
+ {children}
82
+ <DocPagination config={config} prev={prevPage} next={nextPage} />
83
+ </div>
84
+ </main>
85
+ </div>
86
+
87
+ {/* Right Sidebar */}
88
+ <div className="w-64 flex-shrink-0 hidden lg:block">
89
+ <div className="sticky top-0">
90
+ <div className="py-4">
91
+ <TableOfContents headings={headings} />
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </DocsThemeProvider>
98
+ );
99
+ }
package/heading.tsx ADDED
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { useState } from "react";
3
+ import { cn } from "./cn";
4
+ import React from "react";
5
+
6
+ interface HeadingProps {
7
+ level: 1 | 2 | 3;
8
+ id: string;
9
+ children: React.ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ export function Heading({ level, id, children, className }: HeadingProps) {
14
+ const [copied, setCopied] = useState(false);
15
+ const Tag = `h${level}` as keyof React.JSX.IntrinsicElements;
16
+
17
+ const copyLink = () => {
18
+ const url = `${window.location.origin}${window.location.pathname}#${id}`;
19
+ navigator.clipboard.writeText(url);
20
+ setCopied(true);
21
+ setTimeout(() => setCopied(false), 2000);
22
+ };
23
+
24
+ return (
25
+ <Tag
26
+ id={id}
27
+ className={cn(
28
+ "group relative flex items-center scroll-mt-24",
29
+ level === 1 && "text-3xl font-medium mb-6 mt-6",
30
+ level === 2 && "text-2xl font-medium mb-4 mt-8",
31
+ level === 3 && "text-lg font-medium mb-4 mt-6",
32
+ className
33
+ )}
34
+ >
35
+ <a href={`#${id}`} className="no-underline">
36
+ {children}
37
+ </a>
38
+ <button
39
+ onClick={copyLink}
40
+ className="invisible ml-2 group-hover:visible"
41
+ aria-label="Copy link to heading"
42
+ >
43
+ {copied ? (
44
+ <span className="text-green-500 text-sm">✓</span>
45
+ ) : (
46
+ <svg
47
+ className="w-5 h-5 text-gray-400 hover:text-white"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ viewBox="0 0 24 24"
51
+ >
52
+ <path
53
+ strokeLinecap="round"
54
+ strokeLinejoin="round"
55
+ strokeWidth={2}
56
+ d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
57
+ />
58
+ </svg>
59
+ )}
60
+ </button>
61
+ </Tag>
62
+ );
63
+ }
package/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type { ReactDocsConfig, ReactDocsConfigOptions } from "./config";
2
+ export { createReactDocsConfig } from "./config";
3
+
4
+ export { getDocsSidebar } from "./util";
5
+ export type { DocsJsonConfig } from "./util";
6
+ export { getAllDocs, getAllDocsContent, getDocBySlug } from "./mdx";
7
+
8
+ // Search functionality
9
+ export { buildSearchIndex } from "./search-index";
10
+ export { Search } from "./search";
11
+
12
+ // Chat API
13
+ export { streamDocsChatResponse } from "./chat-api";
14
+ export type { ModelProvider, Message, FinishResult } from "./chat-api";
15
+
16
+ // Page components and functions
17
+ export { generateDocsMetadata, generateDocsStaticParams, DocsPage } from "./docs-page";
18
+ export { DocsIndex } from "./docs-index";
19
+
20
+ // Layout components
21
+ export { DocumentationLayout } from "./documentation-layout";
22
+ export { DocsSidebar } from "./docs-sidebar";
23
+ export { TableOfContents } from "./table-of-contents";
24
+ export { DocPagination } from "./doc-pagination";
25
+
26
+ // Theme context
27
+ export { DocsThemeProvider, useDocsTheme, useDocsColors } from "./theme-context";
28
+ export type { ThemeColors, DocsTheme } from "./theme-context";
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+
3
+ export function Info({ children }: { children: React.ReactNode }) {
4
+ return (
5
+ <div className="bg-blue-900/30 border border-blue-500/20 rounded-lg p-3 my-3">
6
+ <div className="flex gap-2 items-center text-blue-400 mb-1.5">
7
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
8
+ <path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm1-3V7H7v6h2zM8 5a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" />
9
+ </svg>
10
+ <span className="font-medium">Info</span>
11
+ </div>
12
+ <div className="text-gray-300 [&>p]:m-0">{children}</div>
13
+ </div>
14
+ );
15
+ }
16
+
17
+ export function Warning({ children }: { children: React.ReactNode }) {
18
+ return (
19
+ <div className="bg-yellow-900/30 border border-yellow-500/20 rounded-lg p-3 my-3">
20
+ <div className="flex gap-2 items-center text-yellow-400 mb-1.5">
21
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
22
+ <path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
23
+ </svg>
24
+ <span className="font-medium">Warning</span>
25
+ </div>
26
+ <div className="text-gray-300 [&>p]:m-0">{children}</div>
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,89 @@
1
+ "use client";
2
+
3
+ import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism";
4
+ import { useState } from "react";
5
+ import { Prism, SyntaxHighlighterProps } from "react-syntax-highlighter";
6
+ const SyntaxHighlighter = Prism as any as React.FC<SyntaxHighlighterProps>;
7
+
8
+ interface CodeBlockProps {
9
+ children: string;
10
+ className?: string;
11
+ inline?: boolean;
12
+ }
13
+
14
+ export function CodeBlock({ children, className, inline }: CodeBlockProps) {
15
+ const [copied, setCopied] = useState(false);
16
+
17
+ if (inline) {
18
+ return (
19
+ <code className="bg-gray-800/50 px-1.5 py-0.5 rounded text-sm font-mono">
20
+ {children}
21
+ </code>
22
+ );
23
+ }
24
+
25
+ const language = className?.replace("language-", "") || "text";
26
+
27
+ const copyCode = () => {
28
+ navigator.clipboard.writeText(String(children));
29
+ setCopied(true);
30
+ setTimeout(() => setCopied(false), 2000);
31
+ };
32
+
33
+ return (
34
+ <div
35
+ className="relative bg-gray-800/50 rounded-lg overflow-x-auto w-full my-4 group"
36
+ style={{ maxWidth: "100%" }}
37
+ >
38
+ <button
39
+ onClick={copyCode}
40
+ className="absolute right-2 top-2 p-2 rounded-md bg-gray-700/50 invisible group-hover:visible hover:bg-gray-700 transition-colors"
41
+ aria-label="Copy code"
42
+ >
43
+ {copied ? (
44
+ <svg
45
+ width="16"
46
+ height="16"
47
+ viewBox="0 0 24 24"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ strokeWidth="2"
51
+ className="text-green-500"
52
+ >
53
+ <path d="M20 6L9 17l-5-5" />
54
+ </svg>
55
+ ) : (
56
+ <svg
57
+ width="16"
58
+ height="16"
59
+ viewBox="0 0 24 24"
60
+ fill="none"
61
+ stroke="currentColor"
62
+ strokeWidth="2"
63
+ className="text-gray-400"
64
+ >
65
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
66
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
67
+ </svg>
68
+ )}
69
+ </button>
70
+ <SyntaxHighlighter
71
+ language={language}
72
+ style={vscDarkPlus}
73
+ customStyle={{
74
+ margin: 0,
75
+ background: "transparent",
76
+ fontSize: "1.1rem",
77
+ lineHeight: "1.4",
78
+ padding: "1.5rem",
79
+ borderRadius: "0.5rem",
80
+ maxWidth: "100%",
81
+ overflowX: "auto",
82
+ }}
83
+ wrapLongLines={false}
84
+ >
85
+ {children}
86
+ </SyntaxHighlighter>
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,55 @@
1
+ import { Heading } from "../heading";
2
+ import { Info, Warning } from "./callouts";
3
+ import { CodeBlock } from "./code-block";
4
+ import { slugify } from "../cn";
5
+ import { HTMLAttributes } from "react";
6
+ import Image from "next/image";
7
+
8
+ export const mdxComponents = {
9
+ Info,
10
+ Warning,
11
+ h1: ({ children, ...props }: HTMLAttributes<HTMLHeadingElement>) => (
12
+ <Heading level={1} id={slugify(String(children))} {...props}>
13
+ {children}
14
+ </Heading>
15
+ ),
16
+ h2: ({ children, ...props }: HTMLAttributes<HTMLHeadingElement>) => (
17
+ <Heading level={2} id={slugify(String(children))} {...props}>
18
+ {children}
19
+ </Heading>
20
+ ),
21
+ h3: ({ children, ...props }: HTMLAttributes<HTMLHeadingElement>) => (
22
+ <Heading level={3} id={slugify(String(children))} {...props}>
23
+ {children}
24
+ </Heading>
25
+ ),
26
+ pre: (props: any) => <CodeBlock {...props.children.props} />,
27
+ code: (props: HTMLAttributes<HTMLElement>) => (
28
+ <code
29
+ {...props}
30
+ className={`${props.className || ""
31
+ } bg-gray-800/50 px-1.5 py-0.5 rounded text-sm font-mono`}
32
+ />
33
+ ),
34
+ img: ({
35
+ src,
36
+ alt,
37
+ width: _width,
38
+ height: _height,
39
+ ...props
40
+ }: React.ImgHTMLAttributes<HTMLImageElement>) => {
41
+ if (!src) return null;
42
+ return (
43
+ <Image
44
+ src={src as string}
45
+ alt={alt || ""}
46
+ width={0}
47
+ height={0}
48
+ sizes="100vw"
49
+ style={{ width: "100%", height: "auto" }}
50
+ className="max-w-[400px] aspect-auto mx-auto rounded-lg"
51
+ {...props}
52
+ />
53
+ );
54
+ },
55
+ };
package/mdx.ts ADDED
@@ -0,0 +1,138 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import matter from "gray-matter";
4
+ import { MDXRemote } from "next-mdx-remote/rsc";
5
+ import { mdxComponents } from "./mdx/components";
6
+ import { MDXComponents } from "mdx/types";
7
+ import { ReactDocsConfig } from "./config";
8
+
9
+ interface Heading {
10
+ id: string;
11
+ text: string;
12
+ level: number;
13
+ }
14
+
15
+ function slugify(str: string) {
16
+ return String(str)
17
+ .toLowerCase()
18
+ .trim()
19
+ .replace(/[^\w\s-]/g, '')
20
+ .replace(/[\s_-]+/g, '-')
21
+ .replace(/^-+|-+$/g, '');
22
+ }
23
+
24
+ function extractHeadings(source: string): Heading[] {
25
+ const headings: Heading[] = [];
26
+ const lines = source.split("\n");
27
+ let inCodeBlock = false;
28
+
29
+ for (let i = 0; i < lines.length; i++) {
30
+ const line = lines[i];
31
+
32
+ // Check for code block delimiters
33
+ if (line.trim().startsWith("```")) {
34
+ inCodeBlock = !inCodeBlock;
35
+ continue;
36
+ }
37
+
38
+ // Skip if we're inside a code block
39
+ if (inCodeBlock) continue;
40
+
41
+ // Match headings, but only if they're not inside a code block
42
+ const headingMatch = line.match(/^(#{1,3})\s+(.+)$/);
43
+ if (headingMatch) {
44
+ const level = headingMatch[1].length;
45
+ const text = headingMatch[2];
46
+ const id = slugify(text);
47
+
48
+ headings.push({ level, text, id });
49
+ }
50
+ }
51
+
52
+ return headings;
53
+ }
54
+
55
+ export async function getDocBySlug(docsConfig: ReactDocsConfig, slug: string) {
56
+ const directory = path.join(process.cwd(), docsConfig.contentPath);
57
+ const fullPath = path.join(directory, `${slug}.mdx`);
58
+ const fileContents = fs.readFileSync(fullPath, "utf8");
59
+ const { data: frontmatter, content: source } = matter(fileContents);
60
+ const headings = extractHeadings(source);
61
+
62
+ return {
63
+ frontmatter,
64
+ content: await MDXRemote({
65
+ source,
66
+ components: mdxComponents as MDXComponents,
67
+ }),
68
+ headings,
69
+ };
70
+ }
71
+
72
+ export function getAllDocs(docsConfig: ReactDocsConfig) {
73
+ const docs: { slug: string; frontmatter: any }[] = [];
74
+
75
+ function traverseDirectory(currentPath: string, baseDir: string) {
76
+ const files = fs.readdirSync(currentPath);
77
+
78
+ for (const file of files) {
79
+ const fullPath = path.join(currentPath, file);
80
+ const stat = fs.statSync(fullPath);
81
+
82
+ if (stat.isDirectory()) {
83
+ traverseDirectory(fullPath, baseDir);
84
+ } else if (file.endsWith(".mdx")) {
85
+ const relativePath = path.relative(baseDir, fullPath);
86
+ const slug = relativePath.replace(/\.mdx$/, "");
87
+ const fileContents = fs.readFileSync(fullPath, "utf8");
88
+ const { data: frontmatter } = matter(fileContents);
89
+
90
+ docs.push({
91
+ slug,
92
+ frontmatter,
93
+ });
94
+ }
95
+ }
96
+ }
97
+
98
+ const docsDirectory = path.join(process.cwd(), docsConfig.contentPath);
99
+ traverseDirectory(docsDirectory, docsDirectory);
100
+ return docs;
101
+ }
102
+
103
+ // Cache for docs content
104
+ const docsContentCache = new Map<string, string>();
105
+
106
+ export function getAllDocsContent(docsConfig: ReactDocsConfig): string {
107
+ const cacheKey = docsConfig.contentPath;
108
+
109
+ if (docsContentCache.has(cacheKey)) {
110
+ return docsContentCache.get(cacheKey)!;
111
+ }
112
+
113
+ const content: string[] = [];
114
+
115
+ function traverseDirectory(currentPath: string) {
116
+ const files = fs.readdirSync(currentPath);
117
+
118
+ for (const file of files) {
119
+ const fullPath = path.join(currentPath, file);
120
+ const stat = fs.statSync(fullPath);
121
+
122
+ if (stat.isDirectory()) {
123
+ traverseDirectory(fullPath);
124
+ } else if (file.endsWith(".mdx")) {
125
+ const fileContents = fs.readFileSync(fullPath, "utf8");
126
+ const { content: mdxContent } = matter(fileContents);
127
+ content.push(mdxContent);
128
+ }
129
+ }
130
+ }
131
+
132
+ const docsDirectory = path.join(process.cwd(), docsConfig.contentPath);
133
+ traverseDirectory(docsDirectory);
134
+
135
+ const joinedContent = content.join('\n\n---\n\n');
136
+ docsContentCache.set(cacheKey, joinedContent);
137
+ return joinedContent;
138
+ }
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "react-docs-module",
3
+ "version": "0.1.0",
4
+ "description": "Embeddable documentation page with search and AI chat",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "development": "./index.ts",
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./config": {
15
+ "development": "./config.ts",
16
+ "types": "./dist/config.d.ts",
17
+ "import": "./dist/config.js"
18
+ },
19
+ "./search": {
20
+ "development": "./search.tsx",
21
+ "types": "./dist/search.d.ts",
22
+ "import": "./dist/search.js"
23
+ },
24
+ "./chat-api": {
25
+ "development": "./chat-api.ts",
26
+ "types": "./dist/chat-api.d.ts",
27
+ "import": "./dist/chat-api.js"
28
+ },
29
+ "./mdx": {
30
+ "development": "./mdx.ts",
31
+ "types": "./dist/mdx.d.ts",
32
+ "import": "./dist/mdx.js"
33
+ },
34
+ "./utils": {
35
+ "development": "./util.ts",
36
+ "types": "./dist/util.d.ts",
37
+ "import": "./dist/util.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "*.ts",
43
+ "*.tsx",
44
+ "mdx/",
45
+ "ui/",
46
+ "README.md"
47
+ ],
48
+ "peerDependencies": {
49
+ "react": "^19.1.0",
50
+ "react-dom": "^19.1.0",
51
+ "@ai-sdk/anthropic": "^2.0.0",
52
+ "@ai-sdk/groq": "^1.2.9",
53
+ "@ai-sdk/openai": "^2.0.0",
54
+ "@radix-ui/react-dialog": "^1.1.4",
55
+ "@radix-ui/react-slot": "^1.1.0",
56
+ "ai": "^4.3.19",
57
+ "class-variance-authority": "^0.7.0",
58
+ "clsx": "^2.1.1",
59
+ "gray-matter": "^4.0.3",
60
+ "lucide-react": "^0.456.0",
61
+ "next-mdx-remote": "^5.0.0",
62
+ "react-markdown": "^9.1.0",
63
+ "react-syntax-highlighter": "^15.6.1",
64
+ "tailwind-merge": "^2.5.4",
65
+ "tailwindcss-animate": "^1.0.7"
66
+ },
67
+ "devDependencies": {
68
+ "@types/react": "^19.1.1",
69
+ "@types/react-dom": "^19.1.2",
70
+ "@types/react-syntax-highlighter": "^15.5.13",
71
+ "@types/node": "^22.10.2",
72
+ "typescript": "^5.6.3"
73
+ },
74
+ "keywords": [
75
+ "documentation",
76
+ "mdx",
77
+ "search",
78
+ "ai-chat",
79
+ "react",
80
+ "typescript",
81
+ "mintlify"
82
+ ],
83
+ "author": "Ivan Chebykin",
84
+ "license": "MIT",
85
+ "readme": "README.md",
86
+ "homepage": "https://github.com/sourcewizard-ai/react-docs-module",
87
+ "repository": {
88
+ "type": "git",
89
+ "url": "https://github.com/sourcewizard-ai/react-docs-module.git"
90
+ },
91
+ "bugs": {
92
+ "url": "https://github.com/sourcewizard-ai/react-docs-module/issues"
93
+ },
94
+ "scripts": {
95
+ "build": "tsc --build",
96
+ "clean": "rm -rf dist",
97
+ "typecheck": "tsc --noEmit --skipLibCheck"
98
+ }
99
+ }
@@ -0,0 +1,52 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import matter from "gray-matter";
4
+ import { ReactDocsConfig } from "./config";
5
+
6
+ export interface SearchResult {
7
+ title: string;
8
+ content: string;
9
+ url: string;
10
+ snippet?: string;
11
+ score?: number;
12
+ }
13
+
14
+ export function buildSearchIndex(config: ReactDocsConfig): SearchResult[] {
15
+ const docsDirectory = path.join(process.cwd(), config.contentPath);
16
+ const searchIndex: SearchResult[] = [];
17
+
18
+ function processDirectory(dir: string, baseUrl: string = "") {
19
+ const items = fs.readdirSync(dir);
20
+
21
+ items.forEach((item) => {
22
+ const fullPath = path.join(dir, item);
23
+ const stat = fs.statSync(fullPath);
24
+
25
+ if (stat.isDirectory()) {
26
+ processDirectory(fullPath, `${baseUrl}${item}/`);
27
+ } else if (item.endsWith(".mdx")) {
28
+ const fileContents = fs.readFileSync(fullPath, "utf8");
29
+ const { data: frontmatter, content } = matter(fileContents);
30
+ const slug = item.replace(/\.mdx$/, "");
31
+ const url = config.basePath + `/${baseUrl}${slug}`;
32
+
33
+ // Clean up the content by removing markdown syntax
34
+ const cleanContent = content
35
+ .replace(/```[\s\S]*?```/g, "") // Remove code blocks
36
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // Remove markdown links but keep text
37
+ .replace(/[#*`_]/g, "") // Remove markdown syntax
38
+ .replace(/\n+/g, " ") // Replace newlines with spaces
39
+ .trim();
40
+
41
+ searchIndex.push({
42
+ title: frontmatter.title || slug,
43
+ content: cleanContent,
44
+ url,
45
+ });
46
+ }
47
+ });
48
+ }
49
+
50
+ processDirectory(docsDirectory);
51
+ return searchIndex;
52
+ }