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.
- package/README.md +279 -0
- package/ai-chat.tsx +222 -0
- package/chat-api.ts +90 -0
- package/cn.ts +15 -0
- package/config.ts +29 -0
- package/dist/ai-chat.d.ts +12 -0
- package/dist/ai-chat.js +72 -0
- package/dist/ai-chat.js.map +1 -0
- package/dist/chat-api.d.ts +16 -0
- package/dist/chat-api.js +62 -0
- package/dist/chat-api.js.map +1 -0
- package/dist/cn.d.ts +4 -0
- package/dist/cn.js +14 -0
- package/dist/cn.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +15 -0
- package/dist/config.js.map +1 -0
- package/dist/doc-pagination.d.ts +13 -0
- package/dist/doc-pagination.js +8 -0
- package/dist/doc-pagination.js.map +1 -0
- package/dist/docs-index.d.ts +7 -0
- package/dist/docs-index.js +11 -0
- package/dist/docs-index.js.map +1 -0
- package/dist/docs-page.d.ts +15 -0
- package/dist/docs-page.js +38 -0
- package/dist/docs-page.js.map +1 -0
- package/dist/docs-sidebar.d.ts +18 -0
- package/dist/docs-sidebar.d.ts.map +1 -0
- package/dist/docs-sidebar.js +27 -0
- package/dist/docs-sidebar.js.map +1 -0
- package/dist/documentation-layout.d.ts +15 -0
- package/dist/documentation-layout.js +20 -0
- package/dist/documentation-layout.js.map +1 -0
- package/dist/heading.d.ts +10 -0
- package/dist/heading.js +16 -0
- package/dist/heading.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/mdx/callouts.d.ts +8 -0
- package/dist/mdx/callouts.js +8 -0
- package/dist/mdx/callouts.js.map +1 -0
- package/dist/mdx/code-block.d.ts +8 -0
- package/dist/mdx/code-block.js +29 -0
- package/dist/mdx/code-block.js.map +1 -0
- package/dist/mdx/components.d.ts +13 -0
- package/dist/mdx/components.js +21 -0
- package/dist/mdx/components.js.map +1 -0
- package/dist/mdx.d.ts +20 -0
- package/dist/mdx.js +109 -0
- package/dist/mdx.js.map +1 -0
- package/dist/search-index.d.ts +10 -0
- package/dist/search-index.js +38 -0
- package/dist/search-index.js.map +1 -0
- package/dist/search.d.ts +6 -0
- package/dist/search.js +142 -0
- package/dist/search.js.map +1 -0
- package/dist/table-of-contents-provider.d.ts +4 -0
- package/dist/table-of-contents-provider.js +30 -0
- package/dist/table-of-contents-provider.js.map +1 -0
- package/dist/table-of-contents.d.ts +11 -0
- package/dist/table-of-contents.js +9 -0
- package/dist/table-of-contents.js.map +1 -0
- package/dist/theme-context.d.ts +20 -0
- package/dist/theme-context.js +28 -0
- package/dist/theme-context.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/ui/button.d.ts +12 -0
- package/dist/ui/button.js +34 -0
- package/dist/ui/button.js.map +1 -0
- package/dist/ui/dialog.d.ts +17 -0
- package/dist/ui/dialog.js +22 -0
- package/dist/ui/dialog.js.map +1 -0
- package/dist/ui/input.d.ts +4 -0
- package/dist/ui/input.js +9 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/util.d.ts +59 -0
- package/dist/util.js +96 -0
- package/dist/util.js.map +1 -0
- package/doc-pagination.tsx +67 -0
- package/docs-index.tsx +17 -0
- package/docs-page.tsx +68 -0
- package/docs-sidebar.tsx +165 -0
- package/documentation-layout.tsx +99 -0
- package/heading.tsx +63 -0
- package/index.ts +28 -0
- package/mdx/callouts.tsx +29 -0
- package/mdx/code-block.tsx +89 -0
- package/mdx/components.tsx +55 -0
- package/mdx.ts +138 -0
- package/package.json +99 -0
- package/search-index.ts +52 -0
- package/search.tsx +273 -0
- package/table-of-contents-provider.tsx +43 -0
- package/table-of-contents.tsx +44 -0
- package/theme-context.tsx +57 -0
- package/ui/button.tsx +56 -0
- package/ui/dialog.tsx +108 -0
- package/ui/input.tsx +22 -0
- package/util.ts +169 -0
package/README.md
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
# React Docs Module
|
2
|
+
|
3
|
+
A powerful, embeddable documentation system with built-in search and AI chat capabilities. Perfect for creating beautiful, interactive documentation pages with minimal setup.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 🎨 **Themeable** - Customize colors and appearance via `docs.json` configuration
|
8
|
+
- 🔍 **Full-text Search** - Fast, client-side search with caching
|
9
|
+
- 🤖 **AI Chat** - Integrated AI assistant for documentation Q&A
|
10
|
+
- 📱 **Responsive** - Mobile-friendly design with adaptive layouts
|
11
|
+
- ⚡ **Fast** - Built with Next.js and optimized for performance
|
12
|
+
- 🎯 **TypeScript** - Full type safety and excellent DX
|
13
|
+
- 📝 **MDX Support** - Rich content with React components
|
14
|
+
- 📋 **Mintlify Compatible** - Full support for Mintlify docs.json schema
|
15
|
+
|
16
|
+
## Quick Start
|
17
|
+
|
18
|
+
### 1. Installation
|
19
|
+
|
20
|
+
```bash
|
21
|
+
npm install react-docs-module
|
22
|
+
```
|
23
|
+
|
24
|
+
### 2. Configuration
|
25
|
+
|
26
|
+
Create a `docs.json` file with your documentation configuration using the **Mintlify schema**:
|
27
|
+
|
28
|
+
```json
|
29
|
+
{
|
30
|
+
"$schema": "https://leaves.mintlify.com/schema/docs.json",
|
31
|
+
"theme": "mint",
|
32
|
+
"name": "My Documentation",
|
33
|
+
"description": "Comprehensive documentation for my project",
|
34
|
+
"logo": {
|
35
|
+
"dark": "/assets/logo-dark.png",
|
36
|
+
"light": "/assets/logo-light.png"
|
37
|
+
},
|
38
|
+
"favicon": "/assets/favicon.png",
|
39
|
+
"colors": {
|
40
|
+
"primary": "#3B82F6",
|
41
|
+
"light": "#60A5FA",
|
42
|
+
"dark": "#1D4ED8"
|
43
|
+
},
|
44
|
+
"navbar": {
|
45
|
+
"cta": {
|
46
|
+
"type": "github",
|
47
|
+
"url": "https://github.com/your-org/your-repo"
|
48
|
+
}
|
49
|
+
},
|
50
|
+
"navigation": {
|
51
|
+
"groups": [
|
52
|
+
{
|
53
|
+
"group": "Getting Started",
|
54
|
+
"pages": [
|
55
|
+
"introduction",
|
56
|
+
"installation",
|
57
|
+
"quick-start"
|
58
|
+
]
|
59
|
+
},
|
60
|
+
{
|
61
|
+
"group": "API Reference",
|
62
|
+
"pages": [
|
63
|
+
"api/overview",
|
64
|
+
"api/authentication"
|
65
|
+
]
|
66
|
+
}
|
67
|
+
]
|
68
|
+
},
|
69
|
+
"footer": {
|
70
|
+
"socials": {
|
71
|
+
"github": "https://github.com/your-org/your-repo",
|
72
|
+
"twitter": "https://twitter.com/your-handle"
|
73
|
+
}
|
74
|
+
},
|
75
|
+
"feedback": {
|
76
|
+
"thumbsRating": true
|
77
|
+
}
|
78
|
+
}
|
79
|
+
```
|
80
|
+
|
81
|
+
### 3. Basic Usage
|
82
|
+
|
83
|
+
```tsx
|
84
|
+
import { DocumentationLayout, createReactDocsConfig } from 'react-docs-module';
|
85
|
+
|
86
|
+
// Create config with defaults or custom overrides
|
87
|
+
const config = createReactDocsConfig({
|
88
|
+
basePath: "/docs",
|
89
|
+
contentPath: "content/docs",
|
90
|
+
// searchApiPath and aiChatApiPath use defaults
|
91
|
+
});
|
92
|
+
|
93
|
+
export default function DocsPage() {
|
94
|
+
return (
|
95
|
+
<DocumentationLayout
|
96
|
+
config={config}
|
97
|
+
navigation={navigation}
|
98
|
+
currentPath="/docs/introduction"
|
99
|
+
headings={headings}
|
100
|
+
>
|
101
|
+
<div>Your documentation content here</div>
|
102
|
+
</DocumentationLayout>
|
103
|
+
);
|
104
|
+
}
|
105
|
+
```
|
106
|
+
|
107
|
+
## Configuration
|
108
|
+
|
109
|
+
### ReactDocsConfig
|
110
|
+
|
111
|
+
```typescript
|
112
|
+
interface ReactDocsConfig {
|
113
|
+
basePath: string; // Base URL path for docs
|
114
|
+
contentPath: string; // Path to MDX content files
|
115
|
+
searchApiPath: string; // API endpoint for search index
|
116
|
+
aiChatApiPath: string; // API endpoint for AI chat
|
117
|
+
}
|
118
|
+
|
119
|
+
// Factory function with optional overrides
|
120
|
+
function createReactDocsConfig(options?: ReactDocsConfigOptions): ReactDocsConfig
|
121
|
+
|
122
|
+
interface ReactDocsConfigOptions {
|
123
|
+
basePath?: string; // Default: "/docs"
|
124
|
+
contentPath?: string; // Default: "content/docs"
|
125
|
+
searchApiPath?: string; // Default: "/api/docs/search-index"
|
126
|
+
aiChatApiPath?: string; // Default: "/api/docs/chat"
|
127
|
+
}
|
128
|
+
```
|
129
|
+
|
130
|
+
### Configuration Examples
|
131
|
+
|
132
|
+
```typescript
|
133
|
+
// Use all defaults
|
134
|
+
const config = createReactDocsConfig();
|
135
|
+
|
136
|
+
// Override specific fields
|
137
|
+
const config = createReactDocsConfig({
|
138
|
+
basePath: "/documentation",
|
139
|
+
aiChatApiPath: "/api/custom-chat"
|
140
|
+
});
|
141
|
+
|
142
|
+
// Override all fields
|
143
|
+
const config = createReactDocsConfig({
|
144
|
+
basePath: "/my-docs",
|
145
|
+
contentPath: "docs",
|
146
|
+
searchApiPath: "/api/search",
|
147
|
+
aiChatApiPath: "/api/ai-chat"
|
148
|
+
});
|
149
|
+
```
|
150
|
+
|
151
|
+
### docs.json Schema (Mintlify Compatible)
|
152
|
+
|
153
|
+
The `docs.json` file uses the complete **[Mintlify schema](https://leaves.mintlify.com/schema/docs.json)** for full compatibility:
|
154
|
+
|
155
|
+
- **Required**: `theme`, `name`, `colors.primary`, `navigation`
|
156
|
+
- **Optional**: `description`, `logo`, `favicon`, `navbar`, `footer`, `feedback`, and more
|
157
|
+
- **Migration**: Existing Mintlify projects can use their `mint.json` structure in `docs.json`
|
158
|
+
|
159
|
+
## Components
|
160
|
+
|
161
|
+
### Core Components
|
162
|
+
|
163
|
+
- `DocumentationLayout` - Main layout wrapper with sidebar and TOC
|
164
|
+
- `Search` - Search component with AI chat integration
|
165
|
+
- `DocsIndex` - Documentation index/landing page
|
166
|
+
- `DocsSidebar` - Navigation sidebar
|
167
|
+
- `TableOfContents` - Page table of contents
|
168
|
+
- `DocPagination` - Previous/next page navigation
|
169
|
+
|
170
|
+
### Theme System
|
171
|
+
|
172
|
+
```tsx
|
173
|
+
import { DocsThemeProvider, useDocsColors } from 'react-docs-module';
|
174
|
+
|
175
|
+
function CustomComponent() {
|
176
|
+
const colors = useDocsColors();
|
177
|
+
|
178
|
+
return (
|
179
|
+
<div style={{ color: colors.primary }}>
|
180
|
+
Themed content
|
181
|
+
</div>
|
182
|
+
);
|
183
|
+
}
|
184
|
+
```
|
185
|
+
|
186
|
+
### AI Chat Integration
|
187
|
+
|
188
|
+
```tsx
|
189
|
+
import { streamDocsChatResponse } from 'react-docs-module/chat-api';
|
190
|
+
|
191
|
+
// API route example
|
192
|
+
export async function POST(req: Request) {
|
193
|
+
const { messages } = await req.json();
|
194
|
+
|
195
|
+
return streamDocsChatResponse(
|
196
|
+
config,
|
197
|
+
messages,
|
198
|
+
"You are a helpful documentation assistant.",
|
199
|
+
async (result) => {
|
200
|
+
// Handle completion
|
201
|
+
console.log(result.text);
|
202
|
+
}
|
203
|
+
);
|
204
|
+
}
|
205
|
+
```
|
206
|
+
|
207
|
+
## API Reference
|
208
|
+
|
209
|
+
### Functions
|
210
|
+
|
211
|
+
- `getDocsSidebar(config)` - Generate sidebar navigation from docs.json
|
212
|
+
- `getAllDocs(config)` - Get all documentation files metadata
|
213
|
+
- `getAllDocsContent(config)` - Get all documentation content (cached)
|
214
|
+
- `getDocBySlug(config, slug)` - Get specific document by slug
|
215
|
+
- `buildSearchIndex(config)` - Build search index from content
|
216
|
+
- `streamDocsChatResponse(...)` - Stream AI chat responses
|
217
|
+
|
218
|
+
### Types
|
219
|
+
|
220
|
+
- `ReactDocsConfig` - Main configuration interface
|
221
|
+
- `ReactDocsConfigOptions` - Configuration factory options
|
222
|
+
- `DocsJsonConfig` - docs.json configuration schema (Mintlify compatible)
|
223
|
+
- `ThemeColors` - Color theme interface
|
224
|
+
- `ModelProvider` - AI model provider configuration
|
225
|
+
|
226
|
+
## Advanced Usage
|
227
|
+
|
228
|
+
### Custom Search API
|
229
|
+
|
230
|
+
```typescript
|
231
|
+
// pages/api/search-index.ts
|
232
|
+
import { buildSearchIndex, createReactDocsConfig } from 'react-docs-module';
|
233
|
+
|
234
|
+
const DOCS_CONFIG = createReactDocsConfig({
|
235
|
+
contentPath: "content/docs"
|
236
|
+
});
|
237
|
+
|
238
|
+
export default async function handler(req, res) {
|
239
|
+
const searchIndex = await buildSearchIndex(DOCS_CONFIG);
|
240
|
+
res.json(searchIndex);
|
241
|
+
}
|
242
|
+
```
|
243
|
+
|
244
|
+
### Custom AI Models
|
245
|
+
|
246
|
+
```typescript
|
247
|
+
const modelProvider: ModelProvider = {
|
248
|
+
providerType: 'anthropic',
|
249
|
+
model: 'claude-3-sonnet',
|
250
|
+
apiKey: process.env.ANTHROPIC_API_KEY
|
251
|
+
};
|
252
|
+
|
253
|
+
streamDocsChatResponse(config, messages, prompt, callback, modelProvider);
|
254
|
+
```
|
255
|
+
|
256
|
+
### Custom Theming
|
257
|
+
|
258
|
+
```css
|
259
|
+
/* Use CSS custom properties set by DocsThemeProvider */
|
260
|
+
.custom-element {
|
261
|
+
color: var(--docs-primary);
|
262
|
+
border-color: var(--docs-primary-light);
|
263
|
+
background-color: var(--docs-primary-dark);
|
264
|
+
}
|
265
|
+
```
|
266
|
+
|
267
|
+
## Requirements
|
268
|
+
|
269
|
+
- React 19+
|
270
|
+
- TypeScript 5.6+
|
271
|
+
- Node.js 18+
|
272
|
+
|
273
|
+
## License
|
274
|
+
|
275
|
+
MIT
|
276
|
+
|
277
|
+
## Contributing
|
278
|
+
|
279
|
+
Contributions welcome! Please read our contributing guidelines and submit pull requests to the main repository.
|
package/ai-chat.tsx
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
"use client";
|
2
|
+
import React, { useEffect, useRef, useCallback } from "react";
|
3
|
+
import { Button } from "./ui/button";
|
4
|
+
import { Input } from "./ui/input";
|
5
|
+
import { useChat } from 'ai/react';
|
6
|
+
import ReactMarkdown from 'react-markdown';
|
7
|
+
import { CodeBlock } from "./mdx/code-block";
|
8
|
+
import { ReactDocsConfig } from "./config";
|
9
|
+
import { useDocsColors } from "./theme-context";
|
10
|
+
|
11
|
+
interface Message {
|
12
|
+
role: 'user' | 'assistant' | 'system' | 'data';
|
13
|
+
content: string;
|
14
|
+
id?: string;
|
15
|
+
}
|
16
|
+
|
17
|
+
const MemoizedMarkdown = React.memo(({ content }: { content: string }) => (
|
18
|
+
<ReactMarkdown
|
19
|
+
className="w-full [&_pre]:max-w-full [&_pre]:overflow-x-auto [&_p]:leading-relaxed"
|
20
|
+
components={{
|
21
|
+
code({ node, className, children, ...props }) {
|
22
|
+
const match = /language-(\w+)/.exec(className || '');
|
23
|
+
if (match) {
|
24
|
+
const codeContent = Array.isArray(children)
|
25
|
+
? children.join('')
|
26
|
+
: String(children);
|
27
|
+
|
28
|
+
return (
|
29
|
+
<div className="max-w-full overflow-auto">
|
30
|
+
<CodeBlock
|
31
|
+
{...props}
|
32
|
+
className={className}
|
33
|
+
>
|
34
|
+
{codeContent}
|
35
|
+
</CodeBlock>
|
36
|
+
</div>
|
37
|
+
);
|
38
|
+
}
|
39
|
+
return (
|
40
|
+
<code {...props} className="bg-gray-800/50 px-1.5 py-0.5 rounded text-sm font-mono">
|
41
|
+
{children}
|
42
|
+
</code>
|
43
|
+
);
|
44
|
+
},
|
45
|
+
}}
|
46
|
+
>
|
47
|
+
{content}
|
48
|
+
</ReactMarkdown>
|
49
|
+
));
|
50
|
+
|
51
|
+
MemoizedMarkdown.displayName = 'MemoizedMarkdown';
|
52
|
+
|
53
|
+
const Message = React.memo(({ message }: { message: Message }) => (
|
54
|
+
<div className="rounded-lg bg-gray-800/50 p-4">
|
55
|
+
<div className="flex gap-3 items-start">
|
56
|
+
<div className={`p-2 rounded-md ${message.role === 'assistant'
|
57
|
+
? 'bg-blue-500/10 text-blue-400'
|
58
|
+
: 'bg-gray-700/50 text-gray-400'
|
59
|
+
} mt-0.5`}>
|
60
|
+
{message.role === 'assistant' ? (
|
61
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
62
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
63
|
+
</svg>
|
64
|
+
) : (
|
65
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
66
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
67
|
+
<circle cx="12" cy="7" r="4" />
|
68
|
+
</svg>
|
69
|
+
)}
|
70
|
+
</div>
|
71
|
+
<div className="flex-1 space-y-2 min-w-0">
|
72
|
+
<div className="text-sm text-white leading-relaxed">
|
73
|
+
<MemoizedMarkdown content={message.content} />
|
74
|
+
</div>
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
));
|
79
|
+
|
80
|
+
Message.displayName = 'Message';
|
81
|
+
|
82
|
+
export const AIChatOption = React.memo(({ query, onClick }: { query: string; onClick: () => void }) => {
|
83
|
+
const colors = useDocsColors();
|
84
|
+
|
85
|
+
return (
|
86
|
+
<button
|
87
|
+
className="block w-full text-left p-4 rounded-lg hover:bg-gray-800/50 transition-colors border border-gray-800"
|
88
|
+
onClick={onClick}
|
89
|
+
>
|
90
|
+
<div className="flex items-center gap-3">
|
91
|
+
<div
|
92
|
+
className="p-2 rounded-md"
|
93
|
+
style={{
|
94
|
+
backgroundColor: `${colors.primary}10`,
|
95
|
+
color: colors.primary,
|
96
|
+
}}
|
97
|
+
>
|
98
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
99
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
100
|
+
</svg>
|
101
|
+
</div>
|
102
|
+
<div>
|
103
|
+
<h3 className="text-sm font-medium text-white mb-1">Ask AI (Experimental)</h3>
|
104
|
+
<p className="text-sm text-gray-400">"{query}"</p>
|
105
|
+
</div>
|
106
|
+
</div>
|
107
|
+
</button>
|
108
|
+
);
|
109
|
+
});
|
110
|
+
|
111
|
+
AIChatOption.displayName = 'AIChatOption';
|
112
|
+
|
113
|
+
export function AIChat({ query, onBack, config }: { query: string; onBack: () => void; config: ReactDocsConfig }) {
|
114
|
+
const colors = useDocsColors();
|
115
|
+
const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
|
116
|
+
initialMessages: [],
|
117
|
+
api: config.aiChatApiPath,
|
118
|
+
id: query,
|
119
|
+
initialInput: query,
|
120
|
+
});
|
121
|
+
|
122
|
+
const messagesEndRef = useRef<HTMLDivElement>(null);
|
123
|
+
const hasSubmittedInitialMessage = useRef(false);
|
124
|
+
|
125
|
+
const scrollToBottom = useCallback(() => {
|
126
|
+
if (messagesEndRef.current) {
|
127
|
+
const behavior = messages.length <= 2 ? "auto" : "smooth";
|
128
|
+
messagesEndRef.current.scrollIntoView({ behavior });
|
129
|
+
}
|
130
|
+
}, [messages.length]);
|
131
|
+
|
132
|
+
useEffect(() => {
|
133
|
+
scrollToBottom();
|
134
|
+
}, [scrollToBottom]);
|
135
|
+
|
136
|
+
useEffect(() => {
|
137
|
+
if (!hasSubmittedInitialMessage.current) {
|
138
|
+
const event = new Event('submit');
|
139
|
+
Object.defineProperty(event, 'target', { value: { value: query } });
|
140
|
+
handleSubmit(event as any);
|
141
|
+
hasSubmittedInitialMessage.current = true;
|
142
|
+
}
|
143
|
+
}, [query, handleSubmit]);
|
144
|
+
|
145
|
+
useEffect(() => {
|
146
|
+
if (error) {
|
147
|
+
console.error('Chat API Error:', error);
|
148
|
+
}
|
149
|
+
}, [error]);
|
150
|
+
|
151
|
+
return (
|
152
|
+
<div className="space-y-4">
|
153
|
+
<div className="flex items-center gap-2">
|
154
|
+
<button
|
155
|
+
onClick={onBack}
|
156
|
+
className="p-1 hover:bg-gray-800 rounded-md text-gray-400 hover:text-white transition-colors"
|
157
|
+
>
|
158
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
159
|
+
<path d="M19 12H5M12 19l-7-7 7-7" />
|
160
|
+
</svg>
|
161
|
+
</button>
|
162
|
+
<h3 className="text-sm font-medium text-white">Ask AI (Experimental)</h3>
|
163
|
+
</div>
|
164
|
+
|
165
|
+
<div className="space-y-4 max-h-[50vh] overflow-y-auto">
|
166
|
+
{error && (
|
167
|
+
<div className="rounded-lg bg-red-900/50 border border-red-900 p-4">
|
168
|
+
<div className="text-sm text-red-400">
|
169
|
+
Error: Failed to send message. Please try again.
|
170
|
+
</div>
|
171
|
+
</div>
|
172
|
+
)}
|
173
|
+
{messages.map((message) => (
|
174
|
+
<Message key={message.id} message={message} />
|
175
|
+
))}
|
176
|
+
{isLoading && (
|
177
|
+
<div className="rounded-lg bg-gray-800/50 p-4">
|
178
|
+
<div className="flex gap-3 items-start">
|
179
|
+
<div className="p-2 rounded-md bg-blue-500/10 text-blue-400 mt-0.5">
|
180
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
181
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
182
|
+
</svg>
|
183
|
+
</div>
|
184
|
+
<div className="flex-1">
|
185
|
+
<div className="flex items-center gap-2 text-sm text-white">
|
186
|
+
<div className="animate-pulse">Thinking</div>
|
187
|
+
<div className="flex gap-1">
|
188
|
+
<span className="animate-bounce">.</span>
|
189
|
+
<span className="animate-bounce" style={{ animationDelay: "0.2s" }}>.</span>
|
190
|
+
<span className="animate-bounce" style={{ animationDelay: "0.4s" }}>.</span>
|
191
|
+
</div>
|
192
|
+
</div>
|
193
|
+
</div>
|
194
|
+
</div>
|
195
|
+
</div>
|
196
|
+
)}
|
197
|
+
<div ref={messagesEndRef} />
|
198
|
+
</div>
|
199
|
+
|
200
|
+
<form onSubmit={handleSubmit} className="relative mt-4">
|
201
|
+
<Input
|
202
|
+
value={input}
|
203
|
+
onChange={handleInputChange}
|
204
|
+
placeholder="Ask a follow-up question..."
|
205
|
+
className="w-full bg-gray-800/50 border-gray-700"
|
206
|
+
disabled={isLoading}
|
207
|
+
/>
|
208
|
+
<Button
|
209
|
+
type="submit"
|
210
|
+
disabled={isLoading || !input.trim()}
|
211
|
+
className="absolute right-1 top-1 h-8"
|
212
|
+
style={{
|
213
|
+
backgroundColor: colors.primary,
|
214
|
+
'--tw-bg-opacity': '1',
|
215
|
+
} as React.CSSProperties}
|
216
|
+
>
|
217
|
+
Send
|
218
|
+
</Button>
|
219
|
+
</form>
|
220
|
+
</div>
|
221
|
+
);
|
222
|
+
}
|
package/chat-api.ts
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
import { createGroq } from '@ai-sdk/groq';
|
2
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
3
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
4
|
+
import { streamText } from 'ai';
|
5
|
+
import { getAllDocsContent } from './mdx';
|
6
|
+
import { ReactDocsConfig } from './config';
|
7
|
+
|
8
|
+
export interface ModelProvider {
|
9
|
+
providerType: 'groq' | 'anthropic' | 'openai';
|
10
|
+
model?: string;
|
11
|
+
baseURL?: string;
|
12
|
+
apiKey?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface Message {
|
16
|
+
role: 'user' | 'assistant' | 'system' | 'data';
|
17
|
+
content: string;
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface FinishResult {
|
21
|
+
text: string;
|
22
|
+
}
|
23
|
+
|
24
|
+
export async function streamDocsChatResponse(
|
25
|
+
config: ReactDocsConfig,
|
26
|
+
messages: Message[],
|
27
|
+
systemPrompt?: string,
|
28
|
+
onFinishCallback?: (result: FinishResult) => Promise<void>,
|
29
|
+
modelProvider?: ModelProvider
|
30
|
+
) {
|
31
|
+
// Default to groq if no provider specified
|
32
|
+
const provider = modelProvider || {
|
33
|
+
providerType: 'groq',
|
34
|
+
model: 'llama-3.3-70b-versatile'
|
35
|
+
};
|
36
|
+
|
37
|
+
// Create the appropriate model based on provider type
|
38
|
+
let model;
|
39
|
+
switch (provider.providerType) {
|
40
|
+
case 'anthropic':
|
41
|
+
const anthropicProvider = createAnthropic({
|
42
|
+
baseURL: provider.baseURL,
|
43
|
+
apiKey: provider.apiKey,
|
44
|
+
});
|
45
|
+
model = anthropicProvider(provider.model || 'claude-3-5-sonnet-20241022');
|
46
|
+
break;
|
47
|
+
case 'openai':
|
48
|
+
const openaiProvider = createOpenAI({
|
49
|
+
baseURL: provider.baseURL,
|
50
|
+
apiKey: provider.apiKey,
|
51
|
+
});
|
52
|
+
model = openaiProvider(provider.model || 'gpt-4o');
|
53
|
+
break;
|
54
|
+
case 'groq':
|
55
|
+
default:
|
56
|
+
const groqProvider = createGroq({
|
57
|
+
baseURL: provider.baseURL,
|
58
|
+
apiKey: provider.apiKey,
|
59
|
+
});
|
60
|
+
model = groqProvider(provider.model || 'llama-3.3-70b-versatile');
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
|
64
|
+
const docsSystemPrompt = systemPrompt || `You are a helpful assistant for the provided documentation knowledge base.
|
65
|
+
Based on user questions you should be able to help them by providing answers based on the knowledge base only.
|
66
|
+
Keep your responses concise and focused.`;
|
67
|
+
|
68
|
+
const docsContent = getAllDocsContent(config);
|
69
|
+
|
70
|
+
const stream = streamText({
|
71
|
+
// Type assertion needed due to AI SDK version compatibility between LanguageModelV1/V2
|
72
|
+
model: model as Parameters<typeof streamText>[0]['model'],
|
73
|
+
onFinish: async (result) => {
|
74
|
+
if (onFinishCallback) {
|
75
|
+
await onFinishCallback({ text: result.text });
|
76
|
+
}
|
77
|
+
},
|
78
|
+
messages: [
|
79
|
+
{
|
80
|
+
role: 'system',
|
81
|
+
content: `${docsSystemPrompt}
|
82
|
+
Documentation Knowledge Base:
|
83
|
+
${docsContent}`,
|
84
|
+
},
|
85
|
+
...messages,
|
86
|
+
],
|
87
|
+
});
|
88
|
+
|
89
|
+
return stream.toDataStreamResponse();
|
90
|
+
}
|
package/cn.ts
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
import { clsx, type ClassValue } from "clsx"
|
2
|
+
import { twMerge } from "tailwind-merge"
|
3
|
+
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
5
|
+
return twMerge(clsx(inputs))
|
6
|
+
}
|
7
|
+
|
8
|
+
export function slugify(str: string) {
|
9
|
+
return String(str)
|
10
|
+
.toLowerCase()
|
11
|
+
.trim()
|
12
|
+
.replace(/[^\w\s-]/g, '')
|
13
|
+
.replace(/[\s_-]+/g, '-')
|
14
|
+
.replace(/^-+|-+$/g, '');
|
15
|
+
}
|
package/config.ts
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
export interface ReactDocsConfig {
|
2
|
+
basePath: string;
|
3
|
+
contentPath: string;
|
4
|
+
searchApiPath: string;
|
5
|
+
aiChatApiPath: string;
|
6
|
+
}
|
7
|
+
|
8
|
+
export interface ReactDocsConfigOptions {
|
9
|
+
basePath?: string;
|
10
|
+
contentPath?: string;
|
11
|
+
searchApiPath?: string;
|
12
|
+
aiChatApiPath?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
const DEFAULT_CONFIG: ReactDocsConfig = {
|
16
|
+
basePath: "/docs",
|
17
|
+
contentPath: "content/docs",
|
18
|
+
searchApiPath: "/api/docs/search-index",
|
19
|
+
aiChatApiPath: "/api/docs/chat",
|
20
|
+
};
|
21
|
+
|
22
|
+
export function createReactDocsConfig(options: ReactDocsConfigOptions = {}): ReactDocsConfig {
|
23
|
+
return {
|
24
|
+
basePath: options.basePath ?? DEFAULT_CONFIG.basePath,
|
25
|
+
contentPath: options.contentPath ?? DEFAULT_CONFIG.contentPath,
|
26
|
+
searchApiPath: options.searchApiPath ?? DEFAULT_CONFIG.searchApiPath,
|
27
|
+
aiChatApiPath: options.aiChatApiPath ?? DEFAULT_CONFIG.aiChatApiPath,
|
28
|
+
};
|
29
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import { ReactDocsConfig } from "./config";
|
3
|
+
export declare const AIChatOption: React.MemoExoticComponent<({ query, onClick }: {
|
4
|
+
query: string;
|
5
|
+
onClick: () => void;
|
6
|
+
}) => import("react/jsx-runtime").JSX.Element>;
|
7
|
+
export declare function AIChat({ query, onBack, config }: {
|
8
|
+
query: string;
|
9
|
+
onBack: () => void;
|
10
|
+
config: ReactDocsConfig;
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
12
|
+
//# sourceMappingURL=ai-chat.d.ts.map
|