@djangocfg/layouts 2.1.103 → 2.1.104
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/package.json +33 -37
- package/src/components/RedirectPage/RedirectPage.tsx +2 -2
- package/src/components/core/ClientOnly.tsx +1 -1
- package/src/components/errors/ErrorLayout.tsx +1 -1
- package/src/components/errors/ErrorsTracker/components/ErrorButtons.tsx +1 -1
- package/src/components/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/layouts/AuthLayout/components/AuthHelp.tsx +1 -1
- package/src/layouts/AuthLayout/components/AuthSuccess.tsx +1 -1
- package/src/layouts/AuthLayout/components/IdentifierForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/OTPForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/TwoFactorForm.tsx +1 -1
- package/src/layouts/AuthLayout/components/TwoFactorSetup.tsx +1 -1
- package/src/layouts/AuthLayout/components/oauth/OAuthCallback.tsx +1 -1
- package/src/layouts/AuthLayout/components/oauth/OAuthProviders.tsx +1 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +3 -2
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +2 -2
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +1 -1
- package/src/layouts/ProfileLayout/__tests__/TwoFactorSection.test.tsx +1 -1
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +1 -1
- package/src/layouts/ProfileLayout/components/DeleteAccountSection.tsx +1 -1
- package/src/layouts/ProfileLayout/components/ProfileForm.tsx +1 -1
- package/src/layouts/ProfileLayout/components/TwoFactorSection.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicFooter/PublicFooter.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +2 -2
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +2 -0
- package/src/pages/index.ts +2 -0
- package/src/pages/legal/LegalPage.tsx +1 -1
- package/src/snippets/AuthDialog/AuthDialog.tsx +3 -2
- package/src/snippets/McpChat/components/AIChatWidget.tsx +1 -1
- package/src/snippets/McpChat/components/AskAIButton.tsx +1 -1
- package/src/snippets/McpChat/components/ChatMessages.tsx +1 -1
- package/src/snippets/McpChat/components/ChatPanel.tsx +1 -1
- package/src/snippets/McpChat/components/ChatSidebar.tsx +1 -1
- package/src/snippets/McpChat/components/ChatWidget.tsx +1 -1
- package/src/snippets/McpChat/components/MessageBubble.tsx +1 -1
- package/src/snippets/McpChat/components/MessageInput.tsx +1 -1
- package/src/snippets/McpChat/context/AIChatContext.tsx +1 -1
- package/src/snippets/McpChat/context/ChatContext.tsx +1 -1
- package/src/snippets/McpChat/hooks/useChatLayout.ts +1 -1
- package/src/snippets/PWAInstall/components/A2HSHint.tsx +0 -1
- package/src/snippets/PWAInstall/components/DesktopGuide.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuide.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuideDrawer.tsx +1 -1
- package/src/snippets/PWAInstall/components/IOSGuideModal.tsx +1 -1
- package/src/snippets/PWAInstall/hooks/useInstallPrompt.ts +2 -2
- package/src/snippets/PushNotifications/components/PushPrompt.tsx +1 -1
- package/src/snippets/index.ts +1 -0
- package/dist/AIChatWidget-LUPM7S2O.mjs +0 -1644
- package/dist/AIChatWidget-LUPM7S2O.mjs.map +0 -1
- package/dist/AIChatWidget-O23TJJ7C.mjs +0 -3
- package/dist/AIChatWidget-O23TJJ7C.mjs.map +0 -1
- package/dist/chunk-53YKWR6F.mjs +0 -6
- package/dist/chunk-53YKWR6F.mjs.map +0 -1
- package/dist/chunk-EI7TDN2G.mjs +0 -1652
- package/dist/chunk-EI7TDN2G.mjs.map +0 -1
- package/dist/components.cjs +0 -925
- package/dist/components.cjs.map +0 -1
- package/dist/components.d.mts +0 -583
- package/dist/components.d.ts +0 -583
- package/dist/components.mjs +0 -879
- package/dist/components.mjs.map +0 -1
- package/dist/index.cjs +0 -7573
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.mts +0 -2376
- package/dist/index.d.ts +0 -2376
- package/dist/index.mjs +0 -5673
- package/dist/index.mjs.map +0 -1
- package/dist/layouts.cjs +0 -6530
- package/dist/layouts.cjs.map +0 -1
- package/dist/layouts.d.mts +0 -748
- package/dist/layouts.d.ts +0 -748
- package/dist/layouts.mjs +0 -4741
- package/dist/layouts.mjs.map +0 -1
- package/dist/pages.cjs +0 -178
- package/dist/pages.cjs.map +0 -1
- package/dist/pages.d.mts +0 -57
- package/dist/pages.d.ts +0 -57
- package/dist/pages.mjs +0 -168
- package/dist/pages.mjs.map +0 -1
- package/dist/snippets.cjs +0 -3793
- package/dist/snippets.cjs.map +0 -1
- package/dist/snippets.d.mts +0 -1192
- package/dist/snippets.d.ts +0 -1192
- package/dist/snippets.mjs +0 -3738
- package/dist/snippets.mjs.map +0 -1
- package/dist/utils.cjs +0 -34
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.d.mts +0 -40
- package/dist/utils.d.ts +0 -40
- package/dist/utils.mjs +0 -25
- package/dist/utils.mjs.map +0 -1
package/dist/chunk-EI7TDN2G.mjs
DELETED
|
@@ -1,1652 +0,0 @@
|
|
|
1
|
-
import { User, Bot, Loader2, ExternalLink, MessageSquare, StopCircle, Send, RotateCcw, PanelRight, X, GripVertical, PanelRightClose, Zap } from 'lucide-react';
|
|
2
|
-
import React, { createContext, forwardRef, useRef, useImperativeHandle, useState, useCallback, useEffect, useContext, useMemo } from 'react';
|
|
3
|
-
import { Avatar, AvatarImage, AvatarFallback, Card, CardContent, Badge, Button, CardHeader, CardFooter, Portal } from '@djangocfg/ui-nextjs';
|
|
4
|
-
import { useLocalStorage, useIsMobile } from '@djangocfg/ui-nextjs/hooks';
|
|
5
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
-
import { useAuth } from '@djangocfg/api/auth';
|
|
7
|
-
import { MarkdownMessage } from '@djangocfg/ui-tools';
|
|
8
|
-
|
|
9
|
-
var __defProp = Object.defineProperty;
|
|
10
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
11
|
-
|
|
12
|
-
// src/snippets/McpChat/config.ts
|
|
13
|
-
var PROD_HOST = "https://mcp.djangocfg.com";
|
|
14
|
-
var DEV_HOST = "http://localhost:3002";
|
|
15
|
-
function getHost(autoDetect = false) {
|
|
16
|
-
if (autoDetect && true) {
|
|
17
|
-
return DEV_HOST;
|
|
18
|
-
}
|
|
19
|
-
return PROD_HOST;
|
|
20
|
-
}
|
|
21
|
-
__name(getHost, "getHost");
|
|
22
|
-
function getMcpEndpoints(autoDetect = false) {
|
|
23
|
-
const HOST = getHost(autoDetect);
|
|
24
|
-
return {
|
|
25
|
-
/** Base URL */
|
|
26
|
-
baseUrl: HOST,
|
|
27
|
-
/** Chat API endpoint */
|
|
28
|
-
chat: `${HOST}/api/chat`,
|
|
29
|
-
/** Search API endpoint */
|
|
30
|
-
search: `${HOST}/api/search`,
|
|
31
|
-
/** Conversations API endpoint */
|
|
32
|
-
conversations: `${HOST}/api/conversations`,
|
|
33
|
-
/** Health check endpoint */
|
|
34
|
-
health: `${HOST}/health`,
|
|
35
|
-
/** MCP protocol endpoint (Streamable HTTP) */
|
|
36
|
-
mcp: `${HOST}/mcp`,
|
|
37
|
-
/** SSE endpoint for legacy clients */
|
|
38
|
-
sse: `${HOST}/mcp/sse`
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
__name(getMcpEndpoints, "getMcpEndpoints");
|
|
42
|
-
var mcpEndpoints = getMcpEndpoints(false);
|
|
43
|
-
var sidebarConfig = {
|
|
44
|
-
/** Minimum sidebar width in pixels */
|
|
45
|
-
minWidth: 320,
|
|
46
|
-
/** Maximum sidebar width in pixels */
|
|
47
|
-
maxWidth: 600,
|
|
48
|
-
/** Default sidebar width in pixels */
|
|
49
|
-
defaultWidth: 400,
|
|
50
|
-
/** Z-index for chat elements */
|
|
51
|
-
zIndex: 300,
|
|
52
|
-
/** Animation duration in milliseconds */
|
|
53
|
-
animationDuration: 200
|
|
54
|
-
};
|
|
55
|
-
var fabConfig = {
|
|
56
|
-
/** Bottom offset in pixels */
|
|
57
|
-
bottom: 24,
|
|
58
|
-
/** Right offset in pixels */
|
|
59
|
-
right: 24};
|
|
60
|
-
var storageKeys = {
|
|
61
|
-
/** Display mode (closed, floating, sidebar) */
|
|
62
|
-
mode: "djangocfg-chat-mode",
|
|
63
|
-
/** User ID for conversation tracking */
|
|
64
|
-
userId: "djangocfg-chat-user-id",
|
|
65
|
-
/** Chat messages history */
|
|
66
|
-
messages: "djangocfg-chat-messages",
|
|
67
|
-
/** Sidebar width */
|
|
68
|
-
sidebarWidth: "djangocfg-chat-sidebar-width"
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// src/snippets/McpChat/hooks/useAIChat.ts
|
|
72
|
-
function generateId() {
|
|
73
|
-
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
74
|
-
}
|
|
75
|
-
__name(generateId, "generateId");
|
|
76
|
-
function generateThreadId() {
|
|
77
|
-
return `thread_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
78
|
-
}
|
|
79
|
-
__name(generateThreadId, "generateThreadId");
|
|
80
|
-
function generateUserId() {
|
|
81
|
-
return `user_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
82
|
-
}
|
|
83
|
-
__name(generateUserId, "generateUserId");
|
|
84
|
-
var STORAGE_KEY = "djangocfg_chat";
|
|
85
|
-
function getPersistedIds() {
|
|
86
|
-
if (typeof window === "undefined") {
|
|
87
|
-
return { threadId: generateThreadId(), userId: generateUserId() };
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
const stored = localStorage.getItem(STORAGE_KEY);
|
|
91
|
-
if (stored) {
|
|
92
|
-
const data = JSON.parse(stored);
|
|
93
|
-
if (data.threadId && data.userId) {
|
|
94
|
-
return data;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
const ids = { threadId: generateThreadId(), userId: generateUserId() };
|
|
100
|
-
try {
|
|
101
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify(ids));
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
return ids;
|
|
105
|
-
}
|
|
106
|
-
__name(getPersistedIds, "getPersistedIds");
|
|
107
|
-
function persistThreadId(threadId, userId) {
|
|
108
|
-
if (typeof window === "undefined") return;
|
|
109
|
-
try {
|
|
110
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify({ threadId, userId }));
|
|
111
|
-
} catch {
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
__name(persistThreadId, "persistThreadId");
|
|
115
|
-
async function saveMessageToServer(threadId, userId, message) {
|
|
116
|
-
try {
|
|
117
|
-
await fetch(`${mcpEndpoints.conversations}/${threadId}/messages`, {
|
|
118
|
-
method: "POST",
|
|
119
|
-
headers: { "Content-Type": "application/json" },
|
|
120
|
-
body: JSON.stringify({
|
|
121
|
-
userId,
|
|
122
|
-
message: {
|
|
123
|
-
id: message.id,
|
|
124
|
-
role: message.role,
|
|
125
|
-
content: message.content,
|
|
126
|
-
timestamp: message.timestamp.getTime(),
|
|
127
|
-
sources: message.sources
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
});
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.warn("[Chat] Failed to save message to server:", error);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
__name(saveMessageToServer, "saveMessageToServer");
|
|
136
|
-
async function loadConversationFromServer(threadId) {
|
|
137
|
-
try {
|
|
138
|
-
const response = await fetch(`${mcpEndpoints.conversations}/${threadId}`);
|
|
139
|
-
if (response.status === 404) return null;
|
|
140
|
-
if (!response.ok) return [];
|
|
141
|
-
const data = await response.json();
|
|
142
|
-
if (!data.messages || !Array.isArray(data.messages)) return [];
|
|
143
|
-
return data.messages.map((m) => ({
|
|
144
|
-
id: m.id,
|
|
145
|
-
role: m.role,
|
|
146
|
-
content: m.content,
|
|
147
|
-
timestamp: new Date(m.timestamp),
|
|
148
|
-
sources: m.sources
|
|
149
|
-
}));
|
|
150
|
-
} catch (error) {
|
|
151
|
-
console.warn("[Chat] Failed to load conversation from server:", error);
|
|
152
|
-
return [];
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
__name(loadConversationFromServer, "loadConversationFromServer");
|
|
156
|
-
async function deleteConversationFromServer(threadId) {
|
|
157
|
-
try {
|
|
158
|
-
await fetch(`${mcpEndpoints.conversations}/${threadId}`, {
|
|
159
|
-
method: "DELETE"
|
|
160
|
-
});
|
|
161
|
-
} catch (error) {
|
|
162
|
-
console.warn("[Chat] Failed to delete conversation from server:", error);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
__name(deleteConversationFromServer, "deleteConversationFromServer");
|
|
166
|
-
function useAIChat(options) {
|
|
167
|
-
const {
|
|
168
|
-
apiEndpoint = mcpEndpoints.chat,
|
|
169
|
-
initialMessages = [],
|
|
170
|
-
onError,
|
|
171
|
-
enableStreaming = true,
|
|
172
|
-
threadId: initialThreadId,
|
|
173
|
-
userId: initialUserId
|
|
174
|
-
} = options;
|
|
175
|
-
const [messages, setMessages] = useState(initialMessages);
|
|
176
|
-
const [isLoadingHistory, setIsLoadingHistory] = useState(true);
|
|
177
|
-
const persistedIds = useRef(null);
|
|
178
|
-
if (persistedIds.current === null && typeof window !== "undefined") {
|
|
179
|
-
persistedIds.current = getPersistedIds();
|
|
180
|
-
}
|
|
181
|
-
const [threadId, setThreadId] = useState(
|
|
182
|
-
() => initialThreadId || persistedIds.current?.threadId || generateThreadId()
|
|
183
|
-
);
|
|
184
|
-
const [userId] = useState(
|
|
185
|
-
() => initialUserId || persistedIds.current?.userId || generateUserId()
|
|
186
|
-
);
|
|
187
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
188
|
-
const [error, setError] = useState(null);
|
|
189
|
-
const abortControllerRef = useRef(null);
|
|
190
|
-
useEffect(() => {
|
|
191
|
-
if (typeof window === "undefined") {
|
|
192
|
-
setIsLoadingHistory(false);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const loadHistory = /* @__PURE__ */ __name(async () => {
|
|
196
|
-
const serverMessages = await loadConversationFromServer(threadId);
|
|
197
|
-
if (serverMessages === null) {
|
|
198
|
-
console.log("[Chat] Session expired or invalid, starting new session");
|
|
199
|
-
const newThreadId = generateThreadId();
|
|
200
|
-
setThreadId(newThreadId);
|
|
201
|
-
persistThreadId(newThreadId, userId);
|
|
202
|
-
setMessages([]);
|
|
203
|
-
} else if (serverMessages.length > 0) {
|
|
204
|
-
setMessages(serverMessages);
|
|
205
|
-
}
|
|
206
|
-
setIsLoadingHistory(false);
|
|
207
|
-
}, "loadHistory");
|
|
208
|
-
loadHistory();
|
|
209
|
-
}, [threadId, userId]);
|
|
210
|
-
const sendMessage = useCallback(
|
|
211
|
-
async (content) => {
|
|
212
|
-
if (!content.trim() || isLoading) return;
|
|
213
|
-
if (abortControllerRef.current) {
|
|
214
|
-
abortControllerRef.current.abort();
|
|
215
|
-
}
|
|
216
|
-
abortControllerRef.current = new AbortController();
|
|
217
|
-
const userMessage = {
|
|
218
|
-
id: generateId(),
|
|
219
|
-
role: "user",
|
|
220
|
-
content: content.trim(),
|
|
221
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
222
|
-
};
|
|
223
|
-
const assistantMessageId = generateId();
|
|
224
|
-
const assistantMessage = {
|
|
225
|
-
id: assistantMessageId,
|
|
226
|
-
role: "assistant",
|
|
227
|
-
content: "",
|
|
228
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
229
|
-
isStreaming: true
|
|
230
|
-
};
|
|
231
|
-
setMessages((prev) => [...prev, userMessage, assistantMessage]);
|
|
232
|
-
setIsLoading(true);
|
|
233
|
-
setError(null);
|
|
234
|
-
saveMessageToServer(threadId, userId, userMessage);
|
|
235
|
-
try {
|
|
236
|
-
const chatMessages = [
|
|
237
|
-
...messages.filter((m) => m.role !== "system").slice(-10).map((m) => ({
|
|
238
|
-
role: m.role,
|
|
239
|
-
content: m.content
|
|
240
|
-
})),
|
|
241
|
-
{ role: "user", content }
|
|
242
|
-
];
|
|
243
|
-
const response = await fetch(apiEndpoint, {
|
|
244
|
-
method: "POST",
|
|
245
|
-
headers: {
|
|
246
|
-
"Content-Type": "application/json"
|
|
247
|
-
},
|
|
248
|
-
body: JSON.stringify({
|
|
249
|
-
messages: chatMessages,
|
|
250
|
-
stream: enableStreaming
|
|
251
|
-
}),
|
|
252
|
-
signal: abortControllerRef.current.signal
|
|
253
|
-
});
|
|
254
|
-
if (!response.ok) {
|
|
255
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
256
|
-
}
|
|
257
|
-
if (enableStreaming && response.headers.get("content-type")?.includes("text/event-stream")) {
|
|
258
|
-
await handleStreamingResponse(response, assistantMessageId);
|
|
259
|
-
} else {
|
|
260
|
-
const data = await response.json();
|
|
261
|
-
if (!data.success) {
|
|
262
|
-
throw new Error(data.error || "Failed to get response");
|
|
263
|
-
}
|
|
264
|
-
if (data.threadId && data.threadId !== threadId) {
|
|
265
|
-
setThreadId(data.threadId);
|
|
266
|
-
}
|
|
267
|
-
const sources = data.sources?.map((s) => ({
|
|
268
|
-
title: s.title,
|
|
269
|
-
path: s.path,
|
|
270
|
-
url: s.url,
|
|
271
|
-
section: s.section,
|
|
272
|
-
score: s.score
|
|
273
|
-
})) || [];
|
|
274
|
-
const finalContent = data.content || "I found some relevant documentation.";
|
|
275
|
-
setMessages(
|
|
276
|
-
(prev) => prev.map(
|
|
277
|
-
(m) => m.id === assistantMessageId ? {
|
|
278
|
-
...m,
|
|
279
|
-
content: finalContent,
|
|
280
|
-
sources,
|
|
281
|
-
isStreaming: false
|
|
282
|
-
} : m
|
|
283
|
-
)
|
|
284
|
-
);
|
|
285
|
-
saveMessageToServer(threadId, userId, {
|
|
286
|
-
id: assistantMessageId,
|
|
287
|
-
role: "assistant",
|
|
288
|
-
content: finalContent,
|
|
289
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
290
|
-
sources
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
} catch (err) {
|
|
294
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
295
|
-
setMessages((prev) => prev.filter((m) => m.id !== assistantMessageId));
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
const error2 = err instanceof Error ? err : new Error("Unknown error");
|
|
299
|
-
setError(error2);
|
|
300
|
-
onError?.(error2);
|
|
301
|
-
setMessages(
|
|
302
|
-
(prev) => prev.map(
|
|
303
|
-
(m) => m.id === assistantMessageId ? {
|
|
304
|
-
...m,
|
|
305
|
-
content: `Sorry, I encountered an error: ${error2.message}. Please try again.`,
|
|
306
|
-
isStreaming: false
|
|
307
|
-
} : m
|
|
308
|
-
)
|
|
309
|
-
);
|
|
310
|
-
} finally {
|
|
311
|
-
setIsLoading(false);
|
|
312
|
-
abortControllerRef.current = null;
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
[apiEndpoint, isLoading, messages, threadId, userId, enableStreaming, onError]
|
|
316
|
-
);
|
|
317
|
-
const handleStreamingResponse = /* @__PURE__ */ __name(async (response, messageId) => {
|
|
318
|
-
const reader = response.body?.getReader();
|
|
319
|
-
if (!reader) {
|
|
320
|
-
throw new Error("No response body");
|
|
321
|
-
}
|
|
322
|
-
const decoder = new TextDecoder();
|
|
323
|
-
let buffer = "";
|
|
324
|
-
let fullContent = "";
|
|
325
|
-
const sources = [];
|
|
326
|
-
try {
|
|
327
|
-
while (true) {
|
|
328
|
-
const { done, value } = await reader.read();
|
|
329
|
-
if (done) break;
|
|
330
|
-
buffer += decoder.decode(value, { stream: true });
|
|
331
|
-
const lines = buffer.split("\n");
|
|
332
|
-
buffer = lines.pop() || "";
|
|
333
|
-
for (const line of lines) {
|
|
334
|
-
if (line.startsWith("data: ")) {
|
|
335
|
-
const data = line.slice(6);
|
|
336
|
-
if (data === "[DONE]") {
|
|
337
|
-
setMessages(
|
|
338
|
-
(prev) => prev.map(
|
|
339
|
-
(m) => m.id === messageId ? {
|
|
340
|
-
...m,
|
|
341
|
-
content: fullContent,
|
|
342
|
-
sources,
|
|
343
|
-
isStreaming: false
|
|
344
|
-
} : m
|
|
345
|
-
)
|
|
346
|
-
);
|
|
347
|
-
saveMessageToServer(threadId, userId, {
|
|
348
|
-
id: messageId,
|
|
349
|
-
role: "assistant",
|
|
350
|
-
content: fullContent,
|
|
351
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
352
|
-
sources
|
|
353
|
-
});
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
const parsed = JSON.parse(data);
|
|
358
|
-
if (parsed.type === "text" && parsed.content) {
|
|
359
|
-
fullContent += parsed.content;
|
|
360
|
-
setMessages(
|
|
361
|
-
(prev) => prev.map(
|
|
362
|
-
(m) => m.id === messageId ? {
|
|
363
|
-
...m,
|
|
364
|
-
content: fullContent,
|
|
365
|
-
isStreaming: true
|
|
366
|
-
} : m
|
|
367
|
-
)
|
|
368
|
-
);
|
|
369
|
-
} else if (parsed.type === "source" && parsed.source) {
|
|
370
|
-
sources.push({
|
|
371
|
-
title: parsed.source.title,
|
|
372
|
-
path: parsed.source.path,
|
|
373
|
-
url: parsed.source.url,
|
|
374
|
-
section: parsed.source.section,
|
|
375
|
-
score: parsed.source.score
|
|
376
|
-
});
|
|
377
|
-
} else if (parsed.type === "done") {
|
|
378
|
-
setMessages(
|
|
379
|
-
(prev) => prev.map(
|
|
380
|
-
(m) => m.id === messageId ? {
|
|
381
|
-
...m,
|
|
382
|
-
content: fullContent,
|
|
383
|
-
sources,
|
|
384
|
-
isStreaming: false
|
|
385
|
-
} : m
|
|
386
|
-
)
|
|
387
|
-
);
|
|
388
|
-
saveMessageToServer(threadId, userId, {
|
|
389
|
-
id: messageId,
|
|
390
|
-
role: "assistant",
|
|
391
|
-
content: fullContent,
|
|
392
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
393
|
-
sources
|
|
394
|
-
});
|
|
395
|
-
} else if (parsed.type === "error") {
|
|
396
|
-
throw new Error(parsed.error || "Stream error");
|
|
397
|
-
}
|
|
398
|
-
} catch {
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
} finally {
|
|
404
|
-
reader.releaseLock();
|
|
405
|
-
}
|
|
406
|
-
}, "handleStreamingResponse");
|
|
407
|
-
const clearMessages = useCallback(async () => {
|
|
408
|
-
if (abortControllerRef.current) {
|
|
409
|
-
abortControllerRef.current.abort();
|
|
410
|
-
}
|
|
411
|
-
await deleteConversationFromServer(threadId);
|
|
412
|
-
setMessages([]);
|
|
413
|
-
setError(null);
|
|
414
|
-
const newThreadId = generateThreadId();
|
|
415
|
-
setThreadId(newThreadId);
|
|
416
|
-
persistThreadId(newThreadId, userId);
|
|
417
|
-
}, [threadId, userId]);
|
|
418
|
-
const stopStreaming = useCallback(() => {
|
|
419
|
-
if (abortControllerRef.current) {
|
|
420
|
-
abortControllerRef.current.abort();
|
|
421
|
-
}
|
|
422
|
-
}, []);
|
|
423
|
-
return {
|
|
424
|
-
messages,
|
|
425
|
-
isLoading: isLoading || isLoadingHistory,
|
|
426
|
-
error,
|
|
427
|
-
threadId,
|
|
428
|
-
userId,
|
|
429
|
-
sendMessage,
|
|
430
|
-
clearMessages,
|
|
431
|
-
stopStreaming
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
__name(useAIChat, "useAIChat");
|
|
435
|
-
var STORAGE_KEY_MODE = "djangocfg-ai-chat-mode";
|
|
436
|
-
var AIChatContext = createContext(null);
|
|
437
|
-
function AIChatProvider({
|
|
438
|
-
children,
|
|
439
|
-
apiEndpoint = mcpEndpoints.chat,
|
|
440
|
-
config: userConfig = {},
|
|
441
|
-
onError,
|
|
442
|
-
enableStreaming = true
|
|
443
|
-
}) {
|
|
444
|
-
const {
|
|
445
|
-
messages,
|
|
446
|
-
isLoading,
|
|
447
|
-
error,
|
|
448
|
-
threadId,
|
|
449
|
-
userId,
|
|
450
|
-
sendMessage: sendAIMessage,
|
|
451
|
-
clearMessages: clearAIMessages,
|
|
452
|
-
stopStreaming
|
|
453
|
-
} = useAIChat({
|
|
454
|
-
apiEndpoint,
|
|
455
|
-
onError,
|
|
456
|
-
enableStreaming
|
|
457
|
-
});
|
|
458
|
-
const [isMinimized, setIsMinimized] = useState(false);
|
|
459
|
-
const [storedMode, setStoredMode] = useLocalStorage(STORAGE_KEY_MODE, "closed");
|
|
460
|
-
const isMobile = useIsMobile();
|
|
461
|
-
const displayMode = useMemo(() => {
|
|
462
|
-
if (isMobile && storedMode === "sidebar") {
|
|
463
|
-
return "floating";
|
|
464
|
-
}
|
|
465
|
-
return storedMode;
|
|
466
|
-
}, [isMobile, storedMode]);
|
|
467
|
-
const isOpen = displayMode !== "closed";
|
|
468
|
-
const isOpenRef = useRef(isOpen);
|
|
469
|
-
useEffect(() => {
|
|
470
|
-
isOpenRef.current = isOpen;
|
|
471
|
-
}, [isOpen]);
|
|
472
|
-
const lastActiveModeRef = useRef("floating");
|
|
473
|
-
useEffect(() => {
|
|
474
|
-
if (displayMode !== "closed") {
|
|
475
|
-
lastActiveModeRef.current = displayMode;
|
|
476
|
-
}
|
|
477
|
-
}, [displayMode]);
|
|
478
|
-
const config = useMemo(
|
|
479
|
-
() => ({
|
|
480
|
-
apiEndpoint,
|
|
481
|
-
title: "DjangoCFG AI",
|
|
482
|
-
placeholder: "Ask about DjangoCFG...",
|
|
483
|
-
greeting: "Hi! I'm your DjangoCFG AI assistant powered by GPT. Ask me anything about configuration, features, or how to use the library.",
|
|
484
|
-
position: "bottom-right",
|
|
485
|
-
variant: "default",
|
|
486
|
-
...userConfig
|
|
487
|
-
}),
|
|
488
|
-
[apiEndpoint, userConfig]
|
|
489
|
-
);
|
|
490
|
-
const sendMessage = useCallback(
|
|
491
|
-
async (content) => {
|
|
492
|
-
await sendAIMessage(content);
|
|
493
|
-
},
|
|
494
|
-
[sendAIMessage]
|
|
495
|
-
);
|
|
496
|
-
const clearMessages = useCallback(() => {
|
|
497
|
-
clearAIMessages();
|
|
498
|
-
}, [clearAIMessages]);
|
|
499
|
-
const openChat = useCallback(() => {
|
|
500
|
-
setStoredMode(lastActiveModeRef.current);
|
|
501
|
-
setIsMinimized(false);
|
|
502
|
-
}, [setStoredMode]);
|
|
503
|
-
const closeChat = useCallback(() => {
|
|
504
|
-
setStoredMode("closed");
|
|
505
|
-
setIsMinimized(false);
|
|
506
|
-
}, [setStoredMode]);
|
|
507
|
-
const toggleChat = useCallback(() => {
|
|
508
|
-
if (displayMode === "closed") {
|
|
509
|
-
setStoredMode("floating");
|
|
510
|
-
setIsMinimized(false);
|
|
511
|
-
} else {
|
|
512
|
-
setStoredMode("closed");
|
|
513
|
-
}
|
|
514
|
-
}, [displayMode, setStoredMode]);
|
|
515
|
-
const toggleMinimize = useCallback(() => {
|
|
516
|
-
setIsMinimized((prev) => !prev);
|
|
517
|
-
}, []);
|
|
518
|
-
const setDisplayMode = useCallback(
|
|
519
|
-
(mode) => {
|
|
520
|
-
if (isMobile && mode === "sidebar") {
|
|
521
|
-
setStoredMode("floating");
|
|
522
|
-
} else {
|
|
523
|
-
setStoredMode(mode);
|
|
524
|
-
}
|
|
525
|
-
setIsMinimized(false);
|
|
526
|
-
},
|
|
527
|
-
[isMobile, setStoredMode]
|
|
528
|
-
);
|
|
529
|
-
const value = useMemo(
|
|
530
|
-
() => ({
|
|
531
|
-
messages,
|
|
532
|
-
isLoading,
|
|
533
|
-
error,
|
|
534
|
-
isOpen,
|
|
535
|
-
isMinimized,
|
|
536
|
-
config,
|
|
537
|
-
displayMode,
|
|
538
|
-
isMobile,
|
|
539
|
-
threadId,
|
|
540
|
-
userId,
|
|
541
|
-
sendMessage,
|
|
542
|
-
clearMessages,
|
|
543
|
-
openChat,
|
|
544
|
-
closeChat,
|
|
545
|
-
toggleChat,
|
|
546
|
-
toggleMinimize,
|
|
547
|
-
setDisplayMode,
|
|
548
|
-
stopStreaming
|
|
549
|
-
}),
|
|
550
|
-
[
|
|
551
|
-
messages,
|
|
552
|
-
isLoading,
|
|
553
|
-
error,
|
|
554
|
-
isOpen,
|
|
555
|
-
isMinimized,
|
|
556
|
-
config,
|
|
557
|
-
displayMode,
|
|
558
|
-
isMobile,
|
|
559
|
-
threadId,
|
|
560
|
-
userId,
|
|
561
|
-
sendMessage,
|
|
562
|
-
clearMessages,
|
|
563
|
-
openChat,
|
|
564
|
-
closeChat,
|
|
565
|
-
toggleChat,
|
|
566
|
-
toggleMinimize,
|
|
567
|
-
setDisplayMode,
|
|
568
|
-
stopStreaming
|
|
569
|
-
]
|
|
570
|
-
);
|
|
571
|
-
useEffect(() => {
|
|
572
|
-
if (typeof window !== "undefined") {
|
|
573
|
-
window.__MCP_CHAT_AVAILABLE__ = true;
|
|
574
|
-
}
|
|
575
|
-
const handleChatEvent = /* @__PURE__ */ __name((event) => {
|
|
576
|
-
const customEvent = event;
|
|
577
|
-
const { message, context, autoSend = true, displayMode: requestedMode } = customEvent.detail;
|
|
578
|
-
window.dispatchEvent(new CustomEvent("mcp:chat:handled"));
|
|
579
|
-
let fullMessage = message;
|
|
580
|
-
if (context) {
|
|
581
|
-
if (context.type) {
|
|
582
|
-
fullMessage = `[${context.type.toUpperCase()}] ${message}`;
|
|
583
|
-
}
|
|
584
|
-
if (context.data) {
|
|
585
|
-
fullMessage += `
|
|
586
|
-
|
|
587
|
-
**Context:**
|
|
588
|
-
\`\`\`json
|
|
589
|
-
${JSON.stringify(context.data, null, 2)}
|
|
590
|
-
\`\`\``;
|
|
591
|
-
}
|
|
592
|
-
if (context.source) {
|
|
593
|
-
fullMessage += `
|
|
594
|
-
|
|
595
|
-
_Source: ${context.source}_`;
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
if (requestedMode) {
|
|
599
|
-
setDisplayMode(requestedMode);
|
|
600
|
-
} else if (!isOpenRef.current) {
|
|
601
|
-
openChat();
|
|
602
|
-
}
|
|
603
|
-
if (autoSend) {
|
|
604
|
-
setTimeout(() => {
|
|
605
|
-
sendMessage(fullMessage);
|
|
606
|
-
}, 100);
|
|
607
|
-
}
|
|
608
|
-
}, "handleChatEvent");
|
|
609
|
-
window.addEventListener("mcp:chat:send", handleChatEvent);
|
|
610
|
-
return () => {
|
|
611
|
-
window.removeEventListener("mcp:chat:send", handleChatEvent);
|
|
612
|
-
if (typeof window !== "undefined") {
|
|
613
|
-
window.__MCP_CHAT_AVAILABLE__ = false;
|
|
614
|
-
}
|
|
615
|
-
};
|
|
616
|
-
}, [sendMessage, setDisplayMode, openChat]);
|
|
617
|
-
return /* @__PURE__ */ jsx(AIChatContext.Provider, { value, children });
|
|
618
|
-
}
|
|
619
|
-
__name(AIChatProvider, "AIChatProvider");
|
|
620
|
-
function useAIChatContext() {
|
|
621
|
-
const context = useContext(AIChatContext);
|
|
622
|
-
if (!context) {
|
|
623
|
-
throw new Error("useAIChatContext must be used within an AIChatProvider");
|
|
624
|
-
}
|
|
625
|
-
return context;
|
|
626
|
-
}
|
|
627
|
-
__name(useAIChatContext, "useAIChatContext");
|
|
628
|
-
function useAIChatContextOptional() {
|
|
629
|
-
return useContext(AIChatContext);
|
|
630
|
-
}
|
|
631
|
-
__name(useAIChatContextOptional, "useAIChatContextOptional");
|
|
632
|
-
var MIN_SIDEBAR_WIDTH = sidebarConfig.minWidth;
|
|
633
|
-
var MAX_SIDEBAR_WIDTH = sidebarConfig.maxWidth;
|
|
634
|
-
var DEFAULT_CONFIG = {
|
|
635
|
-
initialWidth: sidebarConfig.defaultWidth,
|
|
636
|
-
animationDuration: sidebarConfig.animationDuration,
|
|
637
|
-
pushTarget: "body"
|
|
638
|
-
};
|
|
639
|
-
function useChatLayout(config) {
|
|
640
|
-
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
641
|
-
const { initialWidth, animationDuration, pushTarget } = mergedConfig;
|
|
642
|
-
const [storedWidth, setStoredWidth] = useLocalStorage(storageKeys.sidebarWidth, initialWidth);
|
|
643
|
-
const sidebarWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, storedWidth));
|
|
644
|
-
const sidebarWidthRef = useRef(sidebarWidth);
|
|
645
|
-
const [isResizing, setIsResizing] = useState(false);
|
|
646
|
-
useEffect(() => {
|
|
647
|
-
sidebarWidthRef.current = sidebarWidth;
|
|
648
|
-
}, [sidebarWidth]);
|
|
649
|
-
const originalStylesRef = useRef(null);
|
|
650
|
-
const fixedElementsRef = useRef([]);
|
|
651
|
-
const currentModeRef = useRef("closed");
|
|
652
|
-
const getTargetElement = useCallback(() => {
|
|
653
|
-
if (typeof window === "undefined") return null;
|
|
654
|
-
if (pushTarget === "body") {
|
|
655
|
-
return document.body;
|
|
656
|
-
} else if (pushTarget === "main") {
|
|
657
|
-
return document.querySelector("main");
|
|
658
|
-
} else {
|
|
659
|
-
return document.querySelector(pushTarget);
|
|
660
|
-
}
|
|
661
|
-
}, [pushTarget]);
|
|
662
|
-
const getFixedElements = useCallback(() => {
|
|
663
|
-
if (typeof window === "undefined") return [];
|
|
664
|
-
const elements = [];
|
|
665
|
-
const allElements = document.querySelectorAll("*");
|
|
666
|
-
allElements.forEach((el) => {
|
|
667
|
-
if (!(el instanceof HTMLElement)) return;
|
|
668
|
-
if (el.closest("[data-chat-sidebar-panel]")) return;
|
|
669
|
-
const style = window.getComputedStyle(el);
|
|
670
|
-
const position = style.position;
|
|
671
|
-
const right = style.right;
|
|
672
|
-
if ((position === "fixed" || position === "sticky") && right === "0px") {
|
|
673
|
-
elements.push(el);
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
return elements;
|
|
677
|
-
}, []);
|
|
678
|
-
const saveOriginalStyles = useCallback((element) => {
|
|
679
|
-
if (!originalStylesRef.current) {
|
|
680
|
-
originalStylesRef.current = {
|
|
681
|
-
marginRight: element.style.marginRight,
|
|
682
|
-
overflowX: element.style.overflowX,
|
|
683
|
-
transition: element.style.transition
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
}, []);
|
|
687
|
-
const restoreOriginalStyles = useCallback((element) => {
|
|
688
|
-
if (originalStylesRef.current) {
|
|
689
|
-
element.style.marginRight = originalStylesRef.current.marginRight || "";
|
|
690
|
-
element.style.overflowX = originalStylesRef.current.overflowX || "";
|
|
691
|
-
element.style.transition = originalStylesRef.current.transition || "";
|
|
692
|
-
element.removeAttribute("data-chat-sidebar");
|
|
693
|
-
originalStylesRef.current = null;
|
|
694
|
-
}
|
|
695
|
-
}, []);
|
|
696
|
-
const adjustFixedElements = useCallback(
|
|
697
|
-
(open) => {
|
|
698
|
-
const currentWidth = sidebarWidthRef.current;
|
|
699
|
-
if (open) {
|
|
700
|
-
const fixedElements = getFixedElements();
|
|
701
|
-
fixedElementsRef.current = fixedElements.map((el) => ({
|
|
702
|
-
element: el,
|
|
703
|
-
right: el.style.right,
|
|
704
|
-
transition: el.style.transition
|
|
705
|
-
}));
|
|
706
|
-
fixedElements.forEach((el) => {
|
|
707
|
-
el.style.transition = `right ${animationDuration}ms ease`;
|
|
708
|
-
el.style.right = `${currentWidth}px`;
|
|
709
|
-
});
|
|
710
|
-
} else {
|
|
711
|
-
fixedElementsRef.current.forEach(({ element, right, transition }) => {
|
|
712
|
-
element.style.transition = `right ${animationDuration}ms ease`;
|
|
713
|
-
element.style.right = "0px";
|
|
714
|
-
setTimeout(() => {
|
|
715
|
-
element.style.right = right;
|
|
716
|
-
element.style.transition = transition;
|
|
717
|
-
}, animationDuration);
|
|
718
|
-
});
|
|
719
|
-
fixedElementsRef.current = [];
|
|
720
|
-
}
|
|
721
|
-
},
|
|
722
|
-
[getFixedElements, animationDuration]
|
|
723
|
-
);
|
|
724
|
-
const applySidebarLayout = useCallback(() => {
|
|
725
|
-
const target = getTargetElement();
|
|
726
|
-
if (!target) return;
|
|
727
|
-
const currentWidth = sidebarWidthRef.current;
|
|
728
|
-
saveOriginalStyles(target);
|
|
729
|
-
target.style.transition = `margin-right ${animationDuration}ms ease`;
|
|
730
|
-
target.style.marginRight = `${currentWidth}px`;
|
|
731
|
-
target.style.overflowX = "hidden";
|
|
732
|
-
target.setAttribute("data-chat-sidebar", "open");
|
|
733
|
-
adjustFixedElements(true);
|
|
734
|
-
currentModeRef.current = "sidebar";
|
|
735
|
-
}, [getTargetElement, saveOriginalStyles, animationDuration, adjustFixedElements]);
|
|
736
|
-
const applyDefaultLayout = useCallback(
|
|
737
|
-
(mode) => {
|
|
738
|
-
const target = getTargetElement();
|
|
739
|
-
if (!target) return;
|
|
740
|
-
if (currentModeRef.current === "sidebar") {
|
|
741
|
-
target.style.transition = `margin-right ${animationDuration}ms ease`;
|
|
742
|
-
target.style.marginRight = "0px";
|
|
743
|
-
adjustFixedElements(false);
|
|
744
|
-
setTimeout(() => {
|
|
745
|
-
restoreOriginalStyles(target);
|
|
746
|
-
}, animationDuration);
|
|
747
|
-
}
|
|
748
|
-
currentModeRef.current = mode;
|
|
749
|
-
},
|
|
750
|
-
[getTargetElement, restoreOriginalStyles, animationDuration, adjustFixedElements]
|
|
751
|
-
);
|
|
752
|
-
const applyLayout = useCallback(
|
|
753
|
-
(mode) => {
|
|
754
|
-
if (mode === "sidebar") {
|
|
755
|
-
applySidebarLayout();
|
|
756
|
-
} else {
|
|
757
|
-
applyDefaultLayout(mode);
|
|
758
|
-
}
|
|
759
|
-
},
|
|
760
|
-
[applySidebarLayout, applyDefaultLayout]
|
|
761
|
-
);
|
|
762
|
-
const resetLayout = useCallback(() => {
|
|
763
|
-
const target = getTargetElement();
|
|
764
|
-
if (target && originalStylesRef.current) {
|
|
765
|
-
restoreOriginalStyles(target);
|
|
766
|
-
}
|
|
767
|
-
fixedElementsRef.current.forEach(({ element, right, transition }) => {
|
|
768
|
-
element.style.right = right;
|
|
769
|
-
element.style.transition = transition;
|
|
770
|
-
});
|
|
771
|
-
fixedElementsRef.current = [];
|
|
772
|
-
currentModeRef.current = "closed";
|
|
773
|
-
}, [getTargetElement, restoreOriginalStyles]);
|
|
774
|
-
const updateWidthImmediate = useCallback(
|
|
775
|
-
(newWidth) => {
|
|
776
|
-
const clampedWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, newWidth));
|
|
777
|
-
const target = getTargetElement();
|
|
778
|
-
if (target && currentModeRef.current === "sidebar") {
|
|
779
|
-
target.style.transition = "none";
|
|
780
|
-
target.style.marginRight = `${clampedWidth}px`;
|
|
781
|
-
}
|
|
782
|
-
fixedElementsRef.current.forEach(({ element }) => {
|
|
783
|
-
element.style.transition = "none";
|
|
784
|
-
element.style.right = `${clampedWidth}px`;
|
|
785
|
-
});
|
|
786
|
-
return clampedWidth;
|
|
787
|
-
},
|
|
788
|
-
[getTargetElement]
|
|
789
|
-
);
|
|
790
|
-
const updateWidth = useCallback(
|
|
791
|
-
(newWidth) => {
|
|
792
|
-
const clampedWidth = updateWidthImmediate(newWidth);
|
|
793
|
-
setStoredWidth(clampedWidth);
|
|
794
|
-
},
|
|
795
|
-
[updateWidthImmediate, setStoredWidth]
|
|
796
|
-
);
|
|
797
|
-
const startResize = useCallback(
|
|
798
|
-
(e) => {
|
|
799
|
-
e.preventDefault();
|
|
800
|
-
setIsResizing(true);
|
|
801
|
-
const startX = e.clientX;
|
|
802
|
-
const startWidth = sidebarWidthRef.current;
|
|
803
|
-
const handleMouseMove = /* @__PURE__ */ __name((moveEvent) => {
|
|
804
|
-
const deltaX = startX - moveEvent.clientX;
|
|
805
|
-
const newWidth = startWidth + deltaX;
|
|
806
|
-
const clampedWidth = Math.max(MIN_SIDEBAR_WIDTH, Math.min(MAX_SIDEBAR_WIDTH, newWidth));
|
|
807
|
-
updateWidthImmediate(clampedWidth);
|
|
808
|
-
sidebarWidthRef.current = clampedWidth;
|
|
809
|
-
setStoredWidth(clampedWidth);
|
|
810
|
-
}, "handleMouseMove");
|
|
811
|
-
const handleMouseUp = /* @__PURE__ */ __name(() => {
|
|
812
|
-
setIsResizing(false);
|
|
813
|
-
document.removeEventListener("mousemove", handleMouseMove);
|
|
814
|
-
document.removeEventListener("mouseup", handleMouseUp);
|
|
815
|
-
document.body.style.cursor = "";
|
|
816
|
-
document.body.style.userSelect = "";
|
|
817
|
-
}, "handleMouseUp");
|
|
818
|
-
document.addEventListener("mousemove", handleMouseMove);
|
|
819
|
-
document.addEventListener("mouseup", handleMouseUp);
|
|
820
|
-
document.body.style.cursor = "ew-resize";
|
|
821
|
-
document.body.style.userSelect = "none";
|
|
822
|
-
},
|
|
823
|
-
[updateWidthImmediate, setStoredWidth]
|
|
824
|
-
);
|
|
825
|
-
const getSidebarStyles = useCallback(() => {
|
|
826
|
-
return {
|
|
827
|
-
position: "fixed",
|
|
828
|
-
top: 0,
|
|
829
|
-
right: 0,
|
|
830
|
-
bottom: 0,
|
|
831
|
-
width: `${sidebarWidth}px`,
|
|
832
|
-
zIndex: sidebarConfig.zIndex
|
|
833
|
-
};
|
|
834
|
-
}, [sidebarWidth]);
|
|
835
|
-
const getFloatingStyles = useCallback(
|
|
836
|
-
(position) => {
|
|
837
|
-
return {
|
|
838
|
-
position: "fixed",
|
|
839
|
-
zIndex: sidebarConfig.zIndex - 50,
|
|
840
|
-
bottom: fabConfig.bottom,
|
|
841
|
-
...position === "bottom-right" ? { right: fabConfig.right } : { left: fabConfig.right }
|
|
842
|
-
};
|
|
843
|
-
},
|
|
844
|
-
[]
|
|
845
|
-
);
|
|
846
|
-
const getFabStyles = useCallback(
|
|
847
|
-
(position) => {
|
|
848
|
-
return {
|
|
849
|
-
position: "fixed",
|
|
850
|
-
zIndex: sidebarConfig.zIndex - 50,
|
|
851
|
-
bottom: fabConfig.bottom,
|
|
852
|
-
...position === "bottom-right" ? { right: fabConfig.right } : { left: fabConfig.right }
|
|
853
|
-
};
|
|
854
|
-
},
|
|
855
|
-
[]
|
|
856
|
-
);
|
|
857
|
-
useEffect(() => {
|
|
858
|
-
return () => {
|
|
859
|
-
resetLayout();
|
|
860
|
-
};
|
|
861
|
-
}, [resetLayout]);
|
|
862
|
-
return {
|
|
863
|
-
sidebarWidth,
|
|
864
|
-
applyLayout,
|
|
865
|
-
resetLayout,
|
|
866
|
-
updateWidth,
|
|
867
|
-
startResize,
|
|
868
|
-
isResizing,
|
|
869
|
-
getSidebarStyles,
|
|
870
|
-
getFloatingStyles,
|
|
871
|
-
getFabStyles
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
__name(useChatLayout, "useChatLayout");
|
|
875
|
-
function formatTime(date) {
|
|
876
|
-
return date.toLocaleTimeString("en-US", {
|
|
877
|
-
hour: "2-digit",
|
|
878
|
-
minute: "2-digit"
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
__name(formatTime, "formatTime");
|
|
882
|
-
var MessageBubble = React.memo(
|
|
883
|
-
({ message, isCompact = false }) => {
|
|
884
|
-
const isUser = message.role === "user";
|
|
885
|
-
const isAssistant = message.role === "assistant";
|
|
886
|
-
const { user, isAuthenticated } = useAuth();
|
|
887
|
-
const showUserAvatar = isUser && isAuthenticated && user;
|
|
888
|
-
const userAvatar = user?.avatar || "";
|
|
889
|
-
const userDisplayName = user?.display_username || user?.email || "User";
|
|
890
|
-
const userInitial = userDisplayName.charAt(0).toUpperCase();
|
|
891
|
-
const avatarSize = isCompact ? "28px" : "36px";
|
|
892
|
-
const iconSize = isCompact ? "h-3.5 w-3.5" : "h-4 w-4";
|
|
893
|
-
return /* @__PURE__ */ jsxs(
|
|
894
|
-
"div",
|
|
895
|
-
{
|
|
896
|
-
className: `flex gap-3 animate-in fade-in slide-in-from-bottom-2 duration-300 max-w-full overflow-hidden ${isUser ? "flex-row-reverse" : ""}`,
|
|
897
|
-
children: [
|
|
898
|
-
showUserAvatar ? (
|
|
899
|
-
// Authenticated user avatar
|
|
900
|
-
/* @__PURE__ */ jsxs(Avatar, { className: "flex-shrink-0", style: { width: avatarSize, height: avatarSize }, children: [
|
|
901
|
-
/* @__PURE__ */ jsx(AvatarImage, { src: userAvatar, alt: userDisplayName }),
|
|
902
|
-
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary text-primary-foreground text-xs", children: userInitial })
|
|
903
|
-
] })
|
|
904
|
-
) : isUser ? (
|
|
905
|
-
// Guest user icon
|
|
906
|
-
/* @__PURE__ */ jsx(
|
|
907
|
-
"div",
|
|
908
|
-
{
|
|
909
|
-
className: "flex-shrink-0 rounded-full flex items-center justify-center bg-primary text-primary-foreground",
|
|
910
|
-
style: { width: avatarSize, height: avatarSize },
|
|
911
|
-
children: /* @__PURE__ */ jsx(User, { className: iconSize })
|
|
912
|
-
}
|
|
913
|
-
)
|
|
914
|
-
) : (
|
|
915
|
-
// Bot icon
|
|
916
|
-
/* @__PURE__ */ jsx(
|
|
917
|
-
"div",
|
|
918
|
-
{
|
|
919
|
-
className: "flex-shrink-0 rounded-full flex items-center justify-center bg-muted text-muted-foreground",
|
|
920
|
-
style: { width: avatarSize, height: avatarSize },
|
|
921
|
-
children: /* @__PURE__ */ jsx(Bot, { className: iconSize })
|
|
922
|
-
}
|
|
923
|
-
)
|
|
924
|
-
),
|
|
925
|
-
/* @__PURE__ */ jsxs("div", { className: `flex-1 min-w-0 ${isUser ? "max-w-[80%] ml-auto" : "max-w-[85%]"}`, children: [
|
|
926
|
-
/* @__PURE__ */ jsxs("div", { className: `flex items-baseline gap-2 mb-1 ${isUser ? "justify-end" : ""}`, children: [
|
|
927
|
-
/* @__PURE__ */ jsx("span", { className: `font-medium ${isCompact ? "text-xs" : "text-sm"}`, children: isUser ? userDisplayName : "DjangoCFG AI" }),
|
|
928
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatTime(message.timestamp) })
|
|
929
|
-
] }),
|
|
930
|
-
/* @__PURE__ */ jsx(
|
|
931
|
-
Card,
|
|
932
|
-
{
|
|
933
|
-
className: `transition-all duration-200 ${isUser ? "bg-primary text-primary-foreground ml-auto" : "bg-muted"}`,
|
|
934
|
-
children: /* @__PURE__ */ jsxs(CardContent, { className: isCompact ? "p-2" : "p-3", children: [
|
|
935
|
-
/* @__PURE__ */ jsxs("div", { className: `${isCompact ? "text-xs" : "text-sm"} overflow-hidden`, style: { overflowWrap: "anywhere", wordBreak: "break-word" }, children: [
|
|
936
|
-
/* @__PURE__ */ jsx(
|
|
937
|
-
MarkdownMessage,
|
|
938
|
-
{
|
|
939
|
-
content: message.content,
|
|
940
|
-
isUser,
|
|
941
|
-
isCompact
|
|
942
|
-
}
|
|
943
|
-
),
|
|
944
|
-
message.isStreaming && /* @__PURE__ */ jsx(Loader2, { className: "inline-block ml-1 h-3 w-3 animate-spin" })
|
|
945
|
-
] }),
|
|
946
|
-
isAssistant && message.sources && message.sources.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3 pt-2 border-t border-border/50", children: [
|
|
947
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-1.5", children: "Related docs:" }),
|
|
948
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: message.sources.slice(0, 3).map((source, idx) => /* @__PURE__ */ jsx(
|
|
949
|
-
"a",
|
|
950
|
-
{
|
|
951
|
-
href: source.url || source.path,
|
|
952
|
-
target: "_blank",
|
|
953
|
-
rel: "noopener noreferrer",
|
|
954
|
-
className: "inline-block",
|
|
955
|
-
children: /* @__PURE__ */ jsxs(
|
|
956
|
-
Badge,
|
|
957
|
-
{
|
|
958
|
-
variant: "outline",
|
|
959
|
-
className: "text-xs gap-1 hover:bg-primary/10 transition-colors cursor-pointer",
|
|
960
|
-
children: [
|
|
961
|
-
source.title,
|
|
962
|
-
source.section && ` - ${source.section}`,
|
|
963
|
-
/* @__PURE__ */ jsx(ExternalLink, { className: "h-2.5 w-2.5" })
|
|
964
|
-
]
|
|
965
|
-
}
|
|
966
|
-
)
|
|
967
|
-
},
|
|
968
|
-
idx
|
|
969
|
-
)) })
|
|
970
|
-
] })
|
|
971
|
-
] })
|
|
972
|
-
}
|
|
973
|
-
)
|
|
974
|
-
] })
|
|
975
|
-
]
|
|
976
|
-
}
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
);
|
|
980
|
-
MessageBubble.displayName = "MessageBubble";
|
|
981
|
-
var ChatMessages = forwardRef(
|
|
982
|
-
({
|
|
983
|
-
messages,
|
|
984
|
-
isLoading,
|
|
985
|
-
greeting,
|
|
986
|
-
onStopStreaming,
|
|
987
|
-
isCompact = false,
|
|
988
|
-
largeGreetingIcon = false,
|
|
989
|
-
greetingIcon = "bot",
|
|
990
|
-
greetingTitle
|
|
991
|
-
}, ref) => {
|
|
992
|
-
const scrollContainerRef = useRef(null);
|
|
993
|
-
useImperativeHandle(ref, () => ({
|
|
994
|
-
scrollToBottom: /* @__PURE__ */ __name(() => {
|
|
995
|
-
scrollContainerRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
|
996
|
-
}, "scrollToBottom"),
|
|
997
|
-
scrollToLastMessage: /* @__PURE__ */ __name(() => {
|
|
998
|
-
scrollContainerRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
|
999
|
-
}, "scrollToLastMessage")
|
|
1000
|
-
}), []);
|
|
1001
|
-
const GreetingIcon = greetingIcon === "message" ? MessageSquare : Bot;
|
|
1002
|
-
const iconSize = largeGreetingIcon ? { container: "64px", icon: "h-8 w-8" } : { container: "48px", icon: "h-6 w-6" };
|
|
1003
|
-
const padding = largeGreetingIcon ? "py-12" : "py-8";
|
|
1004
|
-
return /* @__PURE__ */ jsx("div", { ref: scrollContainerRef, className: "h-full w-full overflow-y-auto flex flex-col-reverse", children: /* @__PURE__ */ jsxs("div", { className: `${isCompact ? "p-3" : "p-4"} space-y-4 max-w-full overflow-x-hidden`, children: [
|
|
1005
|
-
messages.length === 0 && greeting && /* @__PURE__ */ jsxs("div", { className: `text-center ${padding}`, children: [
|
|
1006
|
-
/* @__PURE__ */ jsx(
|
|
1007
|
-
"div",
|
|
1008
|
-
{
|
|
1009
|
-
className: "mx-auto mb-4 rounded-full bg-primary/10 flex items-center justify-center",
|
|
1010
|
-
style: { width: iconSize.container, height: iconSize.container },
|
|
1011
|
-
children: /* @__PURE__ */ jsx(GreetingIcon, { className: `${iconSize.icon} text-primary` })
|
|
1012
|
-
}
|
|
1013
|
-
),
|
|
1014
|
-
greetingTitle && /* @__PURE__ */ jsx("h4", { className: "font-medium mb-2", children: greetingTitle }),
|
|
1015
|
-
/* @__PURE__ */ jsx("p", { className: `text-sm text-muted-foreground ${largeGreetingIcon ? "max-w-[300px]" : "max-w-[280px]"} mx-auto`, children: greeting })
|
|
1016
|
-
] }),
|
|
1017
|
-
messages.map((message) => /* @__PURE__ */ jsx("div", { "data-message-bubble": true, children: /* @__PURE__ */ jsx(MessageBubble, { message, isCompact }) }, message.id)),
|
|
1018
|
-
isLoading && messages.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-muted-foreground text-sm", children: [
|
|
1019
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1020
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
1021
|
-
/* @__PURE__ */ jsx("span", { className: "animate-bounce", style: { animationDelay: "0ms" }, children: "." }),
|
|
1022
|
-
/* @__PURE__ */ jsx("span", { className: "animate-bounce", style: { animationDelay: "150ms" }, children: "." }),
|
|
1023
|
-
/* @__PURE__ */ jsx("span", { className: "animate-bounce", style: { animationDelay: "300ms" }, children: "." })
|
|
1024
|
-
] }),
|
|
1025
|
-
/* @__PURE__ */ jsx("span", { children: "Generating response..." })
|
|
1026
|
-
] }),
|
|
1027
|
-
onStopStreaming && /* @__PURE__ */ jsxs(
|
|
1028
|
-
Button,
|
|
1029
|
-
{
|
|
1030
|
-
variant: "ghost",
|
|
1031
|
-
size: "sm",
|
|
1032
|
-
onClick: onStopStreaming,
|
|
1033
|
-
className: "h-6 px-2 text-xs",
|
|
1034
|
-
children: [
|
|
1035
|
-
/* @__PURE__ */ jsx(StopCircle, { className: "h-3 w-3 mr-1" }),
|
|
1036
|
-
"Stop"
|
|
1037
|
-
]
|
|
1038
|
-
}
|
|
1039
|
-
)
|
|
1040
|
-
] })
|
|
1041
|
-
] }) });
|
|
1042
|
-
}
|
|
1043
|
-
);
|
|
1044
|
-
ChatMessages.displayName = "ChatMessages";
|
|
1045
|
-
var AIMessageInput = React.memo(
|
|
1046
|
-
({
|
|
1047
|
-
onSend,
|
|
1048
|
-
disabled = false,
|
|
1049
|
-
isLoading = false,
|
|
1050
|
-
placeholder = "Ask about DjangoCFG...",
|
|
1051
|
-
maxRows = 5
|
|
1052
|
-
}) => {
|
|
1053
|
-
const [value, setValue] = useState("");
|
|
1054
|
-
const textareaRef = useRef(null);
|
|
1055
|
-
const adjustHeight = useCallback(() => {
|
|
1056
|
-
const textarea = textareaRef.current;
|
|
1057
|
-
if (!textarea) return;
|
|
1058
|
-
textarea.style.height = "auto";
|
|
1059
|
-
const lineHeight = 24;
|
|
1060
|
-
const minHeight = 44;
|
|
1061
|
-
const maxHeight = lineHeight * maxRows + 20;
|
|
1062
|
-
const newHeight = Math.min(Math.max(textarea.scrollHeight, minHeight), maxHeight);
|
|
1063
|
-
textarea.style.height = `${newHeight}px`;
|
|
1064
|
-
}, [maxRows]);
|
|
1065
|
-
useEffect(() => {
|
|
1066
|
-
adjustHeight();
|
|
1067
|
-
}, [value, adjustHeight]);
|
|
1068
|
-
const handleSubmit = useCallback(
|
|
1069
|
-
(e) => {
|
|
1070
|
-
e?.preventDefault();
|
|
1071
|
-
const trimmed = value.trim();
|
|
1072
|
-
if (!trimmed || disabled || isLoading) return;
|
|
1073
|
-
onSend(trimmed);
|
|
1074
|
-
setValue("");
|
|
1075
|
-
if (textareaRef.current) {
|
|
1076
|
-
textareaRef.current.style.height = "auto";
|
|
1077
|
-
}
|
|
1078
|
-
textareaRef.current?.focus();
|
|
1079
|
-
},
|
|
1080
|
-
[value, disabled, isLoading, onSend]
|
|
1081
|
-
);
|
|
1082
|
-
const handleKeyDown = useCallback(
|
|
1083
|
-
(e) => {
|
|
1084
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
1085
|
-
e.preventDefault();
|
|
1086
|
-
handleSubmit();
|
|
1087
|
-
}
|
|
1088
|
-
},
|
|
1089
|
-
[handleSubmit]
|
|
1090
|
-
);
|
|
1091
|
-
const canSend = value.trim().length > 0 && !disabled && !isLoading;
|
|
1092
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "w-full", children: [
|
|
1093
|
-
/* @__PURE__ */ jsxs(
|
|
1094
|
-
"div",
|
|
1095
|
-
{
|
|
1096
|
-
className: "relative flex items-end rounded-2xl border border-input bg-background transition-colors focus-within:ring-1 focus-within:ring-ring focus-within:border-ring",
|
|
1097
|
-
style: { minHeight: "44px" },
|
|
1098
|
-
children: [
|
|
1099
|
-
/* @__PURE__ */ jsx(
|
|
1100
|
-
"textarea",
|
|
1101
|
-
{
|
|
1102
|
-
ref: textareaRef,
|
|
1103
|
-
value,
|
|
1104
|
-
onChange: (e) => setValue(e.target.value),
|
|
1105
|
-
onKeyDown: handleKeyDown,
|
|
1106
|
-
placeholder,
|
|
1107
|
-
disabled: disabled || isLoading,
|
|
1108
|
-
rows: 1,
|
|
1109
|
-
className: "flex-1 resize-none bg-transparent px-4 py-3 text-sm placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 pr-12",
|
|
1110
|
-
style: {
|
|
1111
|
-
minHeight: "44px",
|
|
1112
|
-
maxHeight: `${24 * maxRows + 20}px`,
|
|
1113
|
-
lineHeight: "1.5rem"
|
|
1114
|
-
},
|
|
1115
|
-
autoComplete: "off"
|
|
1116
|
-
}
|
|
1117
|
-
),
|
|
1118
|
-
/* @__PURE__ */ jsx(
|
|
1119
|
-
"div",
|
|
1120
|
-
{
|
|
1121
|
-
className: "absolute flex items-center justify-center",
|
|
1122
|
-
style: {
|
|
1123
|
-
right: "6px",
|
|
1124
|
-
bottom: "6px"
|
|
1125
|
-
},
|
|
1126
|
-
children: /* @__PURE__ */ jsx(
|
|
1127
|
-
Button,
|
|
1128
|
-
{
|
|
1129
|
-
type: "submit",
|
|
1130
|
-
size: "icon",
|
|
1131
|
-
disabled: !canSend,
|
|
1132
|
-
className: "h-8 w-8 rounded-full transition-all",
|
|
1133
|
-
style: {
|
|
1134
|
-
opacity: canSend ? 1 : 0.5
|
|
1135
|
-
},
|
|
1136
|
-
children: isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
|
|
1137
|
-
}
|
|
1138
|
-
)
|
|
1139
|
-
}
|
|
1140
|
-
)
|
|
1141
|
-
]
|
|
1142
|
-
}
|
|
1143
|
-
),
|
|
1144
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground text-center", children: "Press Enter to send, Shift+Enter for new line" })
|
|
1145
|
-
] });
|
|
1146
|
-
}
|
|
1147
|
-
);
|
|
1148
|
-
AIMessageInput.displayName = "AIMessageInput";
|
|
1149
|
-
var ChatPanel = React.memo(() => {
|
|
1150
|
-
const {
|
|
1151
|
-
messages,
|
|
1152
|
-
isLoading,
|
|
1153
|
-
config,
|
|
1154
|
-
isMobile,
|
|
1155
|
-
sendMessage,
|
|
1156
|
-
closeChat,
|
|
1157
|
-
setDisplayMode,
|
|
1158
|
-
stopStreaming,
|
|
1159
|
-
clearMessages
|
|
1160
|
-
} = useAIChatContext();
|
|
1161
|
-
const panelStyles = isMobile ? {
|
|
1162
|
-
position: "absolute",
|
|
1163
|
-
top: 0,
|
|
1164
|
-
left: 0,
|
|
1165
|
-
right: 0,
|
|
1166
|
-
bottom: 0,
|
|
1167
|
-
width: "100%",
|
|
1168
|
-
height: "100%",
|
|
1169
|
-
maxHeight: "100dvh",
|
|
1170
|
-
borderRadius: 0,
|
|
1171
|
-
display: "flex",
|
|
1172
|
-
flexDirection: "column",
|
|
1173
|
-
margin: 0,
|
|
1174
|
-
border: "none"
|
|
1175
|
-
} : {
|
|
1176
|
-
width: "380px",
|
|
1177
|
-
height: "520px",
|
|
1178
|
-
maxHeight: "calc(100vh - 100px)"
|
|
1179
|
-
};
|
|
1180
|
-
return /* @__PURE__ */ jsxs(
|
|
1181
|
-
Card,
|
|
1182
|
-
{
|
|
1183
|
-
className: `flex flex-col ${isMobile ? "rounded-none border-0 shadow-none" : "shadow-2xl border-border/50"}`,
|
|
1184
|
-
style: panelStyles,
|
|
1185
|
-
children: [
|
|
1186
|
-
/* @__PURE__ */ jsxs(CardHeader, { className: "flex flex-row items-center justify-between p-3 border-b", children: [
|
|
1187
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1188
|
-
/* @__PURE__ */ jsx(
|
|
1189
|
-
"div",
|
|
1190
|
-
{
|
|
1191
|
-
className: "rounded-full bg-primary/10 flex items-center justify-center",
|
|
1192
|
-
style: { width: "32px", height: "32px" },
|
|
1193
|
-
children: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4 text-primary" })
|
|
1194
|
-
}
|
|
1195
|
-
),
|
|
1196
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1197
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-sm", children: config.title || "DjangoCFG AI" }),
|
|
1198
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "AI Assistant" })
|
|
1199
|
-
] })
|
|
1200
|
-
] }),
|
|
1201
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
1202
|
-
messages.length > 0 && /* @__PURE__ */ jsx(
|
|
1203
|
-
Button,
|
|
1204
|
-
{
|
|
1205
|
-
variant: "ghost",
|
|
1206
|
-
size: "icon",
|
|
1207
|
-
className: "h-8 w-8",
|
|
1208
|
-
onClick: clearMessages,
|
|
1209
|
-
title: "New chat",
|
|
1210
|
-
children: /* @__PURE__ */ jsx(RotateCcw, { className: "h-4 w-4" })
|
|
1211
|
-
}
|
|
1212
|
-
),
|
|
1213
|
-
!isMobile && /* @__PURE__ */ jsx(
|
|
1214
|
-
Button,
|
|
1215
|
-
{
|
|
1216
|
-
variant: "ghost",
|
|
1217
|
-
size: "icon",
|
|
1218
|
-
className: "h-8 w-8",
|
|
1219
|
-
onClick: () => setDisplayMode("sidebar"),
|
|
1220
|
-
title: "Switch to sidebar mode",
|
|
1221
|
-
children: /* @__PURE__ */ jsx(PanelRight, { className: "h-4 w-4" })
|
|
1222
|
-
}
|
|
1223
|
-
),
|
|
1224
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: closeChat, title: "Close", children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
|
|
1225
|
-
] })
|
|
1226
|
-
] }),
|
|
1227
|
-
/* @__PURE__ */ jsx(CardContent, { className: "flex-1 p-0 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1228
|
-
ChatMessages,
|
|
1229
|
-
{
|
|
1230
|
-
messages,
|
|
1231
|
-
isLoading,
|
|
1232
|
-
greeting: config.greeting,
|
|
1233
|
-
onStopStreaming: stopStreaming,
|
|
1234
|
-
isCompact: true,
|
|
1235
|
-
greetingIcon: "bot"
|
|
1236
|
-
}
|
|
1237
|
-
) }),
|
|
1238
|
-
/* @__PURE__ */ jsx(CardFooter, { className: "p-3 border-t", children: /* @__PURE__ */ jsx(
|
|
1239
|
-
AIMessageInput,
|
|
1240
|
-
{
|
|
1241
|
-
onSend: sendMessage,
|
|
1242
|
-
isLoading,
|
|
1243
|
-
placeholder: config.placeholder
|
|
1244
|
-
}
|
|
1245
|
-
) })
|
|
1246
|
-
]
|
|
1247
|
-
}
|
|
1248
|
-
);
|
|
1249
|
-
});
|
|
1250
|
-
ChatPanel.displayName = "ChatPanel";
|
|
1251
|
-
var ChatSidebar = React.memo(({
|
|
1252
|
-
resizeHandleWidth = 12,
|
|
1253
|
-
showResizeIcon = true,
|
|
1254
|
-
resizeHandleClassName
|
|
1255
|
-
}) => {
|
|
1256
|
-
const {
|
|
1257
|
-
messages,
|
|
1258
|
-
isLoading,
|
|
1259
|
-
config,
|
|
1260
|
-
sendMessage,
|
|
1261
|
-
closeChat,
|
|
1262
|
-
setDisplayMode,
|
|
1263
|
-
stopStreaming,
|
|
1264
|
-
clearMessages
|
|
1265
|
-
} = useAIChatContext();
|
|
1266
|
-
const { applyLayout, getSidebarStyles, startResize, isResizing } = useChatLayout();
|
|
1267
|
-
useEffect(() => {
|
|
1268
|
-
applyLayout("sidebar");
|
|
1269
|
-
return () => {
|
|
1270
|
-
applyLayout("closed");
|
|
1271
|
-
};
|
|
1272
|
-
}, []);
|
|
1273
|
-
const sidebarStyles = getSidebarStyles();
|
|
1274
|
-
return /* @__PURE__ */ jsxs(
|
|
1275
|
-
"div",
|
|
1276
|
-
{
|
|
1277
|
-
className: "flex bg-background",
|
|
1278
|
-
style: sidebarStyles,
|
|
1279
|
-
"data-chat-sidebar-panel": true,
|
|
1280
|
-
children: [
|
|
1281
|
-
/* @__PURE__ */ jsx(
|
|
1282
|
-
"div",
|
|
1283
|
-
{
|
|
1284
|
-
className: `
|
|
1285
|
-
flex items-center justify-center cursor-ew-resize
|
|
1286
|
-
border-l border-border transition-colors select-none flex-shrink-0
|
|
1287
|
-
${isResizing ? "bg-primary/20" : "bg-muted/30 hover:bg-muted/50"}
|
|
1288
|
-
${resizeHandleClassName || ""}
|
|
1289
|
-
`,
|
|
1290
|
-
style: { width: resizeHandleWidth },
|
|
1291
|
-
onMouseDown: startResize,
|
|
1292
|
-
title: "Drag to resize",
|
|
1293
|
-
children: showResizeIcon && /* @__PURE__ */ jsx(GripVertical, { className: `h-4 w-4 ${isResizing ? "text-primary" : "text-muted-foreground/50"}` })
|
|
1294
|
-
}
|
|
1295
|
-
),
|
|
1296
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-w-0", children: [
|
|
1297
|
-
/* @__PURE__ */ jsxs(
|
|
1298
|
-
"div",
|
|
1299
|
-
{
|
|
1300
|
-
className: "flex items-center justify-between px-4 border-b border-border bg-muted/30",
|
|
1301
|
-
style: { height: "var(--nextra-navbar-height, 64px)", minHeight: "var(--nextra-navbar-height, 64px)" },
|
|
1302
|
-
children: [
|
|
1303
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1304
|
-
/* @__PURE__ */ jsx(
|
|
1305
|
-
"div",
|
|
1306
|
-
{
|
|
1307
|
-
className: "rounded-full bg-primary/10 flex items-center justify-center",
|
|
1308
|
-
style: { width: "32px", height: "32px" },
|
|
1309
|
-
children: /* @__PURE__ */ jsx(Bot, { className: "h-4 w-4 text-primary" })
|
|
1310
|
-
}
|
|
1311
|
-
),
|
|
1312
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
1313
|
-
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-sm", children: config.title || "DjangoCFG AI" }),
|
|
1314
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "AI Assistant" })
|
|
1315
|
-
] })
|
|
1316
|
-
] }),
|
|
1317
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
1318
|
-
messages.length > 0 && /* @__PURE__ */ jsx(
|
|
1319
|
-
Button,
|
|
1320
|
-
{
|
|
1321
|
-
variant: "ghost",
|
|
1322
|
-
size: "icon",
|
|
1323
|
-
className: "h-8 w-8",
|
|
1324
|
-
onClick: clearMessages,
|
|
1325
|
-
title: "New chat",
|
|
1326
|
-
children: /* @__PURE__ */ jsx(RotateCcw, { className: "h-4 w-4" })
|
|
1327
|
-
}
|
|
1328
|
-
),
|
|
1329
|
-
/* @__PURE__ */ jsx(
|
|
1330
|
-
Button,
|
|
1331
|
-
{
|
|
1332
|
-
variant: "ghost",
|
|
1333
|
-
size: "icon",
|
|
1334
|
-
className: "h-8 w-8",
|
|
1335
|
-
onClick: () => setDisplayMode("floating"),
|
|
1336
|
-
title: "Switch to floating mode",
|
|
1337
|
-
children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
|
|
1338
|
-
}
|
|
1339
|
-
),
|
|
1340
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", className: "h-8 w-8", onClick: closeChat, title: "Close chat", children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) })
|
|
1341
|
-
] })
|
|
1342
|
-
]
|
|
1343
|
-
}
|
|
1344
|
-
),
|
|
1345
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
1346
|
-
ChatMessages,
|
|
1347
|
-
{
|
|
1348
|
-
messages,
|
|
1349
|
-
isLoading,
|
|
1350
|
-
greeting: config.greeting,
|
|
1351
|
-
onStopStreaming: stopStreaming,
|
|
1352
|
-
isCompact: false,
|
|
1353
|
-
largeGreetingIcon: true,
|
|
1354
|
-
greetingIcon: "message",
|
|
1355
|
-
greetingTitle: "How can I help?"
|
|
1356
|
-
}
|
|
1357
|
-
) }),
|
|
1358
|
-
/* @__PURE__ */ jsx("div", { className: "p-4 border-t border-border bg-muted/30", children: /* @__PURE__ */ jsx(AIMessageInput, { onSend: sendMessage, isLoading, placeholder: config.placeholder }) })
|
|
1359
|
-
] })
|
|
1360
|
-
]
|
|
1361
|
-
}
|
|
1362
|
-
);
|
|
1363
|
-
});
|
|
1364
|
-
ChatSidebar.displayName = "ChatSidebar";
|
|
1365
|
-
var fabAnimationStyles = `
|
|
1366
|
-
@keyframes rotate-gradient {
|
|
1367
|
-
0% { transform: rotate(0deg); }
|
|
1368
|
-
100% { transform: rotate(360deg); }
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
@keyframes rotate-gradient-reverse {
|
|
1372
|
-
0% { transform: rotate(360deg); }
|
|
1373
|
-
100% { transform: rotate(0deg); }
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
@keyframes color-shift-glow {
|
|
1377
|
-
0%, 100% {
|
|
1378
|
-
box-shadow:
|
|
1379
|
-
0 0 20px rgba(251, 191, 36, 0.5),
|
|
1380
|
-
0 0 40px rgba(168, 85, 247, 0.3),
|
|
1381
|
-
0 0 60px rgba(20, 184, 166, 0.2);
|
|
1382
|
-
}
|
|
1383
|
-
33% {
|
|
1384
|
-
box-shadow:
|
|
1385
|
-
0 0 20px rgba(168, 85, 247, 0.5),
|
|
1386
|
-
0 0 40px rgba(20, 184, 166, 0.3),
|
|
1387
|
-
0 0 60px rgba(251, 191, 36, 0.2);
|
|
1388
|
-
}
|
|
1389
|
-
66% {
|
|
1390
|
-
box-shadow:
|
|
1391
|
-
0 0 20px rgba(20, 184, 166, 0.5),
|
|
1392
|
-
0 0 40px rgba(236, 72, 153, 0.3),
|
|
1393
|
-
0 0 60px rgba(168, 85, 247, 0.2);
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
@keyframes icon-pulse {
|
|
1398
|
-
0%, 100% {
|
|
1399
|
-
opacity: 1;
|
|
1400
|
-
transform: scale(1);
|
|
1401
|
-
filter: drop-shadow(0 0 4px rgba(251, 191, 36, 0.7));
|
|
1402
|
-
}
|
|
1403
|
-
50% {
|
|
1404
|
-
opacity: 0.85;
|
|
1405
|
-
transform: scale(1.15);
|
|
1406
|
-
filter: drop-shadow(0 0 12px rgba(251, 191, 36, 1));
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
@keyframes border-pulse {
|
|
1411
|
-
0%, 100% {
|
|
1412
|
-
opacity: 1;
|
|
1413
|
-
filter: blur(0px);
|
|
1414
|
-
}
|
|
1415
|
-
50% {
|
|
1416
|
-
opacity: 0.85;
|
|
1417
|
-
filter: blur(0.5px);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
@keyframes inner-glow-pulse {
|
|
1422
|
-
0%, 100% {
|
|
1423
|
-
box-shadow:
|
|
1424
|
-
inset 0 0 15px rgba(251, 191, 36, 0.3),
|
|
1425
|
-
inset 0 0 25px rgba(168, 85, 247, 0.2);
|
|
1426
|
-
}
|
|
1427
|
-
50% {
|
|
1428
|
-
box-shadow:
|
|
1429
|
-
inset 0 0 20px rgba(168, 85, 247, 0.35),
|
|
1430
|
-
inset 0 0 30px rgba(20, 184, 166, 0.25);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
@keyframes fab-entrance {
|
|
1435
|
-
0% {
|
|
1436
|
-
transform: scale(0);
|
|
1437
|
-
}
|
|
1438
|
-
50% {
|
|
1439
|
-
transform: scale(1.08);
|
|
1440
|
-
}
|
|
1441
|
-
70% {
|
|
1442
|
-
transform: scale(0.98);
|
|
1443
|
-
}
|
|
1444
|
-
85% {
|
|
1445
|
-
transform: scale(1.02);
|
|
1446
|
-
}
|
|
1447
|
-
100% {
|
|
1448
|
-
transform: scale(1);
|
|
1449
|
-
}
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
@keyframes fab-glow-entrance {
|
|
1453
|
-
0% {
|
|
1454
|
-
box-shadow: 0 0 0 rgba(251, 191, 36, 0);
|
|
1455
|
-
}
|
|
1456
|
-
40% {
|
|
1457
|
-
box-shadow:
|
|
1458
|
-
0 0 25px rgba(251, 191, 36, 0.6),
|
|
1459
|
-
0 0 50px rgba(168, 85, 247, 0.4),
|
|
1460
|
-
0 0 75px rgba(20, 184, 166, 0.25);
|
|
1461
|
-
}
|
|
1462
|
-
100% {
|
|
1463
|
-
box-shadow:
|
|
1464
|
-
0 0 20px rgba(251, 191, 36, 0.5),
|
|
1465
|
-
0 0 40px rgba(168, 85, 247, 0.3),
|
|
1466
|
-
0 0 60px rgba(20, 184, 166, 0.2);
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
`;
|
|
1470
|
-
var AIChatWidgetInternal = React.memo(({ className }) => {
|
|
1471
|
-
const { config, displayMode, openChat, isMobile } = useAIChatContext();
|
|
1472
|
-
const { getFabStyles, getFloatingStyles } = useChatLayout();
|
|
1473
|
-
const position = config.position || "bottom-right";
|
|
1474
|
-
const fabStyles = getFabStyles(position);
|
|
1475
|
-
const floatingStyles = getFloatingStyles(position);
|
|
1476
|
-
if (displayMode === "closed") {
|
|
1477
|
-
return /* @__PURE__ */ jsxs(Portal, { children: [
|
|
1478
|
-
/* @__PURE__ */ jsx("style", { children: fabAnimationStyles }),
|
|
1479
|
-
/* @__PURE__ */ jsx("div", { style: fabStyles, className: className || "", children: /* @__PURE__ */ jsx(
|
|
1480
|
-
"div",
|
|
1481
|
-
{
|
|
1482
|
-
className: "relative rounded-full",
|
|
1483
|
-
style: {
|
|
1484
|
-
width: "68px",
|
|
1485
|
-
height: "68px",
|
|
1486
|
-
overflow: "hidden",
|
|
1487
|
-
animation: "fab-entrance 0.6s cubic-bezier(0.34, 1.45, 0.64, 1) 0s 1 normal forwards, fab-glow-entrance 0.8s ease-out 0s 1 normal forwards, color-shift-glow 8s ease-in-out 0.6s infinite"
|
|
1488
|
-
},
|
|
1489
|
-
children: /* @__PURE__ */ jsxs(
|
|
1490
|
-
"div",
|
|
1491
|
-
{
|
|
1492
|
-
className: "absolute rounded-full",
|
|
1493
|
-
style: {
|
|
1494
|
-
inset: "0",
|
|
1495
|
-
overflow: "hidden"
|
|
1496
|
-
},
|
|
1497
|
-
children: [
|
|
1498
|
-
/* @__PURE__ */ jsx(
|
|
1499
|
-
"div",
|
|
1500
|
-
{
|
|
1501
|
-
className: "absolute rounded-full",
|
|
1502
|
-
style: {
|
|
1503
|
-
inset: "0",
|
|
1504
|
-
background: `conic-gradient(
|
|
1505
|
-
from 0deg,
|
|
1506
|
-
rgba(251, 191, 36, 1) 0%,
|
|
1507
|
-
rgba(251, 191, 36, 0.7) 8%,
|
|
1508
|
-
rgba(251, 191, 36, 0) 15%,
|
|
1509
|
-
rgba(168, 85, 247, 0) 20%,
|
|
1510
|
-
rgba(168, 85, 247, 0.7) 28%,
|
|
1511
|
-
rgba(168, 85, 247, 1) 35%,
|
|
1512
|
-
rgba(168, 85, 247, 0.7) 42%,
|
|
1513
|
-
rgba(168, 85, 247, 0) 50%,
|
|
1514
|
-
rgba(20, 184, 166, 0) 55%,
|
|
1515
|
-
rgba(20, 184, 166, 0.7) 63%,
|
|
1516
|
-
rgba(20, 184, 166, 1) 70%,
|
|
1517
|
-
rgba(20, 184, 166, 0.7) 77%,
|
|
1518
|
-
rgba(20, 184, 166, 0) 85%,
|
|
1519
|
-
rgba(236, 72, 153, 0) 88%,
|
|
1520
|
-
rgba(236, 72, 153, 0.7) 93%,
|
|
1521
|
-
rgba(236, 72, 153, 1) 97%,
|
|
1522
|
-
rgba(251, 191, 36, 1) 100%
|
|
1523
|
-
)`,
|
|
1524
|
-
animation: "rotate-gradient 7s linear infinite, border-pulse 4s ease-in-out infinite",
|
|
1525
|
-
filter: "blur(1px)",
|
|
1526
|
-
opacity: 0.95
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
),
|
|
1530
|
-
/* @__PURE__ */ jsx(
|
|
1531
|
-
"div",
|
|
1532
|
-
{
|
|
1533
|
-
className: "absolute rounded-full",
|
|
1534
|
-
style: {
|
|
1535
|
-
inset: "1px",
|
|
1536
|
-
background: `conic-gradient(
|
|
1537
|
-
from 180deg,
|
|
1538
|
-
rgba(168, 85, 247, 0.85) 0%,
|
|
1539
|
-
rgba(168, 85, 247, 0.5) 10%,
|
|
1540
|
-
rgba(168, 85, 247, 0) 20%,
|
|
1541
|
-
rgba(20, 184, 166, 0) 30%,
|
|
1542
|
-
rgba(20, 184, 166, 0.5) 40%,
|
|
1543
|
-
rgba(20, 184, 166, 0.85) 50%,
|
|
1544
|
-
rgba(20, 184, 166, 0.5) 60%,
|
|
1545
|
-
rgba(20, 184, 166, 0) 70%,
|
|
1546
|
-
rgba(251, 191, 36, 0) 75%,
|
|
1547
|
-
rgba(251, 191, 36, 0.5) 85%,
|
|
1548
|
-
rgba(251, 191, 36, 0.85) 95%,
|
|
1549
|
-
rgba(168, 85, 247, 0.85) 100%
|
|
1550
|
-
)`,
|
|
1551
|
-
animation: "rotate-gradient-reverse 9s linear infinite",
|
|
1552
|
-
filter: "blur(0.75px)",
|
|
1553
|
-
opacity: 0.75
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
),
|
|
1557
|
-
/* @__PURE__ */ jsx(
|
|
1558
|
-
"div",
|
|
1559
|
-
{
|
|
1560
|
-
className: "absolute rounded-full bg-background",
|
|
1561
|
-
style: {
|
|
1562
|
-
inset: "4px",
|
|
1563
|
-
animation: "inner-glow-pulse 5s ease-in-out infinite"
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
),
|
|
1567
|
-
/* @__PURE__ */ jsx(
|
|
1568
|
-
Button,
|
|
1569
|
-
{
|
|
1570
|
-
onClick: openChat,
|
|
1571
|
-
variant: "ghost",
|
|
1572
|
-
className: "absolute rounded-full hover:scale-105 transition-all duration-300 bg-background/80 hover:bg-background/95 border-0 backdrop-blur-sm",
|
|
1573
|
-
style: {
|
|
1574
|
-
inset: "2.5px",
|
|
1575
|
-
width: "auto",
|
|
1576
|
-
height: "auto"
|
|
1577
|
-
},
|
|
1578
|
-
children: /* @__PURE__ */ jsx(
|
|
1579
|
-
Zap,
|
|
1580
|
-
{
|
|
1581
|
-
className: "h-6 w-6",
|
|
1582
|
-
style: {
|
|
1583
|
-
animation: "icon-pulse 2.5s ease-in-out infinite",
|
|
1584
|
-
color: "#fbbf24",
|
|
1585
|
-
fill: "#fbbf24"
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
)
|
|
1589
|
-
}
|
|
1590
|
-
)
|
|
1591
|
-
]
|
|
1592
|
-
}
|
|
1593
|
-
)
|
|
1594
|
-
}
|
|
1595
|
-
) })
|
|
1596
|
-
] });
|
|
1597
|
-
}
|
|
1598
|
-
if (displayMode === "sidebar") {
|
|
1599
|
-
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(ChatSidebar, {}) });
|
|
1600
|
-
}
|
|
1601
|
-
if (isMobile) {
|
|
1602
|
-
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
1603
|
-
"div",
|
|
1604
|
-
{
|
|
1605
|
-
className: "z-[400] overflow-hidden",
|
|
1606
|
-
style: {
|
|
1607
|
-
position: "fixed",
|
|
1608
|
-
top: 0,
|
|
1609
|
-
left: 0,
|
|
1610
|
-
right: 0,
|
|
1611
|
-
bottom: 0,
|
|
1612
|
-
width: "100vw",
|
|
1613
|
-
height: "100dvh"
|
|
1614
|
-
},
|
|
1615
|
-
children: /* @__PURE__ */ jsx(ChatPanel, {})
|
|
1616
|
-
}
|
|
1617
|
-
) });
|
|
1618
|
-
}
|
|
1619
|
-
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx("div", { style: floatingStyles, className: className || "", children: /* @__PURE__ */ jsx(ChatPanel, {}) }) });
|
|
1620
|
-
});
|
|
1621
|
-
AIChatWidgetInternal.displayName = "AIChatWidgetInternal";
|
|
1622
|
-
var AIChatWidget = /* @__PURE__ */ __name(({
|
|
1623
|
-
apiEndpoint,
|
|
1624
|
-
title = "DjangoCFG AI",
|
|
1625
|
-
placeholder = "Ask about DjangoCFG...",
|
|
1626
|
-
greeting = "Hi! I'm your DjangoCFG AI assistant powered by GPT. Ask me anything about configuration, features, or how to use the library.",
|
|
1627
|
-
position = "bottom-right",
|
|
1628
|
-
variant = "default",
|
|
1629
|
-
className,
|
|
1630
|
-
enableStreaming = true,
|
|
1631
|
-
autoDetectEnvironment = false
|
|
1632
|
-
}) => {
|
|
1633
|
-
const existingContext = useAIChatContextOptional();
|
|
1634
|
-
if (existingContext) {
|
|
1635
|
-
return /* @__PURE__ */ jsx(AIChatWidgetInternal, { className });
|
|
1636
|
-
}
|
|
1637
|
-
const finalApiEndpoint = apiEndpoint || getMcpEndpoints(autoDetectEnvironment).chat;
|
|
1638
|
-
return /* @__PURE__ */ jsx(
|
|
1639
|
-
AIChatProvider,
|
|
1640
|
-
{
|
|
1641
|
-
apiEndpoint: finalApiEndpoint,
|
|
1642
|
-
config: { title, placeholder, greeting, position, variant, autoDetectEnvironment },
|
|
1643
|
-
enableStreaming,
|
|
1644
|
-
children: /* @__PURE__ */ jsx(AIChatWidgetInternal, { className })
|
|
1645
|
-
}
|
|
1646
|
-
);
|
|
1647
|
-
}, "AIChatWidget");
|
|
1648
|
-
AIChatWidget.displayName = "AIChatWidget";
|
|
1649
|
-
|
|
1650
|
-
export { AIChatProvider, AIChatWidget, AIMessageInput, ChatPanel, ChatSidebar, MessageBubble, __name, storageKeys, useAIChat, useAIChatContext, useAIChatContextOptional, useChatLayout };
|
|
1651
|
-
//# sourceMappingURL=chunk-EI7TDN2G.mjs.map
|
|
1652
|
-
//# sourceMappingURL=chunk-EI7TDN2G.mjs.map
|