@djangocfg/ext-knowbase 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +177 -0
  2. package/dist/chunk-GNRC54ON.js +4308 -0
  3. package/dist/hooks.cjs +6003 -0
  4. package/dist/hooks.d.cts +610 -0
  5. package/dist/hooks.d.ts +610 -0
  6. package/dist/hooks.js +1486 -0
  7. package/dist/index.cjs +4450 -0
  8. package/dist/index.d.cts +4398 -0
  9. package/dist/index.d.ts +4398 -0
  10. package/dist/index.js +1 -0
  11. package/package.json +81 -0
  12. package/src/api/generated/ext_knowbase/_utils/fetchers/ext_knowbase__knowbase.ts +2983 -0
  13. package/src/api/generated/ext_knowbase/_utils/fetchers/index.ts +28 -0
  14. package/src/api/generated/ext_knowbase/_utils/hooks/ext_knowbase__knowbase.ts +999 -0
  15. package/src/api/generated/ext_knowbase/_utils/hooks/index.ts +28 -0
  16. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItem.schema.ts +33 -0
  17. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItemChunk.schema.ts +29 -0
  18. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItemChunkDetail.schema.ts +30 -0
  19. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItemChunkRequest.schema.ts +22 -0
  20. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItemDetail.schema.ts +35 -0
  21. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveItemRequest.schema.ts +22 -0
  22. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveProcessingResult.schema.ts +26 -0
  23. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveSearchRequestRequest.schema.ts +25 -0
  24. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveSearchResult.schema.ts +24 -0
  25. package/src/api/generated/ext_knowbase/_utils/schemas/ArchiveStatistics.schema.ts +28 -0
  26. package/src/api/generated/ext_knowbase/_utils/schemas/ChatHistory.schema.ts +22 -0
  27. package/src/api/generated/ext_knowbase/_utils/schemas/ChatMessage.schema.ts +27 -0
  28. package/src/api/generated/ext_knowbase/_utils/schemas/ChatQueryRequest.schema.ts +22 -0
  29. package/src/api/generated/ext_knowbase/_utils/schemas/ChatResponse.schema.ts +26 -0
  30. package/src/api/generated/ext_knowbase/_utils/schemas/ChatResponseRequest.schema.ts +26 -0
  31. package/src/api/generated/ext_knowbase/_utils/schemas/ChatSession.schema.ts +29 -0
  32. package/src/api/generated/ext_knowbase/_utils/schemas/ChatSessionCreateRequest.schema.ts +22 -0
  33. package/src/api/generated/ext_knowbase/_utils/schemas/ChatSessionRequest.schema.ts +25 -0
  34. package/src/api/generated/ext_knowbase/_utils/schemas/ChatSource.schema.ts +21 -0
  35. package/src/api/generated/ext_knowbase/_utils/schemas/ChatSourceRequest.schema.ts +21 -0
  36. package/src/api/generated/ext_knowbase/_utils/schemas/ChunkRevectorizationRequestRequest.schema.ts +20 -0
  37. package/src/api/generated/ext_knowbase/_utils/schemas/Document.schema.ts +32 -0
  38. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentArchive.schema.ts +44 -0
  39. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentArchiveDetail.schema.ts +48 -0
  40. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentArchiveList.schema.ts +35 -0
  41. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentArchiveRequest.schema.ts +21 -0
  42. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentCategory.schema.ts +23 -0
  43. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentCategoryRequest.schema.ts +21 -0
  44. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentCreateRequest.schema.ts +22 -0
  45. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentProcessingStatus.schema.ts +23 -0
  46. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentRequest.schema.ts +22 -0
  47. package/src/api/generated/ext_knowbase/_utils/schemas/DocumentStats.schema.ts +25 -0
  48. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedArchiveItemChunkList.schema.ts +24 -0
  49. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedArchiveItemList.schema.ts +24 -0
  50. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedArchiveSearchResultList.schema.ts +24 -0
  51. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedChatResponseList.schema.ts +24 -0
  52. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedChatSessionList.schema.ts +24 -0
  53. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedDocumentArchiveListList.schema.ts +24 -0
  54. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedDocumentList.schema.ts +24 -0
  55. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedPublicCategoryList.schema.ts +24 -0
  56. package/src/api/generated/ext_knowbase/_utils/schemas/PaginatedPublicDocumentListList.schema.ts +24 -0
  57. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedArchiveItemChunkRequest.schema.ts +22 -0
  58. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedArchiveItemRequest.schema.ts +22 -0
  59. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedChatResponseRequest.schema.ts +26 -0
  60. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedChatSessionRequest.schema.ts +25 -0
  61. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedDocumentArchiveRequest.schema.ts +21 -0
  62. package/src/api/generated/ext_knowbase/_utils/schemas/PatchedDocumentRequest.schema.ts +22 -0
  63. package/src/api/generated/ext_knowbase/_utils/schemas/PublicCategory.schema.ts +21 -0
  64. package/src/api/generated/ext_knowbase/_utils/schemas/PublicDocument.schema.ts +25 -0
  65. package/src/api/generated/ext_knowbase/_utils/schemas/PublicDocumentList.schema.ts +24 -0
  66. package/src/api/generated/ext_knowbase/_utils/schemas/VectorizationResult.schema.ts +24 -0
  67. package/src/api/generated/ext_knowbase/_utils/schemas/VectorizationStatistics.schema.ts +26 -0
  68. package/src/api/generated/ext_knowbase/_utils/schemas/index.ts +70 -0
  69. package/src/api/generated/ext_knowbase/api-instance.ts +131 -0
  70. package/src/api/generated/ext_knowbase/client.ts +301 -0
  71. package/src/api/generated/ext_knowbase/enums.ts +241 -0
  72. package/src/api/generated/ext_knowbase/errors.ts +116 -0
  73. package/src/api/generated/ext_knowbase/ext_knowbase__knowbase/client.ts +666 -0
  74. package/src/api/generated/ext_knowbase/ext_knowbase__knowbase/index.ts +2 -0
  75. package/src/api/generated/ext_knowbase/ext_knowbase__knowbase/models.ts +1120 -0
  76. package/src/api/generated/ext_knowbase/http.ts +103 -0
  77. package/src/api/generated/ext_knowbase/index.ts +273 -0
  78. package/src/api/generated/ext_knowbase/logger.ts +259 -0
  79. package/src/api/generated/ext_knowbase/retry.ts +175 -0
  80. package/src/api/generated/ext_knowbase/schema.json +5865 -0
  81. package/src/api/generated/ext_knowbase/storage.ts +161 -0
  82. package/src/api/generated/ext_knowbase/validation-events.ts +133 -0
  83. package/src/api/index.ts +9 -0
  84. package/src/components/Chat/ChatUIContext.tsx +110 -0
  85. package/src/components/Chat/ChatWidget.tsx +476 -0
  86. package/src/components/Chat/README.md +122 -0
  87. package/src/components/Chat/components/MessageInput.tsx +124 -0
  88. package/src/components/Chat/components/MessageList.tsx +169 -0
  89. package/src/components/Chat/components/SessionList.tsx +192 -0
  90. package/src/components/Chat/components/index.ts +9 -0
  91. package/src/components/Chat/hooks/index.ts +6 -0
  92. package/src/components/Chat/hooks/useInfiniteSessions.ts +81 -0
  93. package/src/components/Chat/index.tsx +45 -0
  94. package/src/components/Chat/types.ts +81 -0
  95. package/src/config.ts +20 -0
  96. package/src/contexts/knowbase/ChatContext.tsx +173 -0
  97. package/src/contexts/knowbase/DocumentsContext.tsx +306 -0
  98. package/src/contexts/knowbase/KnowbaseProvider.tsx +47 -0
  99. package/src/contexts/knowbase/SessionsContext.tsx +175 -0
  100. package/src/contexts/knowbase/index.ts +63 -0
  101. package/src/contexts/knowbase/types.ts +69 -0
  102. package/src/hooks/index.ts +28 -0
  103. package/src/index.ts +22 -0
  104. package/src/utils/logger.ts +10 -0
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Chat Widget Component
3
+ * RAG-powered chat interface with session management
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import React, { useCallback, useEffect, useState } from 'react';
9
+ import { createPortal } from 'react-dom';
10
+ import { usePathname } from 'next/navigation';
11
+ import { Card, CardContent, CardHeader, Button, useIsMobile, useLocalStorage } from '@djangocfg/ui-nextjs';
12
+ import {
13
+ MessageSquare,
14
+ X,
15
+ Maximize2,
16
+ Minimize2,
17
+ Plus,
18
+ List,
19
+ } from 'lucide-react';
20
+ import { useKnowbaseChatContext, useKnowbaseSessionsContext } from '../../contexts/knowbase';
21
+ import { Enums } from './types';
22
+ import { chatLogger } from '../../utils/logger';
23
+ import { useChatUI } from './ChatUIContext';
24
+ import { MessageList, MessageInput, SessionList } from './components';
25
+ import type { ChatWidgetProps, ChatMessageWithSources } from './types';
26
+
27
+ const WIDGET_MAX_WIDTH = 800;
28
+ const WIDGET_MAX_HEIGHT = 900;
29
+
30
+ // ─────────────────────────────────────────────────────────────────────────
31
+ // Chat Widget Component
32
+ // ─────────────────────────────────────────────────────────────────────────
33
+
34
+ export const ChatWidget: React.FC<ChatWidgetProps> = ({
35
+ autoOpen = false,
36
+ persistent = true,
37
+ className = '',
38
+ onToggle,
39
+ onMessage,
40
+ }) => {
41
+ const { sendQuery, getChatHistory } = useKnowbaseChatContext();
42
+ const { createSession, getSession } = useKnowbaseSessionsContext();
43
+ const {
44
+ uiState,
45
+ toggleChat,
46
+ expandChat,
47
+ collapseChat,
48
+ toggleSources,
49
+ toggleTimestamps,
50
+ } = useChatUI();
51
+
52
+ const pathname = usePathname();
53
+ const isMobile = useIsMobile();
54
+
55
+ const [isLoading, setIsLoading] = useState(false);
56
+ const [showSessions, setShowSessions] = useState(false);
57
+ const [mounted, setMounted] = useState(false);
58
+
59
+ // Persist session ID and messages in localStorage
60
+ const [currentSessionId, setCurrentSessionId] = useLocalStorage<string | null>(
61
+ 'chat_session_id',
62
+ null
63
+ );
64
+ const [messages, setMessages] = useLocalStorage<ChatMessageWithSources[]>(
65
+ 'chat_messages',
66
+ []
67
+ );
68
+
69
+ const isSupport = pathname.startsWith('/private/support');
70
+ const isContact = pathname.startsWith('/private/contact');
71
+
72
+ // Mount portal target
73
+ useEffect(() => {
74
+ setMounted(true);
75
+ }, []);
76
+
77
+ // if isSupport or isContact, don't render
78
+ useEffect(() => {
79
+ if (isSupport || isContact) {
80
+ setMounted(false);
81
+ } else {
82
+ setMounted(true);
83
+ }
84
+ }, [isSupport, isContact]);
85
+
86
+ // Auto-open on mount if specified
87
+ useEffect(() => {
88
+ if (autoOpen && !uiState.isOpen) {
89
+ toggleChat();
90
+ }
91
+ }, [autoOpen, uiState.isOpen, toggleChat]);
92
+
93
+ // Notify parent of toggle changes
94
+ useEffect(() => {
95
+ if (onToggle) {
96
+ onToggle(uiState.isOpen);
97
+ }
98
+ }, [uiState.isOpen, onToggle]);
99
+
100
+ // Load chat history when session changes (only if it's different from what we had)
101
+ useEffect(() => {
102
+ if (currentSessionId) {
103
+ // Only load from server if we don't have messages locally or session changed
104
+ if (messages.length === 0) {
105
+ loadChatHistory(currentSessionId);
106
+ }
107
+ }
108
+ }, [currentSessionId]);
109
+
110
+ // Load chat history
111
+ const loadChatHistory = async (sessionId: string) => {
112
+ try {
113
+ const history = await getChatHistory(sessionId);
114
+ if (history?.messages) {
115
+ // Convert ChatMessage[] to ChatMessageWithSources[]
116
+ const messagesWithSources: ChatMessageWithSources[] = history.messages.map(msg => ({
117
+ ...msg,
118
+ sources: undefined, // Sources come from ChatResponse, not ChatMessage
119
+ }));
120
+ setMessages(messagesWithSources);
121
+ }
122
+ } catch (error) {
123
+ chatLogger.error('Failed to load chat history:', error);
124
+ // If we can't load the session, clear it
125
+ setCurrentSessionId(null);
126
+ setMessages([]);
127
+ }
128
+ };
129
+
130
+ // Handle message sending
131
+ const handleSendMessage = useCallback(
132
+ async (message: string) => {
133
+ try {
134
+ setIsLoading(true);
135
+
136
+ // Create session if we don't have one
137
+ let sessionId = currentSessionId;
138
+ if (!sessionId) {
139
+ try {
140
+ const newSession = await createSession({
141
+ title: message.substring(0, 50), // Use first 50 chars as title
142
+ });
143
+ sessionId = newSession.id;
144
+ setCurrentSessionId(sessionId);
145
+ chatLogger.info('Created new chat session:', sessionId);
146
+ } catch (error) {
147
+ chatLogger.error('Failed to create session:', error);
148
+ // Continue without session if creation fails
149
+ }
150
+ }
151
+
152
+ // Add user message to UI
153
+ const userMessage: ChatMessageWithSources = {
154
+ id: `temp-${Date.now()}`,
155
+ role: Enums.ChatMessageRole.USER,
156
+ content: message,
157
+ cost_usd: 0,
158
+ created_at: new Date().toISOString(),
159
+ };
160
+ // Don't use functional form with useLocalStorage - use direct value
161
+ const updatedMessages = [...messages, userMessage];
162
+ setMessages(updatedMessages);
163
+
164
+ // Send query to backend with session ID
165
+ const response = await sendQuery({
166
+ query: message,
167
+ session_id: sessionId || undefined,
168
+ });
169
+
170
+ // Add assistant response to UI
171
+ if (response) {
172
+ const assistantMessage: ChatMessageWithSources = {
173
+ id: response.message_id,
174
+ role: Enums.ChatMessageRole.ASSISTANT,
175
+ content: response.content,
176
+ cost_usd: response.cost_usd,
177
+ tokens_used: response.tokens_used,
178
+ processing_time_ms: response.processing_time_ms,
179
+ created_at: new Date().toISOString(),
180
+ sources: response.sources || undefined,
181
+ };
182
+ // Don't use functional form with useLocalStorage - use direct value
183
+ const finalMessages = [...updatedMessages, assistantMessage];
184
+ setMessages(finalMessages);
185
+
186
+ if (onMessage) {
187
+ onMessage(assistantMessage);
188
+ }
189
+ }
190
+ } catch (error) {
191
+ chatLogger.error('Failed to send message:', error);
192
+ throw error;
193
+ } finally {
194
+ setIsLoading(false);
195
+ }
196
+ },
197
+ [sendQuery, currentSessionId, createSession, setCurrentSessionId, messages, setMessages, onMessage]
198
+ );
199
+
200
+ // Handle new chat creation
201
+ const handleNewChat = useCallback(() => {
202
+ setMessages([]);
203
+ setCurrentSessionId(null);
204
+ }, [setMessages, setCurrentSessionId]);
205
+
206
+ // Handle session selection
207
+ const handleSelectSession = useCallback((sessionId: string) => {
208
+ // Clear current messages before loading new session
209
+ setMessages([]);
210
+ setCurrentSessionId(sessionId);
211
+ }, [setMessages, setCurrentSessionId]);
212
+
213
+ // Don't render if not mounted (SSR safety)
214
+ if (!mounted) {
215
+ return null;
216
+ }
217
+
218
+ // Don't render if not open and not persistent
219
+ if (!uiState.isOpen && !persistent) {
220
+ return null;
221
+ }
222
+
223
+ // Mobile layout
224
+ if (isMobile) {
225
+ const mobileContent = (
226
+ <>
227
+ <div
228
+ className={`fixed inset-0 z-9999 bg-background ${
229
+ uiState.isOpen ? 'block' : 'hidden'
230
+ } ${className}`}
231
+ >
232
+ <div className="flex flex-col h-full">
233
+ {/* Mobile header */}
234
+ <div className="flex items-center justify-between p-4 border-b bg-background shadow-sm">
235
+ <div className="flex items-center gap-2">
236
+ <MessageSquare className="h-5 w-5 text-primary" />
237
+ <h2 className="font-semibold text-foreground">Knowledge Assistant</h2>
238
+ </div>
239
+
240
+ <div className="flex items-center gap-2">
241
+ <Button
242
+ variant="ghost"
243
+ size="sm"
244
+ onClick={() => setShowSessions(true)}
245
+ className="text-muted-foreground hover:text-foreground"
246
+ title="Sessions"
247
+ >
248
+ <List className="h-5 w-5" />
249
+ </Button>
250
+
251
+ <Button
252
+ variant="ghost"
253
+ size="sm"
254
+ onClick={handleNewChat}
255
+ className="text-muted-foreground hover:text-foreground"
256
+ title="New Chat"
257
+ >
258
+ <Plus className="h-5 w-5" />
259
+ </Button>
260
+
261
+ <Button
262
+ variant="ghost"
263
+ size="sm"
264
+ onClick={toggleChat}
265
+ className="text-muted-foreground hover:text-foreground"
266
+ >
267
+ <X className="h-5 w-5" />
268
+ </Button>
269
+ </div>
270
+ </div>
271
+
272
+ {/* Chat area */}
273
+ <div className="flex-1 flex flex-col overflow-hidden">
274
+ <MessageList
275
+ messages={messages}
276
+ isLoading={isLoading}
277
+ showSources={uiState.showSources}
278
+ showTimestamps={uiState.showTimestamps}
279
+ autoScroll={true}
280
+ className="flex-1"
281
+ />
282
+
283
+ <MessageInput
284
+ onSend={handleSendMessage}
285
+ isLoading={isLoading}
286
+ placeholder="Ask me anything..."
287
+ />
288
+ </div>
289
+ </div>
290
+ </div>
291
+
292
+ {/* Session List Drawer */}
293
+ <SessionList
294
+ isOpen={showSessions}
295
+ onClose={() => setShowSessions(false)}
296
+ onSelectSession={handleSelectSession}
297
+ />
298
+ </>
299
+ );
300
+
301
+ return createPortal(mobileContent, document.body);
302
+ }
303
+
304
+ // Desktop layout
305
+ const widgetWidth = uiState.isExpanded ? WIDGET_MAX_WIDTH : 400;
306
+ const widgetHeight = uiState.isExpanded ? WIDGET_MAX_HEIGHT : 600;
307
+
308
+ // When expanded, add top margin; when collapsed, stick to bottom
309
+ const widgetStyle = uiState.isExpanded
310
+ ? {
311
+ position: 'fixed' as const,
312
+ top: '24px',
313
+ bottom: '24px',
314
+ right: '24px',
315
+ left: 'auto',
316
+ width: widgetWidth,
317
+ height: 'auto',
318
+ zIndex: 40,
319
+ transformOrigin: 'bottom right' as const,
320
+ }
321
+ : {
322
+ position: 'fixed' as const,
323
+ bottom: '24px',
324
+ right: '24px',
325
+ width: widgetWidth,
326
+ height: widgetHeight,
327
+ zIndex: 40,
328
+ transformOrigin: 'bottom right' as const,
329
+ };
330
+
331
+ const content = (
332
+ <>
333
+ {/* Chat Window */}
334
+ {uiState.isOpen && (
335
+ <div
336
+ className="fixed z-40
337
+ animate-in fade-in zoom-in-95 slide-in-from-bottom-8
338
+ duration-500 ease-out"
339
+ style={widgetStyle}
340
+ >
341
+ <Card className="h-full flex flex-col shadow-2xl border-2 border-primary/20">
342
+ {/* Header */}
343
+ <CardHeader className="flex-row items-center justify-between space-y-0 pb-3 border-b">
344
+ <div className="flex items-center gap-2">
345
+ <MessageSquare className="h-5 w-5 text-primary" />
346
+ <h3 className="font-semibold text-foreground">Support</h3>
347
+ </div>
348
+
349
+ <div className="flex items-center gap-1">
350
+ <Button
351
+ variant="ghost"
352
+ size="sm"
353
+ onClick={() => setShowSessions(true)}
354
+ className="h-8 w-8 p-0"
355
+ title="Sessions"
356
+ >
357
+ <List className="h-4 w-4" />
358
+ </Button>
359
+
360
+ <Button
361
+ variant="ghost"
362
+ size="sm"
363
+ onClick={handleNewChat}
364
+ className="h-8 w-8 p-0"
365
+ title="New Chat"
366
+ >
367
+ <Plus className="h-4 w-4" />
368
+ </Button>
369
+
370
+ <Button
371
+ variant="ghost"
372
+ size="sm"
373
+ onClick={uiState.isExpanded ? collapseChat : expandChat}
374
+ className="h-8 w-8 p-0"
375
+ title={uiState.isExpanded ? 'Collapse' : 'Expand'}
376
+ >
377
+ {uiState.isExpanded ? (
378
+ <Minimize2 className="h-4 w-4" />
379
+ ) : (
380
+ <Maximize2 className="h-4 w-4" />
381
+ )}
382
+ </Button>
383
+
384
+ <Button
385
+ variant="ghost"
386
+ size="sm"
387
+ onClick={toggleChat}
388
+ className="h-8 w-8 p-0"
389
+ title="Close"
390
+ >
391
+ <X className="h-4 w-4" />
392
+ </Button>
393
+ </div>
394
+ </CardHeader>
395
+
396
+ {/* Content */}
397
+ <CardContent className="flex-1 flex flex-col p-0 overflow-hidden">
398
+ <MessageList
399
+ messages={messages}
400
+ isLoading={isLoading}
401
+ showSources={uiState.showSources}
402
+ showTimestamps={uiState.showTimestamps}
403
+ autoScroll={true}
404
+ className="flex-1"
405
+ />
406
+
407
+ <MessageInput
408
+ onSend={handleSendMessage}
409
+ isLoading={isLoading}
410
+ placeholder="Ask me anything..."
411
+ />
412
+ </CardContent>
413
+ </Card>
414
+ </div>
415
+ )}
416
+
417
+ {/* Floating Action Button (when closed) */}
418
+ {!uiState.isOpen && persistent && (
419
+ <div
420
+ className="fixed z-40 animate-in fade-in zoom-in-95 slide-in-from-bottom-8 duration-500"
421
+ style={{
422
+ bottom: '24px',
423
+ right: '24px',
424
+ zIndex: 40,
425
+ width: '64px',
426
+ height: '64px',
427
+ borderRadius: '50%',
428
+ animation: 'pulse-shadow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
429
+ }}
430
+ >
431
+ <style>{`
432
+ @keyframes pulse-shadow {
433
+ 0%, 100% {
434
+ box-shadow: 0 0 0 0 hsl(var(--primary) / 0.7);
435
+ }
436
+ 50% {
437
+ box-shadow: 0 0 0 20px hsl(var(--primary) / 0);
438
+ }
439
+ }
440
+ `}</style>
441
+ <Button
442
+ onClick={toggleChat}
443
+ className="w-full h-full rounded-full shadow-2xl
444
+ hover:scale-110 hover:rotate-12 active:scale-95
445
+ transition-all duration-300 ease-out
446
+ flex items-center justify-center
447
+ group"
448
+ style={{
449
+ backgroundColor: 'hsl(var(--primary))',
450
+ borderRadius: '50%',
451
+ padding: 0,
452
+ }}
453
+ title="Open Support Chat"
454
+ aria-label="Open Support Chat"
455
+ >
456
+ <MessageSquare
457
+ className="h-7 w-7 text-primary-foreground group-hover:scale-110 transition-transform duration-300"
458
+ strokeWidth={2}
459
+ />
460
+ </Button>
461
+ </div>
462
+ )}
463
+
464
+ {/* Session List Drawer */}
465
+ <SessionList
466
+ isOpen={showSessions}
467
+ onClose={() => setShowSessions(false)}
468
+ onSelectSession={handleSelectSession}
469
+ />
470
+ </>
471
+ );
472
+
473
+ // Render to portal
474
+ return createPortal(content, document.body);
475
+ };
476
+
@@ -0,0 +1,122 @@
1
+ # Knowledge Chat Widget
2
+
3
+ RAG-powered chat widget with beautiful animations powered by `tailwindcss-animate`.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Beautiful Animations**: Smooth transitions and micro-interactions
8
+ - 💬 **Real-time Chat**: RAG-powered responses with sources
9
+ - 📱 **Responsive**: Works on desktop and mobile
10
+ - 🗂️ **Session Management**: Create and manage chat sessions
11
+ - 🎯 **Context-aware**: Uses SWR hooks for data management
12
+
13
+ ## Animations
14
+
15
+ ### Widget Animations
16
+ - **Entry**: Fade in + slide from bottom with scale
17
+ - **Exit**: Fade out + slide to bottom with scale reduction
18
+ - **FAB Button**: Fade in + slide from bottom, hover scale, active scale
19
+
20
+ ### Message Animations
21
+ - **Message Entry**: Staggered fade in + slide from bottom (50ms delay between messages)
22
+ - **Message Bubble**: Hover shadow effect with smooth transition
23
+ - **Loading State**: Pulsing avatar with bouncing bot icon
24
+
25
+ ### Sources Animations
26
+ - **Container**: Fade in + slide from left with 100ms delay
27
+ - **Individual Badges**: Staggered zoom in (100ms per badge)
28
+ - **Badge Hover**: Scale up + active scale down
29
+
30
+ ### Session List Animations
31
+ - **Session Items**: Staggered fade in + slide from left (50ms delay)
32
+ - **Hover State**: Border color + shadow transition
33
+ - **Action Buttons**:
34
+ - Slide in from right on hover
35
+ - Scale up on hover, scale down on active
36
+
37
+ ### Input Animations
38
+ - **Textarea**: Focus ring animation
39
+ - **Send Button**: Scale up on hover, scale down on active
40
+ - **Send Icon**: Smooth transitions
41
+
42
+ ## Usage
43
+
44
+ ```tsx
45
+ import { KnowledgeChat } from '@djangocfg/layouts/snippets';
46
+
47
+ // Simple usage
48
+ <KnowledgeChat />
49
+
50
+ // With props
51
+ <KnowledgeChat
52
+ autoOpen={false}
53
+ persistent={true}
54
+ onToggle={(isOpen) => console.log('Chat toggled:', isOpen)}
55
+ onMessage={(message) => console.log('Message sent:', message)}
56
+ />
57
+ ```
58
+
59
+ ## Components
60
+
61
+ ### KnowledgeChat
62
+ Main component with all providers integrated.
63
+
64
+ ### ChatWidget
65
+ Core chat interface (use with providers manually if needed).
66
+
67
+ ### ChatUIProvider
68
+ Manages UI state (open/closed, expanded, sources visibility).
69
+
70
+ ### MessageList
71
+ Displays chat messages with sources and animations.
72
+
73
+ ### MessageInput
74
+ Input field with auto-resize and keyboard shortcuts.
75
+
76
+ ### SessionList
77
+ Drawer with chat sessions list.
78
+
79
+ ## Animation Timing
80
+
81
+ - **Fast**: 200ms (buttons, badges)
82
+ - **Normal**: 300ms (widget, messages, drawer)
83
+ - **Stagger Delay**: 50-100ms (lists)
84
+
85
+ ## Tailwind Classes Used
86
+
87
+ ### tailwindcss-animate
88
+ - `animate-in`
89
+ - `fade-in`
90
+ - `slide-in-from-bottom-{n}`
91
+ - `slide-in-from-left-{n}`
92
+ - `zoom-in-95`
93
+ - `duration-{n}`
94
+ - `delay-{n}`
95
+
96
+ ### Tailwind Core
97
+ - `transition-all`
98
+ - `transition-transform`
99
+ - `transition-colors`
100
+ - `duration-{n}`
101
+ - `ease-out`
102
+ - `animate-spin`
103
+ - `animate-pulse`
104
+ - `animate-bounce`
105
+ - `hover:scale-{n}`
106
+ - `active:scale-{n}`
107
+
108
+ ## Performance
109
+
110
+ All animations use CSS transforms and opacity for optimal performance:
111
+ - Hardware-accelerated transforms (translate, scale)
112
+ - Efficient opacity transitions
113
+ - No layout thrashing
114
+ - Minimal reflows
115
+
116
+ ## Accessibility
117
+
118
+ - Respects `prefers-reduced-motion`
119
+ - Keyboard navigation support
120
+ - ARIA labels
121
+ - Screen reader friendly
122
+