@rag-widget/chat-widget 0.1.2 → 0.1.3
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/dist/__tests__/accessibility.test.d.ts +1 -0
- package/dist/components/ChatWidget.test.d.ts +1 -0
- package/dist/components/home/BookmarksRow.d.ts +6 -0
- package/dist/components/home/BookmarksRow.test.d.ts +1 -0
- package/dist/components/home/HeaderSection.d.ts +6 -0
- package/dist/components/home/HeaderSection.test.d.ts +1 -0
- package/dist/components/home/StartChatCard.d.ts +6 -0
- package/dist/components/home/StartChatCard.test.d.ts +1 -0
- package/dist/components/home/TicketCategoryList.d.ts +7 -0
- package/dist/components/home/TicketCategoryList.test.d.ts +1 -0
- package/dist/components/home/index.d.ts +8 -0
- package/dist/components/views/HomeView.d.ts +9 -0
- package/dist/components/views/HomeView.test.d.ts +1 -0
- package/dist/components/views/index.d.ts +2 -0
- package/dist/hooks/useViewNavigation.d.ts +10 -0
- package/dist/hooks/useViewNavigation.test.d.ts +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.esm.js +206 -41
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +229 -58
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/providers/ChatWidgetProvider.d.ts +7 -1
- package/dist/providers/ChatWidgetProvider.test.d.ts +1 -0
- package/dist/styles.css +1 -1
- package/dist/test/setup.d.ts +1 -0
- package/dist/types/homeView.test.d.ts +1 -0
- package/dist/types/index.d.ts +22 -0
- package/package.json +13 -4
package/dist/index.js
CHANGED
|
@@ -3,19 +3,19 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var
|
|
6
|
+
var React = require('react');
|
|
7
7
|
|
|
8
8
|
function useChatWidget(options) {
|
|
9
9
|
const { apiKey, widgetId, apiBaseUrl, onError, onMessageSent, onMessageReceived } = options;
|
|
10
|
-
const [messages, setMessages] =
|
|
11
|
-
const [isLoading, setIsLoading] =
|
|
12
|
-
const [isConnected, setIsConnected] =
|
|
13
|
-
const [config, setConfig] =
|
|
14
|
-
const [error, setError] =
|
|
15
|
-
const [session, setSession] =
|
|
16
|
-
const abortControllerRef =
|
|
10
|
+
const [messages, setMessages] = React.useState([]);
|
|
11
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
|
12
|
+
const [isConnected, setIsConnected] = React.useState(false);
|
|
13
|
+
const [config, setConfig] = React.useState(null);
|
|
14
|
+
const [error, setError] = React.useState(null);
|
|
15
|
+
const [session, setSession] = React.useState(null);
|
|
16
|
+
const abortControllerRef = React.useRef(null);
|
|
17
17
|
// Fetch widget configuration on mount
|
|
18
|
-
|
|
18
|
+
React.useEffect(() => {
|
|
19
19
|
fetchConfig();
|
|
20
20
|
return () => {
|
|
21
21
|
if (abortControllerRef.current) {
|
|
@@ -23,7 +23,7 @@ function useChatWidget(options) {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
}, [apiKey, widgetId]);
|
|
26
|
-
const fetchConfig =
|
|
26
|
+
const fetchConfig = React.useCallback(async () => {
|
|
27
27
|
try {
|
|
28
28
|
const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/config`, {
|
|
29
29
|
headers: {
|
|
@@ -50,7 +50,7 @@ function useChatWidget(options) {
|
|
|
50
50
|
onError === null || onError === void 0 ? void 0 : onError(error);
|
|
51
51
|
}
|
|
52
52
|
}, [apiKey, widgetId, apiBaseUrl, onError]);
|
|
53
|
-
const createSession =
|
|
53
|
+
const createSession = React.useCallback(async () => {
|
|
54
54
|
const response = await fetch(`${apiBaseUrl}/api/v1/widget/${widgetId}/sessions`, {
|
|
55
55
|
method: 'POST',
|
|
56
56
|
headers: {
|
|
@@ -67,7 +67,7 @@ function useChatWidget(options) {
|
|
|
67
67
|
}
|
|
68
68
|
throw new Error(result.message || 'Failed to create session');
|
|
69
69
|
}, [apiKey, widgetId, apiBaseUrl]);
|
|
70
|
-
const sendMessage =
|
|
70
|
+
const sendMessage = React.useCallback(async (content) => {
|
|
71
71
|
var _a;
|
|
72
72
|
if (!content.trim() || isLoading)
|
|
73
73
|
return;
|
|
@@ -174,11 +174,11 @@ function useChatWidget(options) {
|
|
|
174
174
|
setIsLoading(false);
|
|
175
175
|
}
|
|
176
176
|
}, [apiKey, widgetId, apiBaseUrl, session, isLoading, createSession, onError, onMessageSent, onMessageReceived]);
|
|
177
|
-
const clearMessages =
|
|
177
|
+
const clearMessages = React.useCallback(() => {
|
|
178
178
|
setMessages([]);
|
|
179
179
|
setSession(null);
|
|
180
180
|
}, []);
|
|
181
|
-
const retry =
|
|
181
|
+
const retry = React.useCallback(() => {
|
|
182
182
|
setError(null);
|
|
183
183
|
fetchConfig();
|
|
184
184
|
}, [fetchConfig]);
|
|
@@ -194,57 +194,115 @@ function useChatWidget(options) {
|
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
const
|
|
197
|
+
const useViewNavigation = (initialView = 'home') => {
|
|
198
|
+
const [state, setState] = React.useState({
|
|
199
|
+
currentView: initialView,
|
|
200
|
+
previousView: null,
|
|
201
|
+
});
|
|
202
|
+
const navigateToChat = React.useCallback(() => {
|
|
203
|
+
setState((prev) => ({
|
|
204
|
+
currentView: 'chat',
|
|
205
|
+
previousView: prev.currentView,
|
|
206
|
+
}));
|
|
207
|
+
}, []);
|
|
208
|
+
const navigateToHome = React.useCallback(() => {
|
|
209
|
+
setState((prev) => ({
|
|
210
|
+
currentView: 'home',
|
|
211
|
+
previousView: prev.currentView,
|
|
212
|
+
}));
|
|
213
|
+
}, []);
|
|
214
|
+
const goBack = React.useCallback(() => {
|
|
215
|
+
if (state.currentView !== 'home') {
|
|
216
|
+
setState((prev) => ({
|
|
217
|
+
currentView: 'home',
|
|
218
|
+
previousView: prev.currentView,
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
}, [state.currentView]);
|
|
222
|
+
const canGoBack = state.currentView !== 'home';
|
|
223
|
+
return {
|
|
224
|
+
currentView: state.currentView,
|
|
225
|
+
previousView: state.previousView,
|
|
226
|
+
canGoBack,
|
|
227
|
+
navigateToChat,
|
|
228
|
+
navigateToHome,
|
|
229
|
+
goBack,
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const ChatIcon$1 = () => (jsxRuntime.jsxs("svg", { className: "rag-chat-bubble-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.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" }), jsxRuntime.jsx("path", { d: "M7 9h10v2H7zm0-3h10v2H7z" })] }));
|
|
198
234
|
const CloseIcon$1 = () => (jsxRuntime.jsx("svg", { className: "rag-chat-bubble-icon", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.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" }) }));
|
|
199
235
|
const ChatBubble = ({ isOpen, onClick, primaryColor = '#007bff', position = 'bottom-right', unreadCount = 0 }) => {
|
|
200
|
-
return (jsxRuntime.jsxs("button", { className: `rag-chat-bubble ${position}`, onClick: onClick, style: { backgroundColor: primaryColor }, "aria-label": isOpen ? 'Close chat' : 'Open chat', type: "button", children: [isOpen ? jsxRuntime.jsx(CloseIcon$1, {}) : jsxRuntime.jsx(ChatIcon, {}), !isOpen && unreadCount > 0 && (jsxRuntime.jsx("span", { className: "rag-chat-bubble-badge", children: unreadCount > 9 ? '9+' : unreadCount }))] }));
|
|
236
|
+
return (jsxRuntime.jsxs("button", { className: `rag-chat-bubble ${position}`, onClick: onClick, style: { backgroundColor: primaryColor }, "aria-label": isOpen ? 'Close chat' : 'Open chat', type: "button", children: [isOpen ? jsxRuntime.jsx(CloseIcon$1, {}) : jsxRuntime.jsx(ChatIcon$1, {}), !isOpen && unreadCount > 0 && (jsxRuntime.jsx("span", { className: "rag-chat-bubble-badge", children: unreadCount > 9 ? '9+' : unreadCount }))] }));
|
|
201
237
|
};
|
|
202
238
|
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const inputRef = react.useRef(null);
|
|
211
|
-
// Scroll to bottom when new messages arrive
|
|
212
|
-
react.useEffect(() => {
|
|
213
|
-
var _a;
|
|
214
|
-
(_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
|
|
215
|
-
}, [messages]);
|
|
216
|
-
// Focus input when window opens
|
|
217
|
-
react.useEffect(() => {
|
|
218
|
-
var _a;
|
|
219
|
-
if (isOpen) {
|
|
220
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
221
|
-
}
|
|
222
|
-
}, [isOpen]);
|
|
223
|
-
const handleSubmit = (e) => {
|
|
224
|
-
e.preventDefault();
|
|
225
|
-
if (inputValue.trim() && !isLoading) {
|
|
226
|
-
onSendMessage(inputValue.trim());
|
|
227
|
-
setInputValue('');
|
|
228
|
-
}
|
|
229
|
-
};
|
|
239
|
+
const HeaderSection = ({ greeting, logoUrl, }) => {
|
|
240
|
+
return (jsxRuntime.jsxs("div", { className: "rag-home-header", children: [logoUrl && (jsxRuntime.jsx("img", { src: logoUrl, alt: "Logo", className: "rag-home-logo" })), jsxRuntime.jsx("h2", { className: "rag-home-greeting", children: greeting })] }));
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const ChatIcon = () => (jsxRuntime.jsx("svg", { "data-testid": "chat-icon", className: "rag-start-chat-card__icon", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
244
|
+
const ArrowIcon = () => (jsxRuntime.jsx("svg", { "data-testid": "arrow-icon", className: "rag-start-chat-card__arrow", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M9 18l6-6-6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
245
|
+
const StartChatCard = React.forwardRef(({ onStartChat, subtitle, }, ref) => {
|
|
230
246
|
const handleKeyDown = (e) => {
|
|
231
|
-
if (e.key === 'Enter'
|
|
247
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
232
248
|
e.preventDefault();
|
|
233
|
-
|
|
249
|
+
onStartChat();
|
|
234
250
|
}
|
|
235
251
|
};
|
|
236
|
-
|
|
252
|
+
return (jsxRuntime.jsxs("button", { ref: ref, type: "button", className: "rag-start-chat-card", onClick: onStartChat, onKeyDown: handleKeyDown, "aria-label": "Start new chat conversation", children: [jsxRuntime.jsx(ChatIcon, {}), jsxRuntime.jsxs("div", { className: "rag-start-chat-card__content", children: [jsxRuntime.jsx("div", { className: "rag-start-chat-card__title", children: "Start new chat" }), subtitle && (jsxRuntime.jsx("div", { className: "rag-start-chat-card__subtitle", children: subtitle }))] }), jsxRuntime.jsx(ArrowIcon, {})] }));
|
|
253
|
+
});
|
|
254
|
+
// Display name for debugging
|
|
255
|
+
StartChatCard.displayName = 'StartChatCard';
|
|
256
|
+
|
|
257
|
+
const ChevronIcon = () => (jsxRuntime.jsx("svg", { className: "rag-ticket-category__chevron", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M9 18l6-6-6-6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
258
|
+
const TicketCategoryList = ({ categories, onCategoryClick, }) => {
|
|
259
|
+
const enabledCategories = categories.filter((cat) => cat.enabled);
|
|
260
|
+
if (enabledCategories.length === 0) {
|
|
237
261
|
return null;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
262
|
+
}
|
|
263
|
+
return (jsxRuntime.jsxs("div", { className: "rag-ticket-section", children: [jsxRuntime.jsx("div", { className: "rag-ticket-section__title", children: "Submit a ticket" }), jsxRuntime.jsx("div", { className: "rag-ticket-list", children: enabledCategories.map((category) => (jsxRuntime.jsxs("button", { type: "button", className: "rag-ticket-category", onClick: () => onCategoryClick(category.id), "aria-label": `Submit ${category.label} ticket`, children: [jsxRuntime.jsx("span", { className: "rag-ticket-category__icon", children: category.icon }), jsxRuntime.jsx("span", { className: "rag-ticket-category__label", children: category.label }), jsxRuntime.jsx(ChevronIcon, {})] }, category.id))) })] }));
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const BookmarksRow = ({ bookmarks }) => {
|
|
267
|
+
if (bookmarks.length === 0) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
return (jsxRuntime.jsxs("nav", { className: "rag-bookmarks-section", role: "navigation", "aria-label": "Quick links", children: [jsxRuntime.jsx("div", { className: "rag-bookmarks-section__title", id: "bookmarks-title", children: "Bookmarks" }), jsxRuntime.jsx("div", { className: "rag-bookmarks-row", "aria-labelledby": "bookmarks-title", children: bookmarks.map((bookmark) => (jsxRuntime.jsxs("a", { href: bookmark.url, target: "_blank", rel: "noopener noreferrer", className: "rag-bookmark", "aria-label": `${bookmark.label} - opens in new tab`, children: [jsxRuntime.jsx("span", { className: "rag-bookmark__icon", "aria-hidden": "true", children: bookmark.icon }), jsxRuntime.jsx("span", { className: "rag-bookmark__label", children: bookmark.label })] }, bookmark.id))) })] }));
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const HomeView = ({ config, onStartChat, onTicketCategoryClick, autoFocus = false, }) => {
|
|
274
|
+
const homeConfig = config.homeView;
|
|
275
|
+
const startChatRef = React.useRef(null);
|
|
276
|
+
// Auto-focus first interactive element when autoFocus is enabled
|
|
277
|
+
React.useEffect(() => {
|
|
278
|
+
if (autoFocus && startChatRef.current) {
|
|
279
|
+
startChatRef.current.focus();
|
|
280
|
+
}
|
|
281
|
+
}, [autoFocus]);
|
|
282
|
+
// Don't render if home view is not enabled or missing
|
|
283
|
+
if (!(homeConfig === null || homeConfig === void 0 ? void 0 : homeConfig.enabled)) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
// Use homeView greeting, fallback to widget greeting
|
|
287
|
+
const greeting = homeConfig.greeting || config.greeting;
|
|
288
|
+
const handleTicketCategoryClick = (categoryId) => {
|
|
289
|
+
if (onTicketCategoryClick) {
|
|
290
|
+
onTicketCategoryClick(categoryId);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// Default behavior: log "Coming soon" message
|
|
294
|
+
console.log('Ticket category clicked (Coming soon):', categoryId);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
return (jsxRuntime.jsxs("div", { className: "rag-home-view", role: "region", "aria-label": "Help Center Home", children: [jsxRuntime.jsx(HeaderSection, { greeting: greeting, logoUrl: homeConfig.logoUrl }), homeConfig.showStartChat && (jsxRuntime.jsx(StartChatCard, { ref: startChatRef, onStartChat: onStartChat, subtitle: "How can we help you?" })), homeConfig.ticketCategories && homeConfig.ticketCategories.length > 0 && (jsxRuntime.jsx(TicketCategoryList, { categories: homeConfig.ticketCategories, onCategoryClick: handleTicketCategoryClick })), homeConfig.bookmarks && homeConfig.bookmarks.length > 0 && (jsxRuntime.jsx(BookmarksRow, { bookmarks: homeConfig.bookmarks })), config.showPoweredBy && (jsxRuntime.jsxs("div", { className: "rag-home-footer", children: ["Powered by ", jsxRuntime.jsx("a", { href: "https://rag-widget.com", target: "_blank", rel: "noopener noreferrer", children: "RAG Widget" })] }))] }));
|
|
241
298
|
};
|
|
242
299
|
|
|
243
300
|
const ChatWidget = ({ apiKey, widgetId, apiBaseUrl = '', position, primaryColor, greeting, placeholder, showPoweredBy, onError, onMessageSent, onMessageReceived }) => {
|
|
244
|
-
|
|
301
|
+
var _a, _b, _c;
|
|
302
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
245
303
|
// Determine the API base URL
|
|
246
304
|
const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
247
|
-
const { messages, isLoading,
|
|
305
|
+
const { messages, isLoading, config, error, sendMessage, retry } = useChatWidget({
|
|
248
306
|
apiKey,
|
|
249
307
|
widgetId,
|
|
250
308
|
apiBaseUrl: baseUrl,
|
|
@@ -261,21 +319,115 @@ const ChatWidget = ({ apiKey, widgetId, apiBaseUrl = '', position, primaryColor,
|
|
|
261
319
|
placeholder: placeholder || config.placeholder,
|
|
262
320
|
showPoweredBy: showPoweredBy !== undefined ? showPoweredBy : config.showPoweredBy
|
|
263
321
|
} : null;
|
|
322
|
+
// Determine initial view based on home view configuration
|
|
323
|
+
const homeViewEnabled = (_b = (_a = mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.homeView) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : false;
|
|
324
|
+
const initialView = homeViewEnabled ? 'home' : 'chat';
|
|
325
|
+
const { currentView, canGoBack, navigateToChat, navigateToHome, } = useViewNavigation(initialView);
|
|
326
|
+
// Update view when config loads - fixes race condition where config isn't
|
|
327
|
+
// available on first render but homeView.enabled is true
|
|
328
|
+
React.useEffect(() => {
|
|
329
|
+
var _a;
|
|
330
|
+
if (((_a = mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.homeView) === null || _a === void 0 ? void 0 : _a.enabled) && currentView === 'chat') {
|
|
331
|
+
navigateToHome();
|
|
332
|
+
}
|
|
333
|
+
}, [(_c = mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.homeView) === null || _c === void 0 ? void 0 : _c.enabled]);
|
|
264
334
|
const handleToggle = () => {
|
|
265
335
|
setIsOpen(prev => !prev);
|
|
266
336
|
};
|
|
267
337
|
const handleClose = () => {
|
|
268
338
|
setIsOpen(false);
|
|
269
339
|
};
|
|
340
|
+
const handleStartChat = () => {
|
|
341
|
+
navigateToChat();
|
|
342
|
+
};
|
|
343
|
+
const handleBack = () => {
|
|
344
|
+
navigateToHome();
|
|
345
|
+
};
|
|
346
|
+
const effectivePrimaryColor = (mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.primaryColor) || primaryColor || '#007bff';
|
|
347
|
+
const effectivePosition = (mergedConfig === null || mergedConfig === void 0 ? void 0 : mergedConfig.position) || position || 'bottom-right';
|
|
270
348
|
// Show error state in the window if there's an error
|
|
271
349
|
if (error && isOpen) {
|
|
272
|
-
return (jsxRuntime.jsxs("div", { className: "rag-chat-widget", children: [jsxRuntime.jsx(ChatBubble, { isOpen: isOpen, onClick: handleToggle, primaryColor:
|
|
350
|
+
return (jsxRuntime.jsxs("div", { className: "rag-chat-widget", children: [jsxRuntime.jsx(ChatBubble, { isOpen: isOpen, onClick: handleToggle, primaryColor: effectivePrimaryColor, position: effectivePosition }), jsxRuntime.jsxs("div", { className: `rag-chat-window ${effectivePosition}`, children: [jsxRuntime.jsxs("div", { className: "rag-chat-header", style: { backgroundColor: effectivePrimaryColor }, children: [jsxRuntime.jsx("h3", { className: "rag-chat-header-title", children: "Chat" }), jsxRuntime.jsx("button", { className: "rag-chat-header-close", onClick: handleClose, type: "button", "aria-label": "Close chat", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.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" }) }) })] }), jsxRuntime.jsxs("div", { className: "rag-chat-error", children: [jsxRuntime.jsx("p", { className: "rag-chat-error-message", children: error.message || 'Unable to connect. Please try again.' }), jsxRuntime.jsx("button", { className: "rag-chat-error-retry", onClick: retry, type: "button", children: "Retry" })] })] })] }));
|
|
273
351
|
}
|
|
274
|
-
|
|
352
|
+
// Determine which view to render
|
|
353
|
+
const shouldShowHomeView = currentView === 'home' && homeViewEnabled && mergedConfig;
|
|
354
|
+
const shouldShowChatView = currentView === 'chat' || !homeViewEnabled;
|
|
355
|
+
return (jsxRuntime.jsxs("div", { className: "rag-chat-widget", children: [jsxRuntime.jsx(ChatBubble, { isOpen: isOpen, onClick: handleToggle, primaryColor: effectivePrimaryColor, position: effectivePosition }), isOpen && mergedConfig && (jsxRuntime.jsxs("div", { className: `rag-chat-window ${effectivePosition}`, style: { '--rag-primary-color': effectivePrimaryColor }, children: [jsxRuntime.jsxs("div", { className: "rag-chat-header", style: { backgroundColor: effectivePrimaryColor }, children: [jsxRuntime.jsxs("div", { className: "rag-chat-header-left", children: [canGoBack && (jsxRuntime.jsx("button", { className: "rag-chat-header-back", onClick: handleBack, type: "button", "aria-label": "Back to home", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { fill: "currentColor", d: "M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" }) }) })), jsxRuntime.jsx("h3", { className: "rag-chat-header-title", children: mergedConfig.name || 'Chat' })] }), jsxRuntime.jsx("button", { className: "rag-chat-header-close", onClick: handleClose, "aria-label": "Close chat", type: "button", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.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" }) }) })] }), jsxRuntime.jsxs("div", { className: "rag-view-container", children: [shouldShowHomeView && (jsxRuntime.jsx(HomeView, { config: mergedConfig, onStartChat: handleStartChat })), shouldShowChatView && (jsxRuntime.jsx(ChatWindowContent, { config: mergedConfig, messages: messages, isLoading: isLoading, onSendMessage: sendMessage }))] })] }))] }));
|
|
356
|
+
};
|
|
357
|
+
const ChatWindowContent = ({ config, messages, isLoading, onSendMessage, }) => {
|
|
358
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
359
|
+
const messagesEndRef = React.useRef(null);
|
|
360
|
+
const inputRef = React.useRef(null);
|
|
361
|
+
// Scroll to bottom when new messages arrive
|
|
362
|
+
React.useEffect(() => {
|
|
363
|
+
if (messagesEndRef.current && typeof messagesEndRef.current.scrollIntoView === 'function') {
|
|
364
|
+
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
365
|
+
}
|
|
366
|
+
}, [messages]);
|
|
367
|
+
// Focus input when content mounts
|
|
368
|
+
React.useEffect(() => {
|
|
369
|
+
var _a;
|
|
370
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
371
|
+
}, []);
|
|
372
|
+
const handleSubmit = (e) => {
|
|
373
|
+
e.preventDefault();
|
|
374
|
+
if (inputValue.trim() && !isLoading) {
|
|
375
|
+
onSendMessage(inputValue.trim());
|
|
376
|
+
setInputValue('');
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const handleKeyDown = (e) => {
|
|
380
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
381
|
+
e.preventDefault();
|
|
382
|
+
handleSubmit(e);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "rag-chat-messages", children: [messages.length === 0 && (config.uspText || config.greeting) && (jsxRuntime.jsxs("div", { className: "rag-chat-greeting", children: [config.uspText && (jsxRuntime.jsx("p", { className: "rag-chat-usp-text", children: config.uspText })), config.greeting && (jsxRuntime.jsx("p", { className: "rag-chat-greeting-text", children: config.greeting }))] })), messages.map(message => (jsxRuntime.jsxs("div", { className: `rag-chat-message ${message.role}`, children: [message.isStreaming && !message.content ? (jsxRuntime.jsxs("div", { className: "rag-chat-loading", children: [jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" }), jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" }), jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" })] })) : (jsxRuntime.jsx("p", { className: "rag-chat-message-content", children: message.content })), message.sources && message.sources.length > 0 && (jsxRuntime.jsxs("div", { className: "rag-chat-message-sources", children: [jsxRuntime.jsx("div", { className: "rag-chat-message-sources-title", children: "Sources:" }), message.sources.map((source, index) => (jsxRuntime.jsx("div", { className: "rag-chat-message-source", children: source.title }, index)))] }))] }, message.id))), jsxRuntime.jsx("div", { ref: messagesEndRef })] }), jsxRuntime.jsxs("form", { className: "rag-chat-input-container", onSubmit: handleSubmit, children: [jsxRuntime.jsx("textarea", { ref: inputRef, className: "rag-chat-input", value: inputValue, onChange: e => setInputValue(e.target.value), onKeyDown: handleKeyDown, placeholder: config.placeholder || 'Type a message...', rows: 1, maxLength: config.allowedMessageLength || 2000, disabled: isLoading }), jsxRuntime.jsx("button", { type: "submit", className: "rag-chat-send-button", disabled: !inputValue.trim() || isLoading, style: { backgroundColor: config.primaryColor }, "aria-label": "Send message", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }) })] }), config.showPoweredBy && (jsxRuntime.jsxs("div", { className: "rag-chat-powered-by", children: ["Powered by ", jsxRuntime.jsx("a", { href: "https://rag-widget.com", target: "_blank", rel: "noopener noreferrer", children: "RAG Widget" })] }))] }));
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const CloseIcon = () => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.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" }) }));
|
|
389
|
+
const SendIcon = () => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", children: jsxRuntime.jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }));
|
|
390
|
+
const LoadingDots = () => (jsxRuntime.jsxs("div", { className: "rag-chat-loading", children: [jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" }), jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" }), jsxRuntime.jsx("div", { className: "rag-chat-loading-dot" })] }));
|
|
391
|
+
const MessageBubble = ({ message }) => (jsxRuntime.jsxs("div", { className: `rag-chat-message ${message.role}`, children: [message.isStreaming && !message.content ? (jsxRuntime.jsx(LoadingDots, {})) : (jsxRuntime.jsx("p", { className: "rag-chat-message-content", children: message.content })), message.sources && message.sources.length > 0 && (jsxRuntime.jsxs("div", { className: "rag-chat-message-sources", children: [jsxRuntime.jsx("div", { className: "rag-chat-message-sources-title", children: "Sources:" }), message.sources.map((source, index) => (jsxRuntime.jsx("div", { className: "rag-chat-message-source", children: source.title }, index)))] }))] }));
|
|
392
|
+
const ChatWindow = ({ isOpen, onClose, config, messages, isLoading, onSendMessage }) => {
|
|
393
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
394
|
+
const messagesEndRef = React.useRef(null);
|
|
395
|
+
const inputRef = React.useRef(null);
|
|
396
|
+
// Scroll to bottom when new messages arrive
|
|
397
|
+
React.useEffect(() => {
|
|
398
|
+
var _a;
|
|
399
|
+
(_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
|
|
400
|
+
}, [messages]);
|
|
401
|
+
// Focus input when window opens
|
|
402
|
+
React.useEffect(() => {
|
|
403
|
+
var _a;
|
|
404
|
+
if (isOpen) {
|
|
405
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
406
|
+
}
|
|
407
|
+
}, [isOpen]);
|
|
408
|
+
const handleSubmit = (e) => {
|
|
409
|
+
e.preventDefault();
|
|
410
|
+
if (inputValue.trim() && !isLoading) {
|
|
411
|
+
onSendMessage(inputValue.trim());
|
|
412
|
+
setInputValue('');
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
const handleKeyDown = (e) => {
|
|
416
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
417
|
+
e.preventDefault();
|
|
418
|
+
handleSubmit(e);
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
if (!isOpen)
|
|
422
|
+
return null;
|
|
423
|
+
const position = (config === null || config === void 0 ? void 0 : config.position) || 'bottom-right';
|
|
424
|
+
const primaryColor = (config === null || config === void 0 ? void 0 : config.primaryColor) || '#007bff';
|
|
425
|
+
return (jsxRuntime.jsxs("div", { className: `rag-chat-window ${position}`, style: { '--rag-primary-color': primaryColor }, children: [jsxRuntime.jsxs("div", { className: "rag-chat-header", style: { backgroundColor: primaryColor }, children: [jsxRuntime.jsx("h3", { className: "rag-chat-header-title", children: (config === null || config === void 0 ? void 0 : config.name) || 'Chat' }), jsxRuntime.jsx("button", { className: "rag-chat-header-close", onClick: onClose, "aria-label": "Close chat", type: "button", children: jsxRuntime.jsx(CloseIcon, {}) })] }), jsxRuntime.jsxs("div", { className: "rag-chat-messages", children: [messages.length === 0 && ((config === null || config === void 0 ? void 0 : config.uspText) || (config === null || config === void 0 ? void 0 : config.greeting)) && (jsxRuntime.jsxs("div", { className: "rag-chat-greeting", children: [(config === null || config === void 0 ? void 0 : config.uspText) && (jsxRuntime.jsx("p", { className: "rag-chat-usp-text", children: config.uspText })), (config === null || config === void 0 ? void 0 : config.greeting) && (jsxRuntime.jsx("p", { className: "rag-chat-greeting-text", children: config.greeting }))] })), messages.map(message => (jsxRuntime.jsx(MessageBubble, { message: message }, message.id))), jsxRuntime.jsx("div", { ref: messagesEndRef })] }), jsxRuntime.jsxs("form", { className: "rag-chat-input-container", onSubmit: handleSubmit, children: [jsxRuntime.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 }), jsxRuntime.jsx("button", { type: "submit", className: "rag-chat-send-button", disabled: !inputValue.trim() || isLoading, style: { backgroundColor: primaryColor }, "aria-label": "Send message", children: jsxRuntime.jsx(SendIcon, {}) })] }), (config === null || config === void 0 ? void 0 : config.showPoweredBy) && (jsxRuntime.jsxs("div", { className: "rag-chat-powered-by", children: ["Powered by ", jsxRuntime.jsx("a", { href: "https://rag-widget.com", target: "_blank", rel: "noopener noreferrer", children: "RAG Widget" })] }))] }));
|
|
275
426
|
};
|
|
276
427
|
|
|
277
|
-
const ChatWidgetContext =
|
|
428
|
+
const ChatWidgetContext = React.createContext(null);
|
|
278
429
|
const ChatWidgetProvider = ({ apiKey, widgetId, apiBaseUrl = '', onError, onMessageSent, onMessageReceived, children }) => {
|
|
430
|
+
var _a, _b, _c;
|
|
279
431
|
const baseUrl = apiBaseUrl || (typeof window !== 'undefined' ? window.location.origin : '');
|
|
280
432
|
const chatWidget = useChatWidget({
|
|
281
433
|
apiKey,
|
|
@@ -285,7 +437,13 @@ const ChatWidgetProvider = ({ apiKey, widgetId, apiBaseUrl = '', onError, onMess
|
|
|
285
437
|
onMessageSent,
|
|
286
438
|
onMessageReceived
|
|
287
439
|
});
|
|
288
|
-
|
|
440
|
+
// Determine initial view based on config
|
|
441
|
+
// If homeView is enabled, start with home; otherwise start with chat
|
|
442
|
+
const homeViewEnabled = (_c = (_b = (_a = chatWidget.config) === null || _a === void 0 ? void 0 : _a.homeView) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : false;
|
|
443
|
+
const initialView = homeViewEnabled ? 'home' : 'chat';
|
|
444
|
+
const viewNavigation = useViewNavigation(initialView);
|
|
445
|
+
const value = React.useMemo(() => ({
|
|
446
|
+
// Chat state
|
|
289
447
|
messages: chatWidget.messages,
|
|
290
448
|
isLoading: chatWidget.isLoading,
|
|
291
449
|
isConnected: chatWidget.isConnected,
|
|
@@ -293,23 +451,36 @@ const ChatWidgetProvider = ({ apiKey, widgetId, apiBaseUrl = '', onError, onMess
|
|
|
293
451
|
error: chatWidget.error,
|
|
294
452
|
sendMessage: chatWidget.sendMessage,
|
|
295
453
|
clearMessages: chatWidget.clearMessages,
|
|
296
|
-
retry: chatWidget.retry
|
|
297
|
-
|
|
454
|
+
retry: chatWidget.retry,
|
|
455
|
+
// View navigation
|
|
456
|
+
currentView: viewNavigation.currentView,
|
|
457
|
+
previousView: viewNavigation.previousView,
|
|
458
|
+
canGoBack: viewNavigation.canGoBack,
|
|
459
|
+
navigateToChat: viewNavigation.navigateToChat,
|
|
460
|
+
navigateToHome: viewNavigation.navigateToHome,
|
|
461
|
+
goBack: viewNavigation.goBack,
|
|
462
|
+
}), [chatWidget, viewNavigation]);
|
|
298
463
|
return (jsxRuntime.jsx(ChatWidgetContext.Provider, { value: value, children: children }));
|
|
299
464
|
};
|
|
300
465
|
const useChatWidgetContext = () => {
|
|
301
|
-
const context =
|
|
466
|
+
const context = React.useContext(ChatWidgetContext);
|
|
302
467
|
if (!context) {
|
|
303
468
|
throw new Error('useChatWidgetContext must be used within a ChatWidgetProvider');
|
|
304
469
|
}
|
|
305
470
|
return context;
|
|
306
471
|
};
|
|
307
472
|
|
|
473
|
+
exports.BookmarksRow = BookmarksRow;
|
|
308
474
|
exports.ChatBubble = ChatBubble;
|
|
309
475
|
exports.ChatWidget = ChatWidget;
|
|
310
476
|
exports.ChatWidgetProvider = ChatWidgetProvider;
|
|
311
477
|
exports.ChatWindow = ChatWindow;
|
|
478
|
+
exports.HeaderSection = HeaderSection;
|
|
479
|
+
exports.HomeView = HomeView;
|
|
480
|
+
exports.StartChatCard = StartChatCard;
|
|
481
|
+
exports.TicketCategoryList = TicketCategoryList;
|
|
312
482
|
exports.default = ChatWidget;
|
|
313
483
|
exports.useChatWidget = useChatWidget;
|
|
314
484
|
exports.useChatWidgetContext = useChatWidgetContext;
|
|
485
|
+
exports.useViewNavigation = useViewNavigation;
|
|
315
486
|
//# sourceMappingURL=index.js.map
|