@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/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 react = require('react');
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] = 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);
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
- react.useEffect(() => {
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 = react.useCallback(async () => {
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 = react.useCallback(async () => {
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 = react.useCallback(async (content) => {
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 = react.useCallback(() => {
177
+ const clearMessages = React.useCallback(() => {
178
178
  setMessages([]);
179
179
  setSession(null);
180
180
  }, []);
181
- const retry = react.useCallback(() => {
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 ChatIcon = () => (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" })] }));
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 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" }) }));
204
- 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" }) }));
205
- 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" })] }));
206
- 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)))] }))] }));
207
- const ChatWindow = ({ isOpen, onClose, config, messages, isLoading, onSendMessage }) => {
208
- const [inputValue, setInputValue] = react.useState('');
209
- const messagesEndRef = react.useRef(null);
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' && !e.shiftKey) {
247
+ if (e.key === 'Enter' || e.key === ' ') {
232
248
  e.preventDefault();
233
- handleSubmit(e);
249
+ onStartChat();
234
250
  }
235
251
  };
236
- if (!isOpen)
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
- const position = (config === null || config === void 0 ? void 0 : config.position) || 'bottom-right';
239
- const primaryColor = (config === null || config === void 0 ? void 0 : config.primaryColor) || '#007bff';
240
- 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" })] }))] }));
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
- const [isOpen, setIsOpen] = react.useState(false);
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, isConnected, config, error, sendMessage, retry } = useChatWidget({
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: primaryColor || '#007bff', position: position || 'bottom-right' }), jsxRuntime.jsxs("div", { className: `rag-chat-window ${position || 'bottom-right'}`, children: [jsxRuntime.jsxs("div", { className: "rag-chat-header", style: { backgroundColor: primaryColor || '#007bff' }, children: [jsxRuntime.jsx("h3", { className: "rag-chat-header-title", children: "Chat" }), jsxRuntime.jsx("button", { className: "rag-chat-header-close", onClick: handleClose, 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-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" })] })] })] }));
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
- return (jsxRuntime.jsxs("div", { className: "rag-chat-widget", children: [jsxRuntime.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 && (jsxRuntime.jsx(ChatWindow, { isOpen: isOpen, onClose: handleClose, config: mergedConfig, messages: messages, isLoading: isLoading, onSendMessage: sendMessage }))] }));
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 = react.createContext(null);
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
- const value = react.useMemo(() => ({
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
- }), [chatWidget]);
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 = react.useContext(ChatWidgetContext);
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