@rag-widget/chat-widget 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -0
- package/dist/components/ChatBubble.d.ts +4 -0
- package/dist/components/ChatWidget.d.ts +5 -0
- package/dist/components/ChatWindow.d.ts +4 -0
- package/dist/hooks/useChatWidget.d.ts +21 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +305 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +315 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/providers/ChatWidgetProvider.d.ts +18 -0
- package/dist/styles.css +1 -0
- package/dist/types/index.d.ts +68 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# @rag-widget/chat-widget
|
|
2
|
+
|
|
3
|
+
Embeddable React chat widget for RAG-powered knowledge bases.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### NPM/Yarn (React Applications)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @rag-widget/chat-widget
|
|
11
|
+
# or
|
|
12
|
+
yarn add @rag-widget/chat-widget
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### CDN (Any Website)
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<!-- Include React and ReactDOM first -->
|
|
19
|
+
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
20
|
+
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
21
|
+
|
|
22
|
+
<!-- Include the widget -->
|
|
23
|
+
<script src="https://your-cdn.com/rag-chat-widget.umd.js"></script>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### React Application
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { ChatWidget } from '@rag-widget/chat-widget';
|
|
32
|
+
|
|
33
|
+
function App() {
|
|
34
|
+
return (
|
|
35
|
+
<ChatWidget
|
|
36
|
+
apiKey="rw_live_your_api_key"
|
|
37
|
+
widgetId="your-widget-id"
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Script Tag (Non-React)
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<div id="chat-widget-root"></div>
|
|
47
|
+
<script>
|
|
48
|
+
RAGChatWidget.render({
|
|
49
|
+
apiKey: 'rw_live_your_api_key',
|
|
50
|
+
widgetId: 'your-widget-id',
|
|
51
|
+
container: document.getElementById('chat-widget-root')
|
|
52
|
+
});
|
|
53
|
+
</script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Props
|
|
57
|
+
|
|
58
|
+
| Prop | Type | Required | Default | Description |
|
|
59
|
+
|------|------|----------|---------|-------------|
|
|
60
|
+
| `apiKey` | `string` | Yes | - | Your RAG Widget API key |
|
|
61
|
+
| `widgetId` | `string` | Yes | - | The widget ID to connect to |
|
|
62
|
+
| `apiBaseUrl` | `string` | No | Current origin | API server URL |
|
|
63
|
+
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | No | `'bottom-right'` | Widget position |
|
|
64
|
+
| `primaryColor` | `string` | No | `'#007bff'` | Primary theme color |
|
|
65
|
+
| `greeting` | `string` | No | From config | Initial greeting message |
|
|
66
|
+
| `placeholder` | `string` | No | From config | Input placeholder text |
|
|
67
|
+
| `showPoweredBy` | `boolean` | No | From config | Show "Powered by" footer |
|
|
68
|
+
| `onError` | `(error: Error) => void` | No | - | Error callback |
|
|
69
|
+
| `onMessageSent` | `(message: string) => void` | No | - | Message sent callback |
|
|
70
|
+
| `onMessageReceived` | `(message: Message) => void` | No | - | Message received callback |
|
|
71
|
+
|
|
72
|
+
## Using the Hook
|
|
73
|
+
|
|
74
|
+
For custom implementations, use the `useChatWidget` hook:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { useChatWidget } from '@rag-widget/chat-widget';
|
|
78
|
+
|
|
79
|
+
function CustomChat() {
|
|
80
|
+
const {
|
|
81
|
+
messages,
|
|
82
|
+
isLoading,
|
|
83
|
+
isConnected,
|
|
84
|
+
config,
|
|
85
|
+
error,
|
|
86
|
+
sendMessage,
|
|
87
|
+
clearMessages,
|
|
88
|
+
retry
|
|
89
|
+
} = useChatWidget({
|
|
90
|
+
apiKey: 'rw_live_your_api_key',
|
|
91
|
+
widgetId: 'your-widget-id',
|
|
92
|
+
apiBaseUrl: 'https://your-api.com'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
{messages.map(msg => (
|
|
98
|
+
<div key={msg.id} className={msg.role}>
|
|
99
|
+
{msg.content}
|
|
100
|
+
</div>
|
|
101
|
+
))}
|
|
102
|
+
<input
|
|
103
|
+
onKeyDown={e => {
|
|
104
|
+
if (e.key === 'Enter') {
|
|
105
|
+
sendMessage(e.currentTarget.value);
|
|
106
|
+
e.currentTarget.value = '';
|
|
107
|
+
}
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Using the Provider
|
|
116
|
+
|
|
117
|
+
For sharing state across components:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { ChatWidgetProvider, useChatWidgetContext } from '@rag-widget/chat-widget';
|
|
121
|
+
|
|
122
|
+
function App() {
|
|
123
|
+
return (
|
|
124
|
+
<ChatWidgetProvider
|
|
125
|
+
apiKey="rw_live_your_api_key"
|
|
126
|
+
widgetId="your-widget-id"
|
|
127
|
+
>
|
|
128
|
+
<ChatMessages />
|
|
129
|
+
<ChatInput />
|
|
130
|
+
</ChatWidgetProvider>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function ChatMessages() {
|
|
135
|
+
const { messages, isLoading } = useChatWidgetContext();
|
|
136
|
+
// ...
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function ChatInput() {
|
|
140
|
+
const { sendMessage } = useChatWidgetContext();
|
|
141
|
+
// ...
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Styling
|
|
146
|
+
|
|
147
|
+
The widget comes with default styles. To customize:
|
|
148
|
+
|
|
149
|
+
### CSS Variables
|
|
150
|
+
|
|
151
|
+
```css
|
|
152
|
+
.rag-chat-widget {
|
|
153
|
+
--rag-primary-color: #007bff;
|
|
154
|
+
--rag-bg-color: #ffffff;
|
|
155
|
+
--rag-text-color: #333333;
|
|
156
|
+
--rag-border-color: #e0e0e0;
|
|
157
|
+
--rag-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
158
|
+
--rag-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Custom Styles
|
|
163
|
+
|
|
164
|
+
Import the CSS separately and override:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import '@rag-widget/chat-widget/dist/styles.css';
|
|
168
|
+
// Then add your overrides
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## API Endpoints
|
|
172
|
+
|
|
173
|
+
The widget communicates with these endpoints:
|
|
174
|
+
|
|
175
|
+
- `GET /api/v1/widget/:widgetId/config` - Get widget configuration
|
|
176
|
+
- `POST /api/v1/widget/:widgetId/query` - One-shot query
|
|
177
|
+
- `POST /api/v1/widget/:widgetId/sessions` - Create chat session
|
|
178
|
+
- `POST /api/v1/widget/:widgetId/sessions/:sessionId/messages` - Send message (SSE streaming)
|
|
179
|
+
|
|
180
|
+
## Security
|
|
181
|
+
|
|
182
|
+
- API keys are sent via `X-API-Key` header
|
|
183
|
+
- Domain whitelisting is enforced server-side
|
|
184
|
+
- Rate limiting is applied per API key
|
|
185
|
+
- Sessions expire after 1 hour of inactivity
|
|
186
|
+
|
|
187
|
+
## Browser Support
|
|
188
|
+
|
|
189
|
+
- Chrome 60+
|
|
190
|
+
- Firefox 55+
|
|
191
|
+
- Safari 12+
|
|
192
|
+
- Edge 79+
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Message, WidgetConfig } from '../types';
|
|
2
|
+
interface UseChatWidgetOptions {
|
|
3
|
+
apiKey: string;
|
|
4
|
+
widgetId: string;
|
|
5
|
+
apiBaseUrl: string;
|
|
6
|
+
onError?: (error: Error) => void;
|
|
7
|
+
onMessageSent?: (message: string) => void;
|
|
8
|
+
onMessageReceived?: (message: Message) => void;
|
|
9
|
+
}
|
|
10
|
+
interface UseChatWidgetReturn {
|
|
11
|
+
messages: Message[];
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
config: WidgetConfig | null;
|
|
15
|
+
error: Error | null;
|
|
16
|
+
sendMessage: (content: string) => Promise<void>;
|
|
17
|
+
clearMessages: () => void;
|
|
18
|
+
retry: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare function useChatWidget(options: UseChatWidgetOptions): UseChatWidgetReturn;
|
|
21
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ChatWidget, default as default } from './components/ChatWidget';
|
|
2
|
+
export { ChatBubble } from './components/ChatBubble';
|
|
3
|
+
export { ChatWindow } from './components/ChatWindow';
|
|
4
|
+
export { useChatWidget } from './hooks/useChatWidget';
|
|
5
|
+
export { ChatWidgetProvider, useChatWidgetContext } from './providers/ChatWidgetProvider';
|
|
6
|
+
export type { ChatWidgetProps, ChatBubbleProps, ChatWindowProps, WidgetConfig, Message, Source, ChatSession, UsageInfo, ApiResponse } from './types';
|
|
7
|
+
import './styles/widget.css';
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useRef, useEffect, useCallback, createContext, useMemo, useContext } from 'react';
|
|
3
|
+
|
|
4
|
+
function useChatWidget(options) {
|
|
5
|
+
const { apiKey, widgetId, apiBaseUrl, onError, onMessageSent, onMessageReceived } = options;
|
|
6
|
+
const [messages, setMessages] = useState([]);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
8
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
9
|
+
const [config, setConfig] = useState(null);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
const [session, setSession] = useState(null);
|
|
12
|
+
const abortControllerRef = useRef(null);
|
|
13
|
+
// Fetch widget configuration on mount
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
fetchConfig();
|
|
16
|
+
return () => {
|
|
17
|
+
if (abortControllerRef.current) {
|
|
18
|
+
abortControllerRef.current.abort();
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}, [apiKey, widgetId]);
|
|
22
|
+
const fetchConfig = useCallback(async () => {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/config`, {
|
|
25
|
+
headers: {
|
|
26
|
+
'X-API-Key': apiKey
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
throw new Error(`Failed to fetch config: ${response.status}`);
|
|
31
|
+
}
|
|
32
|
+
const result = await response.json();
|
|
33
|
+
if (result.status === 'success' && result.data) {
|
|
34
|
+
setConfig(result.data);
|
|
35
|
+
setIsConnected(true);
|
|
36
|
+
setError(null);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
throw new Error(result.message || 'Failed to load widget configuration');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const error = err instanceof Error ? err : new Error('Unknown error');
|
|
44
|
+
setError(error);
|
|
45
|
+
setIsConnected(false);
|
|
46
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
47
|
+
}
|
|
48
|
+
}, [apiKey, widgetId, apiBaseUrl, onError]);
|
|
49
|
+
const createSession = useCallback(async () => {
|
|
50
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/sessions`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'X-API-Key': apiKey,
|
|
54
|
+
'Content-Type': 'application/json'
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`Failed to create session: ${response.status}`);
|
|
59
|
+
}
|
|
60
|
+
const result = await response.json();
|
|
61
|
+
if (result.status === 'success' && result.data) {
|
|
62
|
+
return result.data;
|
|
63
|
+
}
|
|
64
|
+
throw new Error(result.message || 'Failed to create session');
|
|
65
|
+
}, [apiKey, widgetId, apiBaseUrl]);
|
|
66
|
+
const sendMessage = useCallback(async (content) => {
|
|
67
|
+
var _a;
|
|
68
|
+
if (!content.trim() || isLoading)
|
|
69
|
+
return;
|
|
70
|
+
// Cancel any ongoing request
|
|
71
|
+
if (abortControllerRef.current) {
|
|
72
|
+
abortControllerRef.current.abort();
|
|
73
|
+
}
|
|
74
|
+
abortControllerRef.current = new AbortController();
|
|
75
|
+
const userMessage = {
|
|
76
|
+
id: `msg-${Date.now()}`,
|
|
77
|
+
role: 'user',
|
|
78
|
+
content: content.trim(),
|
|
79
|
+
timestamp: new Date()
|
|
80
|
+
};
|
|
81
|
+
setMessages(prev => [...prev, userMessage]);
|
|
82
|
+
setIsLoading(true);
|
|
83
|
+
setError(null);
|
|
84
|
+
onMessageSent === null || onMessageSent === void 0 ? void 0 : onMessageSent(content);
|
|
85
|
+
try {
|
|
86
|
+
// Ensure we have a session
|
|
87
|
+
let currentSession = session;
|
|
88
|
+
if (!currentSession || new Date() > new Date(currentSession.expiresAt)) {
|
|
89
|
+
currentSession = await createSession();
|
|
90
|
+
setSession(currentSession);
|
|
91
|
+
}
|
|
92
|
+
// Create placeholder for assistant message
|
|
93
|
+
const assistantMessageId = `msg-${Date.now()}-assistant`;
|
|
94
|
+
const assistantMessage = {
|
|
95
|
+
id: assistantMessageId,
|
|
96
|
+
role: 'assistant',
|
|
97
|
+
content: '',
|
|
98
|
+
timestamp: new Date(),
|
|
99
|
+
isStreaming: true
|
|
100
|
+
};
|
|
101
|
+
setMessages(prev => [...prev, assistantMessage]);
|
|
102
|
+
// Send message with SSE streaming
|
|
103
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/sessions/${currentSession.sessionId}/messages`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'X-API-Key': apiKey,
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
'Accept': 'text/event-stream'
|
|
109
|
+
},
|
|
110
|
+
body: JSON.stringify({ message: content }),
|
|
111
|
+
signal: abortControllerRef.current.signal
|
|
112
|
+
});
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error(`Failed to send message: ${response.status}`);
|
|
115
|
+
}
|
|
116
|
+
const reader = (_a = response.body) === null || _a === void 0 ? void 0 : _a.getReader();
|
|
117
|
+
const decoder = new TextDecoder();
|
|
118
|
+
let fullContent = '';
|
|
119
|
+
if (reader) {
|
|
120
|
+
while (true) {
|
|
121
|
+
const { done, value } = await reader.read();
|
|
122
|
+
if (done)
|
|
123
|
+
break;
|
|
124
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
125
|
+
const lines = chunk.split('\n');
|
|
126
|
+
for (const line of lines) {
|
|
127
|
+
if (line.startsWith('data: ')) {
|
|
128
|
+
try {
|
|
129
|
+
const data = JSON.parse(line.slice(6));
|
|
130
|
+
if (data.type === 'chunk') {
|
|
131
|
+
fullContent += data.content;
|
|
132
|
+
setMessages(prev => prev.map(msg => msg.id === assistantMessageId
|
|
133
|
+
? { ...msg, content: fullContent }
|
|
134
|
+
: msg));
|
|
135
|
+
}
|
|
136
|
+
else if (data.type === 'done') {
|
|
137
|
+
setMessages(prev => prev.map(msg => msg.id === assistantMessageId
|
|
138
|
+
? { ...msg, isStreaming: false, sources: data.sources }
|
|
139
|
+
: msg));
|
|
140
|
+
const finalMessage = {
|
|
141
|
+
id: assistantMessageId,
|
|
142
|
+
role: 'assistant',
|
|
143
|
+
content: fullContent,
|
|
144
|
+
timestamp: new Date(),
|
|
145
|
+
sources: data.sources
|
|
146
|
+
};
|
|
147
|
+
onMessageReceived === null || onMessageReceived === void 0 ? void 0 : onMessageReceived(finalMessage);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch (_b) {
|
|
151
|
+
// Ignore parse errors for partial SSE data
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
if (err.name === 'AbortError') {
|
|
160
|
+
// Request was cancelled, ignore
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const error = err instanceof Error ? err : new Error('Failed to send message');
|
|
164
|
+
setError(error);
|
|
165
|
+
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
166
|
+
// Remove the incomplete assistant message
|
|
167
|
+
setMessages(prev => prev.filter(msg => !msg.isStreaming));
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
setIsLoading(false);
|
|
171
|
+
}
|
|
172
|
+
}, [apiKey, widgetId, apiBaseUrl, session, isLoading, createSession, onError, onMessageSent, onMessageReceived]);
|
|
173
|
+
const clearMessages = useCallback(() => {
|
|
174
|
+
setMessages([]);
|
|
175
|
+
setSession(null);
|
|
176
|
+
}, []);
|
|
177
|
+
const retry = useCallback(() => {
|
|
178
|
+
setError(null);
|
|
179
|
+
fetchConfig();
|
|
180
|
+
}, [fetchConfig]);
|
|
181
|
+
return {
|
|
182
|
+
messages,
|
|
183
|
+
isLoading,
|
|
184
|
+
isConnected,
|
|
185
|
+
config,
|
|
186
|
+
error,
|
|
187
|
+
sendMessage,
|
|
188
|
+
clearMessages,
|
|
189
|
+
retry
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const ChatIcon = () => (jsxs("svg", { className: "rag-chat-bubble-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z" }), jsx("path", { d: "M7 9h10v2H7zm0-3h10v2H7z" })] }));
|
|
194
|
+
const CloseIcon$1 = () => (jsx("svg", { className: "rag-chat-bubble-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }));
|
|
195
|
+
const ChatBubble = ({ isOpen, onClick, primaryColor = '#007bff', position = 'bottom-right', unreadCount = 0 }) => {
|
|
196
|
+
return (jsxs("button", { className: `rag-chat-bubble ${position}`, onClick: onClick, style: { backgroundColor: primaryColor }, "aria-label": isOpen ? 'Close chat' : 'Open chat', type: "button", children: [isOpen ? jsx(CloseIcon$1, {}) : jsx(ChatIcon, {}), !isOpen && unreadCount > 0 && (jsx("span", { className: "rag-chat-bubble-badge", children: unreadCount > 9 ? '9+' : unreadCount }))] }));
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const CloseIcon = () => (jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }));
|
|
200
|
+
const SendIcon = () => (jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }));
|
|
201
|
+
const LoadingDots = () => (jsxs("div", { className: "rag-chat-loading", children: [jsx("div", { className: "rag-chat-loading-dot" }), jsx("div", { className: "rag-chat-loading-dot" }), jsx("div", { className: "rag-chat-loading-dot" })] }));
|
|
202
|
+
const MessageBubble = ({ message }) => (jsxs("div", { className: `rag-chat-message ${message.role}`, children: [jsx("p", { className: "rag-chat-message-content", children: message.content }), message.sources && message.sources.length > 0 && (jsxs("div", { className: "rag-chat-message-sources", children: [jsx("div", { className: "rag-chat-message-sources-title", children: "Sources:" }), message.sources.map((source, index) => (jsx("div", { className: "rag-chat-message-source", children: source.title }, index)))] }))] }));
|
|
203
|
+
const ChatWindow = ({ isOpen, onClose, config, messages, isLoading, onSendMessage }) => {
|
|
204
|
+
const [inputValue, setInputValue] = useState('');
|
|
205
|
+
const messagesEndRef = useRef(null);
|
|
206
|
+
const inputRef = useRef(null);
|
|
207
|
+
// Scroll to bottom when new messages arrive
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
var _a;
|
|
210
|
+
(_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
|
|
211
|
+
}, [messages]);
|
|
212
|
+
// Focus input when window opens
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
var _a;
|
|
215
|
+
if (isOpen) {
|
|
216
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
217
|
+
}
|
|
218
|
+
}, [isOpen]);
|
|
219
|
+
const handleSubmit = (e) => {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
if (inputValue.trim() && !isLoading) {
|
|
222
|
+
onSendMessage(inputValue.trim());
|
|
223
|
+
setInputValue('');
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const handleKeyDown = (e) => {
|
|
227
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
handleSubmit(e);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
if (!isOpen)
|
|
233
|
+
return null;
|
|
234
|
+
const position = (config === null || config === void 0 ? void 0 : config.position) || 'bottom-right';
|
|
235
|
+
const primaryColor = (config === null || config === void 0 ? void 0 : config.primaryColor) || '#007bff';
|
|
236
|
+
return (jsxs("div", { className: `rag-chat-window ${position}`, style: { '--rag-primary-color': primaryColor }, children: [jsxs("div", { className: "rag-chat-header", style: { backgroundColor: primaryColor }, children: [jsx("h3", { className: "rag-chat-header-title", children: (config === null || config === void 0 ? void 0 : config.name) || 'Chat' }), jsx("button", { className: "rag-chat-header-close", onClick: onClose, "aria-label": "Close chat", type: "button", children: jsx(CloseIcon, {}) })] }), jsxs("div", { className: "rag-chat-messages", children: [messages.length === 0 && (config === null || config === void 0 ? void 0 : config.greeting) && (jsx("div", { className: "rag-chat-greeting", children: config.greeting })), messages.map(message => (jsx(MessageBubble, { message: message }, message.id))), isLoading && jsx(LoadingDots, {}), jsx("div", { ref: messagesEndRef })] }), jsxs("form", { className: "rag-chat-input-container", onSubmit: handleSubmit, children: [jsx("textarea", { ref: inputRef, className: "rag-chat-input", value: inputValue, onChange: e => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: (config === null || config === void 0 ? void 0 : config.placeholder) || 'Type a message...', rows: 1, maxLength: (config === null || config === void 0 ? void 0 : config.allowedMessageLength) || 2000, disabled: isLoading }), jsx("button", { type: "submit", className: "rag-chat-send-button", disabled: !inputValue.trim() || isLoading, style: { backgroundColor: primaryColor }, "aria-label": "Send message", children: jsx(SendIcon, {}) })] }), (config === null || config === void 0 ? void 0 : config.showPoweredBy) && (jsxs("div", { className: "rag-chat-powered-by", children: ["Powered by ", jsx("a", { href: "https://rag-widget.com", target: "_blank", rel: "noopener noreferrer", children: "RAG Widget" })] }))] }));
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const ChatWidget = ({ apiKey, widgetId, apiBaseUrl = '', position, primaryColor, greeting, placeholder, showPoweredBy, onError, onMessageSent, onMessageReceived }) => {
|
|
240
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
241
|
+
// Determine the API base URL
|
|
242
|
+
const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
243
|
+
const { messages, isLoading, isConnected, config, error, sendMessage, retry } = useChatWidget({
|
|
244
|
+
apiKey,
|
|
245
|
+
widgetId,
|
|
246
|
+
apiBaseUrl: baseUrl,
|
|
247
|
+
onError,
|
|
248
|
+
onMessageSent,
|
|
249
|
+
onMessageReceived
|
|
250
|
+
});
|
|
251
|
+
// Merge props with fetched config (props take precedence)
|
|
252
|
+
const mergedConfig = config ? {
|
|
253
|
+
...config,
|
|
254
|
+
position: position || config.position,
|
|
255
|
+
primaryColor: primaryColor || config.primaryColor,
|
|
256
|
+
greeting: greeting || config.greeting,
|
|
257
|
+
placeholder: placeholder || config.placeholder,
|
|
258
|
+
showPoweredBy: showPoweredBy !== undefined ? showPoweredBy : config.showPoweredBy
|
|
259
|
+
} : null;
|
|
260
|
+
const handleToggle = () => {
|
|
261
|
+
setIsOpen(prev => !prev);
|
|
262
|
+
};
|
|
263
|
+
const handleClose = () => {
|
|
264
|
+
setIsOpen(false);
|
|
265
|
+
};
|
|
266
|
+
// Show error state in the window if there's an error
|
|
267
|
+
if (error && isOpen) {
|
|
268
|
+
return (jsxs("div", { className: "rag-chat-widget", children: [jsx(ChatBubble, { isOpen: isOpen, onClick: handleToggle, primaryColor: primaryColor || '#007bff', position: position || 'bottom-right' }), jsxs("div", { className: `rag-chat-window ${position || 'bottom-right'}`, children: [jsxs("div", { className: "rag-chat-header", style: { backgroundColor: primaryColor || '#007bff' }, children: [jsx("h3", { className: "rag-chat-header-title", children: "Chat" }), jsx("button", { className: "rag-chat-header-close", onClick: handleClose, type: "button", children: jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsx("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })] }), jsxs("div", { className: "rag-chat-error", children: [jsx("p", { className: "rag-chat-error-message", children: error.message || 'Unable to connect. Please try again.' }), jsx("button", { className: "rag-chat-error-retry", onClick: retry, type: "button", children: "Retry" })] })] })] }));
|
|
269
|
+
}
|
|
270
|
+
return (jsxs("div", { className: "rag-chat-widget", children: [jsx(ChatBubble, { isOpen: isOpen, onClick: handleToggle, primaryColor: (mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.primaryColor) || primaryColor || '#007bff', position: (mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.position) || position || 'bottom-right' }), mergedConfig && (jsx(ChatWindow, { isOpen: isOpen, onClose: handleClose, config: mergedConfig, messages: messages, isLoading: isLoading, onSendMessage: sendMessage }))] }));
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const ChatWidgetContext = createContext(null);
|
|
274
|
+
const ChatWidgetProvider = ({ apiKey, widgetId, apiBaseUrl = '', onError, onMessageSent, onMessageReceived, children }) => {
|
|
275
|
+
const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
276
|
+
const chatWidget = useChatWidget({
|
|
277
|
+
apiKey,
|
|
278
|
+
widgetId,
|
|
279
|
+
apiBaseUrl: baseUrl,
|
|
280
|
+
onError,
|
|
281
|
+
onMessageSent,
|
|
282
|
+
onMessageReceived
|
|
283
|
+
});
|
|
284
|
+
const value = useMemo(() => ({
|
|
285
|
+
messages: chatWidget.messages,
|
|
286
|
+
isLoading: chatWidget.isLoading,
|
|
287
|
+
isConnected: chatWidget.isConnected,
|
|
288
|
+
config: chatWidget.config,
|
|
289
|
+
error: chatWidget.error,
|
|
290
|
+
sendMessage: chatWidget.sendMessage,
|
|
291
|
+
clearMessages: chatWidget.clearMessages,
|
|
292
|
+
retry: chatWidget.retry
|
|
293
|
+
}), [chatWidget]);
|
|
294
|
+
return (jsx(ChatWidgetContext.Provider, { value: value, children: children }));
|
|
295
|
+
};
|
|
296
|
+
const useChatWidgetContext = () => {
|
|
297
|
+
const context = useContext(ChatWidgetContext);
|
|
298
|
+
if (!context) {
|
|
299
|
+
throw new Error('useChatWidgetContext must be used within a ChatWidgetProvider');
|
|
300
|
+
}
|
|
301
|
+
return context;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
export { ChatBubble, ChatWidget, ChatWidgetProvider, ChatWindow, ChatWidget as default, useChatWidget, useChatWidgetContext };
|
|
305
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/hooks/useChatWidget.ts","../src/components/ChatBubble.tsx","../src/components/ChatWindow.tsx","../src/components/ChatWidget.tsx","../src/providers/ChatWidgetProvider.tsx"],"sourcesContent":["import { useState, useCallback, useRef, useEffect } from 'react';\r\nimport type { Message, WidgetConfig, ChatSession, ApiResponse } from '../types';\r\n\r\ninterface UseChatWidgetOptions {\r\n apiKey: string;\r\n widgetId: string;\r\n apiBaseUrl: string;\r\n onError?: (error: Error) => void;\r\n onMessageSent?: (message: string) => void;\r\n onMessageReceived?: (message: Message) => void;\r\n}\r\n\r\ninterface UseChatWidgetReturn {\r\n messages: Message[];\r\n isLoading: boolean;\r\n isConnected: boolean;\r\n config: WidgetConfig | null;\r\n error: Error | null;\r\n sendMessage: (content: string) => Promise<void>;\r\n clearMessages: () => void;\r\n retry: () => void;\r\n}\r\n\r\nexport function useChatWidget(options: UseChatWidgetOptions): UseChatWidgetReturn {\r\n const { apiKey, widgetId, apiBaseUrl, onError, onMessageSent, onMessageReceived } = options;\r\n\r\n const [messages, setMessages] = useState<Message[]>([]);\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [isConnected, setIsConnected] = useState(false);\r\n const [config, setConfig] = useState<WidgetConfig | null>(null);\r\n const [error, setError] = useState<Error | null>(null);\r\n const [session, setSession] = useState<ChatSession | null>(null);\r\n\r\n const abortControllerRef = useRef<AbortController | null>(null);\r\n\r\n // Fetch widget configuration on mount\r\n useEffect(() => {\r\n fetchConfig();\r\n return () => {\r\n if (abortControllerRef.current) {\r\n abortControllerRef.current.abort();\r\n }\r\n };\r\n }, [apiKey, widgetId]);\r\n\r\n const fetchConfig = useCallback(async () => {\r\n try {\r\n const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/config`, {\r\n headers: {\r\n 'X-API-Key': apiKey\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch config: ${response.status}`);\r\n }\r\n\r\n const result: ApiResponse<WidgetConfig> = await response.json();\r\n\r\n if (result.status === 'success' && result.data) {\r\n setConfig(result.data);\r\n setIsConnected(true);\r\n setError(null);\r\n } else {\r\n throw new Error(result.message || 'Failed to load widget configuration');\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error('Unknown error');\r\n setError(error);\r\n setIsConnected(false);\r\n onError?.(error);\r\n }\r\n }, [apiKey, widgetId, apiBaseUrl, onError]);\r\n\r\n const createSession = useCallback(async (): Promise<ChatSession> => {\r\n const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/sessions`, {\r\n method: 'POST',\r\n headers: {\r\n 'X-API-Key': apiKey,\r\n 'Content-Type': 'application/json'\r\n }\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to create session: ${response.status}`);\r\n }\r\n\r\n const result: ApiResponse<ChatSession> = await response.json();\r\n\r\n if (result.status === 'success' && result.data) {\r\n return result.data;\r\n }\r\n\r\n throw new Error(result.message || 'Failed to create session');\r\n }, [apiKey, widgetId, apiBaseUrl]);\r\n\r\n const sendMessage = useCallback(async (content: string) => {\r\n if (!content.trim() || isLoading) return;\r\n\r\n // Cancel any ongoing request\r\n if (abortControllerRef.current) {\r\n abortControllerRef.current.abort();\r\n }\r\n abortControllerRef.current = new AbortController();\r\n\r\n const userMessage: Message = {\r\n id: `msg-${Date.now()}`,\r\n role: 'user',\r\n content: content.trim(),\r\n timestamp: new Date()\r\n };\r\n\r\n setMessages(prev => [...prev, userMessage]);\r\n setIsLoading(true);\r\n setError(null);\r\n onMessageSent?.(content);\r\n\r\n try {\r\n // Ensure we have a session\r\n let currentSession = session;\r\n if (!currentSession || new Date() > new Date(currentSession.expiresAt)) {\r\n currentSession = await createSession();\r\n setSession(currentSession);\r\n }\r\n\r\n // Create placeholder for assistant message\r\n const assistantMessageId = `msg-${Date.now()}-assistant`;\r\n const assistantMessage: Message = {\r\n id: assistantMessageId,\r\n role: 'assistant',\r\n content: '',\r\n timestamp: new Date(),\r\n isStreaming: true\r\n };\r\n setMessages(prev => [...prev, assistantMessage]);\r\n\r\n // Send message with SSE streaming\r\n const response = await fetch(\r\n `${apiBaseUrl}/api/v1/widget/${widgetId}/sessions/${currentSession.sessionId}/messages`,\r\n {\r\n method: 'POST',\r\n headers: {\r\n 'X-API-Key': apiKey,\r\n 'Content-Type': 'application/json',\r\n 'Accept': 'text/event-stream'\r\n },\r\n body: JSON.stringify({ message: content }),\r\n signal: abortControllerRef.current.signal\r\n }\r\n );\r\n\r\n if (!response.ok) {\r\n throw new Error(`Failed to send message: ${response.status}`);\r\n }\r\n\r\n const reader = response.body?.getReader();\r\n const decoder = new TextDecoder();\r\n let fullContent = '';\r\n\r\n if (reader) {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n const chunk = decoder.decode(value, { stream: true });\r\n const lines = chunk.split('\\n');\r\n\r\n for (const line of lines) {\r\n if (line.startsWith('data: ')) {\r\n try {\r\n const data = JSON.parse(line.slice(6));\r\n\r\n if (data.type === 'chunk') {\r\n fullContent += data.content;\r\n setMessages(prev =>\r\n prev.map(msg =>\r\n msg.id === assistantMessageId\r\n ? { ...msg, content: fullContent }\r\n : msg\r\n )\r\n );\r\n } else if (data.type === 'done') {\r\n setMessages(prev =>\r\n prev.map(msg =>\r\n msg.id === assistantMessageId\r\n ? { ...msg, isStreaming: false, sources: data.sources }\r\n : msg\r\n )\r\n );\r\n\r\n const finalMessage: Message = {\r\n id: assistantMessageId,\r\n role: 'assistant',\r\n content: fullContent,\r\n timestamp: new Date(),\r\n sources: data.sources\r\n };\r\n onMessageReceived?.(finalMessage);\r\n }\r\n } catch {\r\n // Ignore parse errors for partial SSE data\r\n }\r\n }\r\n }\r\n }\r\n }\r\n } catch (err) {\r\n if ((err as Error).name === 'AbortError') {\r\n // Request was cancelled, ignore\r\n return;\r\n }\r\n\r\n const error = err instanceof Error ? err : new Error('Failed to send message');\r\n setError(error);\r\n onError?.(error);\r\n\r\n // Remove the incomplete assistant message\r\n setMessages(prev => prev.filter(msg => !msg.isStreaming));\r\n } finally {\r\n setIsLoading(false);\r\n }\r\n }, [apiKey, widgetId, apiBaseUrl, session, isLoading, createSession, onError, onMessageSent, onMessageReceived]);\r\n\r\n const clearMessages = useCallback(() => {\r\n setMessages([]);\r\n setSession(null);\r\n }, []);\r\n\r\n const retry = useCallback(() => {\r\n setError(null);\r\n fetchConfig();\r\n }, [fetchConfig]);\r\n\r\n return {\r\n messages,\r\n isLoading,\r\n isConnected,\r\n config,\r\n error,\r\n sendMessage,\r\n clearMessages,\r\n retry\r\n };\r\n}\r\n","import React from 'react';\r\nimport type { ChatBubbleProps } from '../types';\r\n\r\nconst ChatIcon: React.FC = () => (\r\n <svg\r\n className=\"rag-chat-bubble-icon\"\r\n viewBox=\"0 0 24 24\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z\" />\r\n <path d=\"M7 9h10v2H7zm0-3h10v2H7z\" />\r\n </svg>\r\n);\r\n\r\nconst CloseIcon: React.FC = () => (\r\n <svg\r\n className=\"rag-chat-bubble-icon\"\r\n viewBox=\"0 0 24 24\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n >\r\n <path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\" />\r\n </svg>\r\n);\r\n\r\nexport const ChatBubble: React.FC<ChatBubbleProps> = ({\r\n isOpen,\r\n onClick,\r\n primaryColor = '#007bff',\r\n position = 'bottom-right',\r\n unreadCount = 0\r\n}) => {\r\n return (\r\n <button\r\n className={`rag-chat-bubble ${position}`}\r\n onClick={onClick}\r\n style={{ backgroundColor: primaryColor }}\r\n aria-label={isOpen ? 'Close chat' : 'Open chat'}\r\n type=\"button\"\r\n >\r\n {isOpen ? <CloseIcon /> : <ChatIcon />}\r\n {!isOpen && unreadCount > 0 && (\r\n <span className=\"rag-chat-bubble-badge\">\r\n {unreadCount > 9 ? '9+' : unreadCount}\r\n </span>\r\n )}\r\n </button>\r\n );\r\n};\r\n\r\nexport default ChatBubble;\r\n","import React, { useState, useRef, useEffect } from 'react';\r\nimport type { ChatWindowProps, Message } from '../types';\r\n\r\nconst CloseIcon: React.FC = () => (\r\n <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\r\n />\r\n </svg>\r\n);\r\n\r\nconst SendIcon: React.FC = () => (\r\n <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\" />\r\n </svg>\r\n);\r\n\r\nconst LoadingDots: React.FC = () => (\r\n <div className=\"rag-chat-loading\">\r\n <div className=\"rag-chat-loading-dot\" />\r\n <div className=\"rag-chat-loading-dot\" />\r\n <div className=\"rag-chat-loading-dot\" />\r\n </div>\r\n);\r\n\r\ninterface MessageBubbleProps {\r\n message: Message;\r\n}\r\n\r\nconst MessageBubble: React.FC<MessageBubbleProps> = ({ message }) => (\r\n <div className={`rag-chat-message ${message.role}`}>\r\n <p className=\"rag-chat-message-content\">{message.content}</p>\r\n {message.sources && message.sources.length > 0 && (\r\n <div className=\"rag-chat-message-sources\">\r\n <div className=\"rag-chat-message-sources-title\">Sources:</div>\r\n {message.sources.map((source, index) => (\r\n <div key={index} className=\"rag-chat-message-source\">\r\n {source.title}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n);\r\n\r\nexport const ChatWindow: React.FC<ChatWindowProps> = ({\r\n isOpen,\r\n onClose,\r\n config,\r\n messages,\r\n isLoading,\r\n onSendMessage\r\n}) => {\r\n const [inputValue, setInputValue] = useState('');\r\n const messagesEndRef = useRef<HTMLDivElement>(null);\r\n const inputRef = useRef<HTMLTextAreaElement>(null);\r\n\r\n // Scroll to bottom when new messages arrive\r\n useEffect(() => {\r\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\r\n }, [messages]);\r\n\r\n // Focus input when window opens\r\n useEffect(() => {\r\n if (isOpen) {\r\n inputRef.current?.focus();\r\n }\r\n }, [isOpen]);\r\n\r\n const handleSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (inputValue.trim() && !isLoading) {\r\n onSendMessage(inputValue.trim());\r\n setInputValue('');\r\n }\r\n };\r\n\r\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\r\n if (e.key === 'Enter' && !e.shiftKey) {\r\n e.preventDefault();\r\n handleSubmit(e);\r\n }\r\n };\r\n\r\n if (!isOpen) return null;\r\n\r\n const position = config?.position || 'bottom-right';\r\n const primaryColor = config?.primaryColor || '#007bff';\r\n\r\n return (\r\n <div\r\n className={`rag-chat-window ${position}`}\r\n style={{ '--rag-primary-color': primaryColor } as React.CSSProperties}\r\n >\r\n <div className=\"rag-chat-header\" style={{ backgroundColor: primaryColor }}>\r\n <h3 className=\"rag-chat-header-title\">{config?.name || 'Chat'}</h3>\r\n <button\r\n className=\"rag-chat-header-close\"\r\n onClick={onClose}\r\n aria-label=\"Close chat\"\r\n type=\"button\"\r\n >\r\n <CloseIcon />\r\n </button>\r\n </div>\r\n\r\n <div className=\"rag-chat-messages\">\r\n {messages.length === 0 && config?.greeting && (\r\n <div className=\"rag-chat-greeting\">{config.greeting}</div>\r\n )}\r\n\r\n {messages.map(message => (\r\n <MessageBubble key={message.id} message={message} />\r\n ))}\r\n\r\n {isLoading && <LoadingDots />}\r\n\r\n <div ref={messagesEndRef} />\r\n </div>\r\n\r\n <form className=\"rag-chat-input-container\" onSubmit={handleSubmit}>\r\n <textarea\r\n ref={inputRef}\r\n className=\"rag-chat-input\"\r\n value={inputValue}\r\n onChange={e => setInputValue(e.target.value)}\r\n onKeyDown={handleKeyDown}\r\n placeholder={config?.placeholder || 'Type a message...'}\r\n rows={1}\r\n maxLength={config?.allowedMessageLength || 2000}\r\n disabled={isLoading}\r\n />\r\n <button\r\n type=\"submit\"\r\n className=\"rag-chat-send-button\"\r\n disabled={!inputValue.trim() || isLoading}\r\n style={{ backgroundColor: primaryColor }}\r\n aria-label=\"Send message\"\r\n >\r\n <SendIcon />\r\n </button>\r\n </form>\r\n\r\n {config?.showPoweredBy && (\r\n <div className=\"rag-chat-powered-by\">\r\n Powered by <a href=\"https://rag-widget.com\" target=\"_blank\" rel=\"noopener noreferrer\">RAG Widget</a>\r\n </div>\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nexport default ChatWindow;\r\n","import React, { useState } from 'react';\r\nimport { useChatWidget } from '../hooks/useChatWidget';\r\nimport { ChatBubble } from './ChatBubble';\r\nimport { ChatWindow } from './ChatWindow';\r\nimport type { ChatWidgetProps } from '../types';\r\nimport '../styles/widget.css';\r\n\r\nexport const ChatWidget: React.FC<ChatWidgetProps> = ({\r\n apiKey,\r\n widgetId,\r\n apiBaseUrl = '',\r\n position,\r\n primaryColor,\r\n greeting,\r\n placeholder,\r\n showPoweredBy,\r\n onError,\r\n onMessageSent,\r\n onMessageReceived\r\n}) => {\r\n const [isOpen, setIsOpen] = useState(false);\r\n\r\n // Determine the API base URL\r\n const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');\r\n\r\n const {\r\n messages,\r\n isLoading,\r\n isConnected,\r\n config,\r\n error,\r\n sendMessage,\r\n retry\r\n } = useChatWidget({\r\n apiKey,\r\n widgetId,\r\n apiBaseUrl: baseUrl,\r\n onError,\r\n onMessageSent,\r\n onMessageReceived\r\n });\r\n\r\n // Merge props with fetched config (props take precedence)\r\n const mergedConfig = config ? {\r\n ...config,\r\n position: position || config.position,\r\n primaryColor: primaryColor || config.primaryColor,\r\n greeting: greeting || config.greeting,\r\n placeholder: placeholder || config.placeholder,\r\n showPoweredBy: showPoweredBy !== undefined ? showPoweredBy : config.showPoweredBy\r\n } : null;\r\n\r\n const handleToggle = () => {\r\n setIsOpen(prev => !prev);\r\n };\r\n\r\n const handleClose = () => {\r\n setIsOpen(false);\r\n };\r\n\r\n // Show error state in the window if there's an error\r\n if (error && isOpen) {\r\n return (\r\n <div className=\"rag-chat-widget\">\r\n <ChatBubble\r\n isOpen={isOpen}\r\n onClick={handleToggle}\r\n primaryColor={primaryColor || '#007bff'}\r\n position={position || 'bottom-right'}\r\n />\r\n <div className={`rag-chat-window ${position || 'bottom-right'}`}>\r\n <div className=\"rag-chat-header\" style={{ backgroundColor: primaryColor || '#007bff' }}>\r\n <h3 className=\"rag-chat-header-title\">Chat</h3>\r\n <button className=\"rag-chat-header-close\" onClick={handleClose} type=\"button\">\r\n <svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path\r\n fill=\"currentColor\"\r\n d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"\r\n />\r\n </svg>\r\n </button>\r\n </div>\r\n <div className=\"rag-chat-error\">\r\n <p className=\"rag-chat-error-message\">\r\n {error.message || 'Unable to connect. Please try again.'}\r\n </p>\r\n <button className=\"rag-chat-error-retry\" onClick={retry} type=\"button\">\r\n Retry\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n return (\r\n <div className=\"rag-chat-widget\">\r\n <ChatBubble\r\n isOpen={isOpen}\r\n onClick={handleToggle}\r\n primaryColor={mergedConfig?.primaryColor || primaryColor || '#007bff'}\r\n position={mergedConfig?.position || position || 'bottom-right'}\r\n />\r\n {mergedConfig && (\r\n <ChatWindow\r\n isOpen={isOpen}\r\n onClose={handleClose}\r\n config={mergedConfig}\r\n messages={messages}\r\n isLoading={isLoading}\r\n onSendMessage={sendMessage}\r\n />\r\n )}\r\n </div>\r\n );\r\n};\r\n\r\nexport default ChatWidget;\r\n","import React, { createContext, useContext, useMemo } from 'react';\r\nimport type { ChatWidgetProps, WidgetConfig, Message } from '../types';\r\nimport { useChatWidget } from '../hooks/useChatWidget';\r\n\r\ninterface ChatWidgetContextValue {\r\n messages: Message[];\r\n isLoading: boolean;\r\n isConnected: boolean;\r\n config: WidgetConfig | null;\r\n error: Error | null;\r\n sendMessage: (content: string) => Promise<void>;\r\n clearMessages: () => void;\r\n retry: () => void;\r\n}\r\n\r\nconst ChatWidgetContext = createContext<ChatWidgetContextValue | null>(null);\r\n\r\nexport interface ChatWidgetProviderProps extends Omit<ChatWidgetProps, 'position' | 'primaryColor' | 'greeting' | 'placeholder' | 'showPoweredBy'> {\r\n children: React.ReactNode;\r\n}\r\n\r\nexport const ChatWidgetProvider: React.FC<ChatWidgetProviderProps> = ({\r\n apiKey,\r\n widgetId,\r\n apiBaseUrl = '',\r\n onError,\r\n onMessageSent,\r\n onMessageReceived,\r\n children\r\n}) => {\r\n const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');\r\n\r\n const chatWidget = useChatWidget({\r\n apiKey,\r\n widgetId,\r\n apiBaseUrl: baseUrl,\r\n onError,\r\n onMessageSent,\r\n onMessageReceived\r\n });\r\n\r\n const value = useMemo(() => ({\r\n messages: chatWidget.messages,\r\n isLoading: chatWidget.isLoading,\r\n isConnected: chatWidget.isConnected,\r\n config: chatWidget.config,\r\n error: chatWidget.error,\r\n sendMessage: chatWidget.sendMessage,\r\n clearMessages: chatWidget.clearMessages,\r\n retry: chatWidget.retry\r\n }), [chatWidget]);\r\n\r\n return (\r\n <ChatWidgetContext.Provider value={value}>\r\n {children}\r\n </ChatWidgetContext.Provider>\r\n );\r\n};\r\n\r\nexport const useChatWidgetContext = (): ChatWidgetContextValue => {\r\n const context = useContext(ChatWidgetContext);\r\n if (!context) {\r\n throw new Error('useChatWidgetContext must be used within a ChatWidgetProvider');\r\n }\r\n return context;\r\n};\r\n\r\nexport default ChatWidgetProvider;\r\n"],"names":["_jsxs","_jsx","CloseIcon"],"mappings":"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"}
|