@xapp/chat-widget 1.81.5 → 1.83.1

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.es.js CHANGED
@@ -1,4 +1,4 @@
1
- import require$$1, { useRef, useEffect, useCallback, createContext, useMemo, useContext, memo, useState, useLayoutEffect } from 'react';
1
+ import require$$1, { useRef, useEffect, useCallback, createContext, useContext, useMemo, memo, useState, useLayoutEffect } from 'react';
2
2
  export { default as React } from 'react';
3
3
  import require$$0, { jsx, Fragment, jsxs } from 'react/jsx-runtime';
4
4
  import { useSelector, useDispatch, Provider } from 'react-redux';
@@ -1612,7 +1612,7 @@ var RichText = function (props) {
1612
1612
  };
1613
1613
 
1614
1614
  var RichInput = function (props) {
1615
- var id = props.id, value = props.value, type = props.type, autoFocus = props.autoFocus, spellCheck = props.spellCheck, tabIndex = props.tabIndex, onChange = props.onChange, onInput = props.onInput, onKeyDown = props.onKeyDown, onSearch = props.onSearch;
1615
+ var id = props.id, value = props.value, type = props.type, autoFocus = props.autoFocus, spellCheck = props.spellCheck, tabIndex = props.tabIndex, maxLength = props.maxLength, onChange = props.onChange, onInput = props.onInput, onKeyDown = props.onKeyDown, onSearch = props.onSearch;
1616
1616
  var rich = value.formats.some(function (f) { return f.type === "inputText"; });
1617
1617
  var handleChange = react.useCallback(function (ev) {
1618
1618
  if (onChange) {
@@ -1665,7 +1665,7 @@ var RichInput = function (props) {
1665
1665
  }
1666
1666
  return undefined;
1667
1667
  }, [inputNode, onSearch]);
1668
- return (jsxRuntime.jsx("div", { className: "xappw-rich-input ".concat(props.className), children: rich ? (jsxRuntime.jsx(RichText, { id: id, value: value, onChange: handleRichChange, onInput: handleRichInput, onKeyDown: handleKeyDown, className: "xappw-rich-input__input ".concat(props.className, "__input") })) : (jsxRuntime.jsx("input", { id: id, ref: inputRef, type: type, value: value.text, autoComplete: "off", autoFocus: autoFocus, placeholder: props.placeholder, spellCheck: spellCheck, tabIndex: tabIndex ? Number(tabIndex) : 0, className: "xappw-rich-input__input ".concat(props.className, "__input"), onFocus: props.onFocus, onChange: handleChange, onInput: handleInput, onKeyDown: handleKeyDown })) }));
1668
+ return (jsxRuntime.jsx("div", { className: "xappw-rich-input ".concat(props.className), children: rich ? (jsxRuntime.jsx(RichText, { id: id, value: value, onChange: handleRichChange, onInput: handleRichInput, onKeyDown: handleKeyDown, className: "xappw-rich-input__input ".concat(props.className, "__input") })) : (jsxRuntime.jsx("input", { id: id, ref: inputRef, type: type, value: value.text, autoComplete: "off", autoFocus: autoFocus, placeholder: props.placeholder, spellCheck: spellCheck, tabIndex: tabIndex ? Number(tabIndex) : 0, maxLength: maxLength, className: "xappw-rich-input__input ".concat(props.className, "__input"), onFocus: props.onFocus, onChange: handleChange, onInput: handleInput, onKeyDown: handleKeyDown })) }));
1669
1669
  };
1670
1670
 
1671
1671
  var SuggestionsGroupHeading = function (props) {
@@ -2541,6 +2541,8 @@ var StentorDirectChat = /** @class */ (function () {
2541
2541
  if (origin) {
2542
2542
  attributes["origin"] = origin;
2543
2543
  }
2544
+ // Add current time for accurate date/time responses
2545
+ attributes["currentTime"] = new Date().toISOString();
2544
2546
  now = new Date().getTime();
2545
2547
  if (this.isNewSession && !((_a = message === null || message === void 0 ? void 0 : message.msg) === null || _a === void 0 ? void 0 : _a.text)) {
2546
2548
  request = {
@@ -3767,6 +3769,8 @@ var StentorRouterChat = /** @class */ (function () {
3767
3769
  if (origin) {
3768
3770
  attributes["origin"] = origin;
3769
3771
  }
3772
+ // Add current time for accurate date/time responses
3773
+ attributes["currentTime"] = new Date().toISOString();
3770
3774
  request = requestFromMessage(message, userId, this.isNewSession, sessionId, accessToken, attributes, this.visitorInfo);
3771
3775
  // Router attributes are now added at the payload root level in emit()
3772
3776
  this.emit("new message", request);
@@ -9046,7 +9050,7 @@ var ChatMessage = function (props) {
9046
9050
  var agentInfo = (_a = props.agents) === null || _a === void 0 ? void 0 : _a[props.message.user.nick];
9047
9051
  var hideUserInfo = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.hideUserInfo) || false;
9048
9052
  function renderByType() {
9049
- var _a, _b, _c, _d;
9053
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
9050
9054
  var msg = props.message.msg;
9051
9055
  switch (props.message.type) {
9052
9056
  // TODO: props actually requires it to be "chat.msg". Fix prop typing?
@@ -9055,15 +9059,15 @@ var ChatMessage = function (props) {
9055
9059
  // OR card OR list only. Avatar with text bubble.
9056
9060
  var avaKey = avaKeys.find(function (key) { return !!msg[key]; });
9057
9061
  return (jsxs(Fragment, { children: [msg.text &&
9058
- jsx(ChatMessagePart, { showAvatar: avaKey === "text", user: user, avatarPosition: (_a = chatConfig.env.theme.messages) === null || _a === void 0 ? void 0 : _a.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatTextMessage, { message: props.message, sibling: props.sibling }) }), msg.html &&
9059
- jsx(ChatMessagePart, { showAvatar: avaKey === "html", user: user, avatarPosition: (_b = chatConfig.env.theme.messages) === null || _b === void 0 ? void 0 : _b.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatMarkdownMessage, { message: props.message, sibling: props.sibling, onOpenUrl: (_c = props.middlewareContext) === null || _c === void 0 ? void 0 : _c.openUrl }) }), msg.displays && middleware && msg.displays.map(function (display, index) {
9062
+ jsx(ChatMessagePart, { showAvatar: avaKey === "text", user: user, avatarPosition: (_c = (_b = (_a = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _a === void 0 ? void 0 : _a.theme) === null || _b === void 0 ? void 0 : _b.messages) === null || _c === void 0 ? void 0 : _c.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatTextMessage, { message: props.message, sibling: props.sibling }) }), msg.html &&
9063
+ jsx(ChatMessagePart, { showAvatar: avaKey === "html", user: user, avatarPosition: (_f = (_e = (_d = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _d === void 0 ? void 0 : _d.theme) === null || _e === void 0 ? void 0 : _e.messages) === null || _f === void 0 ? void 0 : _f.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatMarkdownMessage, { message: props.message, sibling: props.sibling, onOpenUrl: (_g = props.middlewareContext) === null || _g === void 0 ? void 0 : _g.openUrl }) }), msg.displays && middleware && msg.displays.map(function (display, index) {
9060
9064
  if (display.type === "ScheduleButton") {
9061
9065
  return (jsx(ChatScheduleWidget, { minimizeOnClick: props.minimizeOnClick, display: display }));
9062
9066
  }
9063
9067
  var Middleware = middleware;
9064
9068
  return (jsx(Middleware, { msg: display, ctx: props.middlewareContext }, index));
9065
9069
  }), msg.permissionRequest && ctx &&
9066
- jsx(ChatMessagePart, { showAvatar: avaKey === "permissionRequest", user: user, avatarPosition: (_d = chatConfig.env.theme.messages) === null || _d === void 0 ? void 0 : _d.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatPermissionMessage, { message: props.message, sibling: props.sibling, ctx: ctx }) })] }));
9070
+ jsx(ChatMessagePart, { showAvatar: avaKey === "permissionRequest", user: user, avatarPosition: (_k = (_j = (_h = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _h === void 0 ? void 0 : _h.theme) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatPermissionMessage, { message: props.message, sibling: props.sibling, ctx: ctx }) })] }));
9067
9071
  }
9068
9072
  return (jsx(Fragment, {}));
9069
9073
  }
@@ -9170,10 +9174,12 @@ function useChatServerVisitorId() {
9170
9174
  }
9171
9175
  //send whenever server settings or visitor id changes
9172
9176
  function useGreeting(active) {
9177
+ var _a, _b;
9173
9178
  var curr = useChatServerVisitorId();
9174
9179
  var snapshotRef = useRef(null);
9175
9180
  var ctx = useContext(ChatConfigContext);
9176
- var isAdmin = ctx.env.isAdmin;
9181
+ // Handle null context (e.g., in preview mode)
9182
+ var isAdmin = (_b = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.env) === null || _a === void 0 ? void 0 : _a.isAdmin) !== null && _b !== void 0 ? _b : false;
9177
9183
  var sessionId = useSelector(function (state) { return state.sessionId; });
9178
9184
  useEffect(function () {
9179
9185
  if (active) {
@@ -9452,10 +9458,16 @@ var SendButton = function (props) {
9452
9458
  return (jsx(Fragment, { children: !props.sendButtonIcon ? (jsx(IconButton_1, { className: "xappw-send-button ".concat(props.className || "", " ").concat(disabled ? 'disabled' : ''), tabIndex: props.tabIndex, onClick: disabled ? undefined : props.onClick, icon: SendIcon })) : (jsx("button", { className: "xappw-custom-send-button ".concat(disabled ? 'disabled' : ''), tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, onClick: props.onClick, disabled: disabled, onMouseEnter: function () { return setIsHovered(true); }, onMouseLeave: function () { return setIsHovered(false); }, children: jsx("img", { src: getIconSrc(), alt: "Send button", draggable: false, className: "send-button-icon" }) })) }));
9453
9459
  };
9454
9460
 
9461
+ // Show counter when within this percentage of the limit
9462
+ var COUNTER_THRESHOLD_PERCENT = 0.8;
9455
9463
  var Input = function (props) {
9456
9464
  var _a, _b;
9457
9465
  var value = props.value, placeholder = props.placeholder, sendButtonIcon = props.sendButtonIcon, sendButtonIconHover = props.sendButtonIconHover, sendButtonIconDisabled = props.sendButtonIconDisabled, suggestion = props.suggestion, footerConfig = props.footerConfig, inputConfig = props.inputConfig, onChange = props.onChange, onSubmit = props.onSubmit, onSuggestionCommand = props.onSuggestionCommand;
9458
9466
  var _c = useState(false), dragover = _c[0], setDragover = _c[1];
9467
+ var maxLength = inputConfig === null || inputConfig === void 0 ? void 0 : inputConfig.maxLength;
9468
+ var currentLength = value.text.length;
9469
+ var isAtLimit = maxLength !== undefined && currentLength >= maxLength;
9470
+ var showCounter = maxLength !== undefined && currentLength >= maxLength * COUNTER_THRESHOLD_PERCENT;
9459
9471
  function onDragOver(event) {
9460
9472
  setDragover(true);
9461
9473
  event.preventDefault();
@@ -9503,9 +9515,9 @@ var Input = function (props) {
9503
9515
  return (jsx("div", { className: "xappw-input-container ".concat(props.addClass, " ").concat(dragover ? "drag-drop-zone" : ""), "aria-label": "To start typing click on rounded rectangle at the bottom of widget. To send your message click icon on right side of rounded rectangle at the bottom of widget" +
9504
9516
  value.text
9505
9517
  ? "To clear input field click on clear icon on the left of send button"
9506
- : "", "aria-hidden": false, onDrop: onDrop, onDragOver: onDragOver, onDragLeave: onDragLeave, children: jsxs("form", { className: "xappw-input-form", onSubmit: handleOnSubmit, children: [jsx(RichInput_1, { id: "chatWidgetInput", className: "xappw-input", placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : "type your question here", tabIndex: inputConfig === null || inputConfig === void 0 ? void 0 : inputConfig.tabIndex, onInput: onChange, onChange: onChange, onKeyDown: handleKeyDown,
9518
+ : "", "aria-hidden": false, onDrop: onDrop, onDragOver: onDragOver, onDragLeave: onDragLeave, children: jsxs("form", { className: "xappw-input-form", onSubmit: handleOnSubmit, children: [jsx(RichInput_1, { id: "chatWidgetInput", className: "xappw-input", placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : "type your question here", tabIndex: inputConfig === null || inputConfig === void 0 ? void 0 : inputConfig.tabIndex, maxLength: maxLength, onInput: onChange, onChange: onChange, onKeyDown: handleKeyDown,
9507
9519
  // onFocus={onFocus}
9508
- value: value, spellCheck: true }, "input"), jsxs("div", { className: "xappw-input-form__buttons", children: [value.text && (jsx(IconButton_1, { icon: CloseIcon, tabIndex: (_a = footerConfig === null || footerConfig === void 0 ? void 0 : footerConfig.clearButton) === null || _a === void 0 ? void 0 : _a.tabIndex, className: "xappw-input-form__btn", onClick: handleClear })), jsx(SendButton, { className: "xappw-input-form__btn", sendButtonIcon: sendButtonIcon, sendButtonIconHover: sendButtonIconHover, sendButtonIconDisabled: sendButtonIconDisabled, tabIndex: (_b = footerConfig === null || footerConfig === void 0 ? void 0 : footerConfig.sendButton) === null || _b === void 0 ? void 0 : _b.tabIndex, disabled: !value.text, onClick: handleOnSubmit })] })] }) }));
9520
+ value: value, spellCheck: true }, "input"), jsxs("div", { className: "xappw-input-form__buttons", children: [showCounter && (jsxs("span", { className: "xappw-input-form__counter".concat(isAtLimit ? " xappw-input-form__counter--limit" : ""), children: [currentLength, "/", maxLength] })), value.text && (jsx(IconButton_1, { icon: CloseIcon, tabIndex: (_a = footerConfig === null || footerConfig === void 0 ? void 0 : footerConfig.clearButton) === null || _a === void 0 ? void 0 : _a.tabIndex, className: "xappw-input-form__btn", onClick: handleClear })), jsx(SendButton, { className: "xappw-input-form__btn", sendButtonIcon: sendButtonIcon, sendButtonIconHover: sendButtonIconHover, sendButtonIconDisabled: sendButtonIconDisabled, tabIndex: (_b = footerConfig === null || footerConfig === void 0 ? void 0 : footerConfig.sendButton) === null || _b === void 0 ? void 0 : _b.tabIndex, disabled: !value.text, onClick: handleOnSubmit })] })] }) }));
9509
9521
  };
9510
9522
 
9511
9523
  function createActions(onItemUse) {
@@ -9566,7 +9578,7 @@ var Suggestions = function (props) {
9566
9578
 
9567
9579
  var ChatFooter = function (props) {
9568
9580
  var _a, _b, _c;
9569
- var isAdmin = props.isAdmin, isChatting = props.isChatting, visible = props.visible, placeholder = props.placeholder, sendButtonIcon = props.sendButtonIcon, sendButtonIconHover = props.sendButtonIconHover, sendButtonIconDisabled = props.sendButtonIconDisabled, footerConfig = props.footerConfig, menuConfig = props.menuConfig, inputConfig = props.inputConfig, hasWsButton = props.hasWsButton, onSubmit = props.onSubmit;
9581
+ var isAdmin = props.isAdmin, isChatting = props.isChatting, visible = props.visible, placeholder = props.placeholder, sendButtonIcon = props.sendButtonIcon, sendButtonIconHover = props.sendButtonIconHover, sendButtonIconDisabled = props.sendButtonIconDisabled, footerConfig = props.footerConfig, menuConfig = props.menuConfig, inputConfig = props.inputConfig, hasWsButton = props.hasWsButton, disabled = props.disabled, onSubmit = props.onSubmit;
9570
9582
  var innerDispatch = useChatDispatch();
9571
9583
  var _d = useState(false), drawerOpen = _d[0], setDrawerState = _d[1]; // false initially
9572
9584
  var _e = useState(), suggestionSearch = _e[0], setSuggestionSearch = _e[1];
@@ -9624,7 +9636,7 @@ var ChatFooter = function (props) {
9624
9636
  setEnableInput(status);
9625
9637
  };
9626
9638
  return (jsxs("div", { className: "chat-footer background-footer", "aria-label": menuItems.length ? "to open menu click a button above the rounded rectangle at the bottom of widget" : "", "aria-hidden": false, children: [showMenu && menuItems.length ?
9627
- jsxs(Fragment, { children: [drawerOpen ? jsx(ChatMenu, { opened: drawerOpen, tabIndex: menuItemsTabIndex, onItemClick: handleMenuItem, items: menuItems }) : jsx(Fragment, {}), jsx("div", { className: "chat-footer__menu-icon", children: jsx(DrawerBars, { tabIndex: menuButtonTabIndex, onToggle: toggleDrawer }) })] }) : jsx(Fragment, {}), jsx(Suggestions, { className: "xappw-chat-footer__suggestions", data: suggestions.suggestions, index: suggestions.index, searchTerms: suggestionSearch, hasWsButton: hasWsButton, onItemClick: handleItemClick, onItemUse: handleItemUse }), isAdmin && isChatting && visible && jsx(AdminBar, { onAdminJoin: handleAdminJoin }), jsx("div", { style: { pointerEvents: enableInput ? "auto" : "none", opacity: enableInput ? 1 : 0.5 }, children: jsx(Input, { addClass: "chat-footer__input " + (isChatting && visible ? "visible" : ""), suggestion: suggestions.item, value: input, placeholder: placeholder, sendButtonIcon: sendButtonIcon, sendButtonIconHover: sendButtonIconHover, sendButtonIconDisabled: sendButtonIconDisabled, footerConfig: footerConfig, inputConfig: inputConfig, onSubmit: handleSubmit, onChange: handleChange, onSuggestionCommand: suggestions.execute,
9639
+ jsxs(Fragment, { children: [drawerOpen ? jsx(ChatMenu, { opened: drawerOpen, tabIndex: menuItemsTabIndex, onItemClick: handleMenuItem, items: menuItems }) : jsx(Fragment, {}), jsx("div", { className: "chat-footer__menu-icon", children: jsx(DrawerBars, { tabIndex: menuButtonTabIndex, onToggle: toggleDrawer }) })] }) : jsx(Fragment, {}), jsx(Suggestions, { className: "xappw-chat-footer__suggestions", data: suggestions.suggestions, index: suggestions.index, searchTerms: suggestionSearch, hasWsButton: hasWsButton, onItemClick: handleItemClick, onItemUse: handleItemUse }), isAdmin && isChatting && visible && jsx(AdminBar, { onAdminJoin: handleAdminJoin }), jsx("div", { style: { pointerEvents: enableInput && !disabled ? "auto" : "none", opacity: enableInput ? 1 : 0.5 }, children: jsx(Input, { addClass: "chat-footer__input " + (isChatting && visible ? "visible" : ""), suggestion: suggestions.item, value: input, placeholder: placeholder, sendButtonIcon: sendButtonIcon, sendButtonIconHover: sendButtonIconHover, sendButtonIconDisabled: sendButtonIconDisabled, footerConfig: footerConfig, inputConfig: inputConfig, onSubmit: handleSubmit, onChange: handleChange, onSuggestionCommand: suggestions.execute,
9628
9640
  // onFocus={this.inputOnFocus}
9629
9641
  onFileUpload: props.onFileUpload }) }), brandingEnabled && brandingText && jsx(ChatBranding, { text: brandingText })] }));
9630
9642
  };
@@ -31384,7 +31396,7 @@ var TypingStatus = function (props) {
31384
31396
  };
31385
31397
 
31386
31398
  var MessageList = function (_a) {
31387
- _a.visible; var _c = _a.messages, messages = _c === void 0 ? [] : _c, _d = _a.agents, agents = _d === void 0 ? {} : _d; _a.isOffline; _a.isChatting; var _g = _a.queuePosition, queuePosition = _g === void 0 ? 0 : _g, _h = _a.lastRatingRequestTimestamp, lastRatingRequestTimestamp = _h === void 0 ? 0 : _h, _j = _a.hasRating, hasRating = _j === void 0 ? false : _j; _a.visitorId; var messageMiddleware = _a.messageMiddleware, textTypingStatusEnabled = _a.textTypingStatusEnabled, agent = _a.agent, hasWsButton = _a.hasWsButton, _l = _a.onSend, onSend = _l === void 0 ? function () { return Promise.resolve(); } : _l, _m = _a.onWrite, onWrite = _m === void 0 ? function () { return Promise.resolve(); } : _m, _o = _a.onOpenUrl, onOpenUrl = _o === void 0 ? function () { } : _o, _p = _a.minimizeOnClick, minimizeOnClick = _p === void 0 ? function () { } : _p; _a.children;
31399
+ _a.visible; var _c = _a.messages, messages = _c === void 0 ? [] : _c, _d = _a.agents, agents = _d === void 0 ? {} : _d; _a.isOffline; _a.isChatting; var _g = _a.queuePosition, queuePosition = _g === void 0 ? 0 : _g, _h = _a.lastRatingRequestTimestamp, lastRatingRequestTimestamp = _h === void 0 ? 0 : _h, _j = _a.hasRating, hasRating = _j === void 0 ? false : _j; _a.visitorId; var messageMiddleware = _a.messageMiddleware, textTypingStatusEnabled = _a.textTypingStatusEnabled, agent = _a.agent, hasWsButton = _a.hasWsButton, _l = _a.disableAutoScroll, disableAutoScroll = _l === void 0 ? false : _l, _m = _a.onSend, onSend = _m === void 0 ? function () { return Promise.resolve(); } : _m, _o = _a.onWrite, onWrite = _o === void 0 ? function () { return Promise.resolve(); } : _o, _p = _a.onOpenUrl, onOpenUrl = _p === void 0 ? function () { } : _p, _q = _a.minimizeOnClick, minimizeOnClick = _q === void 0 ? function () { } : _q; _a.children;
31388
31400
  var messagesEndRef = useRef(null);
31389
31401
  var containerRef = useRef(null);
31390
31402
  var prevHasWsButtonRef = useRef(hasWsButton);
@@ -31402,12 +31414,17 @@ var MessageList = function (_a) {
31402
31414
  messagesEndRef.current.scrollIntoView({ behavior: behavior });
31403
31415
  }
31404
31416
  }, []);
31405
- // Always scroll to bottom when messages change
31417
+ // Always scroll to bottom when messages change (unless disabled)
31406
31418
  useEffect(function () {
31407
- scrollToBottom("smooth");
31408
- }, [messages, agents, scrollToBottom]);
31409
- // Handle WS button dismissal - only scroll when button is removed
31419
+ if (!disableAutoScroll) {
31420
+ scrollToBottom("smooth");
31421
+ }
31422
+ }, [messages, agents, scrollToBottom, disableAutoScroll]);
31423
+ // Handle WS button dismissal - only scroll when button is removed (unless disabled)
31410
31424
  useEffect(function () {
31425
+ if (disableAutoScroll) {
31426
+ return undefined;
31427
+ }
31411
31428
  var prevHasButton = prevHasWsButtonRef.current;
31412
31429
  var currentHasButton = hasWsButton;
31413
31430
  // Update ref for next render
@@ -31424,15 +31441,19 @@ var MessageList = function (_a) {
31424
31441
  }
31425
31442
  }
31426
31443
  return undefined;
31427
- }, [hasWsButton, isAtBottom, scrollToBottom]);
31444
+ }, [hasWsButton, isAtBottom, scrollToBottom, disableAutoScroll]);
31428
31445
  var handleSend = useCallback(function (msg) {
31429
31446
  onSend(msg);
31430
- messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
31431
- }, [onSend]);
31447
+ if (!disableAutoScroll && messagesEndRef.current) {
31448
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
31449
+ }
31450
+ }, [onSend, disableAutoScroll]);
31432
31451
  var handleWrite = useCallback(function (msg) {
31433
31452
  onWrite(msg);
31434
- messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
31435
- }, [onWrite]);
31453
+ if (!disableAutoScroll && messagesEndRef.current) {
31454
+ messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
31455
+ }
31456
+ }, [onWrite, disableAutoScroll]);
31436
31457
  var ctxCache = useMemo(function () {
31437
31458
  return new MiddlewareContextFactory({
31438
31459
  send: handleSend,
@@ -32139,425 +32160,61 @@ var ModalContent = function (_a) {
32139
32160
  };
32140
32161
 
32141
32162
  /**
32142
- * Debounce delay for typing indicator in milliseconds.
32143
- * After this delay of inactivity, the "stop typing" event will be sent.
32163
+ * Id the visitor (cookies, browser fingerprint, etc)
32164
+ *
32165
+ * @export
32166
+ * @returns {string}
32144
32167
  */
32145
- var TYPING_INDICATOR_DEBOUNCE_MS = 2000;
32146
- var ChatWidgetWrapper = function (props) {
32147
- var _a;
32148
- var _b = useState(props.config), rawConfig = _b[0], setRawConfig = _b[1];
32149
- var _c = useState(!!props.getConfig), configLoading = _c[0], setConfigLoading = _c[1];
32150
- var _d = useState(), configError = _d[0], setConfigError = _d[1];
32151
- // Load config from callback if provided
32152
- useEffect(function () {
32153
- var cancelled = false;
32154
- if (props.getConfig) {
32155
- setConfigLoading(true);
32156
- setConfigError(undefined);
32157
- props
32158
- .getConfig()
32159
- .then(function (config) {
32160
- if (!cancelled) {
32161
- setRawConfig(config);
32162
- setConfigLoading(false);
32163
- log("[ChatWidget] Config loaded from getConfig callback");
32164
- }
32165
- })
32166
- .catch(function (error) {
32167
- if (!cancelled) {
32168
- setConfigError(error);
32169
- setConfigLoading(false);
32170
- err("[ChatWidget] Failed to load config: ".concat(error.message));
32171
- }
32172
- });
32173
- }
32174
- else if (props.config) {
32175
- // If no callback, use the config prop directly
32176
- setRawConfig(props.config);
32177
- setConfigLoading(false);
32178
- }
32179
- return function () {
32180
- cancelled = true;
32181
- };
32182
- // Only depend on getConfig and config - not the entire props object to avoid infinite loops
32183
- // eslint-disable-next-line react-hooks/exhaustive-deps
32184
- }, [props.getConfig, props.config]);
32185
- var connection = useConnectionInfo(rawConfig);
32186
- var config = useMemo(function () {
32187
- var _a;
32188
- return (__assign(__assign({}, rawConfig), { connection: connection, assetUrl: (_a = connection === null || connection === void 0 ? void 0 : connection.serverUrl) !== null && _a !== void 0 ? _a : defaultServerUrl, env: rawConfig }));
32189
- }, [connection, rawConfig]);
32190
- var token = useSelector(function (state) { return state.connection.token; });
32191
- var options = useMemo(function () {
32192
- var configurableMessages = getConfigurableMessages();
32193
- if ((rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages) &&
32194
- Array.isArray(rawConfig.configurableMessages.items) &&
32195
- rawConfig.configurableMessages.items.length > 0) {
32196
- configurableMessages = rawConfig.configurableMessages;
32197
- }
32198
- return {
32199
- token: token,
32200
- bot: {
32201
- nick: "Bot",
32202
- displayName: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
32203
- avatarPath: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
32204
- },
32205
- configurableMessages: configurableMessages,
32206
- hooks: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
32207
- env: rawConfig,
32208
- };
32209
- }, [
32210
- token,
32211
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
32212
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
32213
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages,
32214
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
32215
- ]);
32216
- // Only create chat server when config is ready (not loading and no error)
32217
- var chatServer = useChatServer(configLoading || configError ? null : connection, configLoading || configError ? null : options);
32218
- // Determine mode class for loading/error states
32219
- var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
32220
- var modeClass = "widget-container--".concat(mode);
32221
- // Show loading state while config is being fetched
32222
- if (configLoading) {
32223
- return (jsx("div", { className: "widget-container widget-container--loading ".concat(modeClass), children: jsx("div", { className: "xa-spinner-container visible", children: jsx("div", { className: "xa-spinner" }) }) }));
32224
- }
32225
- // Show error state if config failed to load
32226
- if (configError) {
32227
- return (jsx("div", { className: "widget-container widget-container--error ".concat(modeClass), children: jsxs("div", { className: "widget-error-message", children: ["Failed to load chat configuration: ", configError.message] }) }));
32228
- }
32229
- return (jsx(ChatConfigContext.Provider, { value: config, children: jsx(ChatServerContext.Provider, { value: chatServer, children: jsx(ChatWidget, __assign({}, props, { config: rawConfig })) }) }));
32168
+ function visitorFingerprint() {
32169
+ return uuid_1();
32170
+ }
32171
+
32172
+ var DEFAULT_VISITOR = {
32173
+ displayName: "Visitor",
32174
+ nick: "visitor:",
32175
+ typing: false
32230
32176
  };
32231
- var ChatWidget = function (props) {
32232
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7;
32233
- var innerDispatch = useChatDispatch();
32234
- var dispatch = useChatServerDispatch();
32235
- // From Redux
32236
- var chatState = useSelector(function (state) { return state; });
32237
- // Refresh modalReference
32238
- var modalRef = useRef({});
32239
- var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
32240
- var dockedMode = mode === "docked";
32241
- var staticMode = mode === "static";
32242
- var modeClass = "widget-container--".concat(mode);
32243
- var canRefresh = (_c = (_b = props.config.header) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.refresh;
32244
- // can't minimize in docked mode or static mode.
32245
- var canMinimize = !dockedMode && !staticMode && ((_e = (_d = props.config.header) === null || _d === void 0 ? void 0 : _d.actions) === null || _e === void 0 ? void 0 : _e.minimize);
32246
- log("docked: ".concat(dockedMode, " static: ").concat(staticMode, " minimized: ").concat((_g = (_f = props.config.header) === null || _f === void 0 ? void 0 : _f.actions) === null || _g === void 0 ? void 0 : _g.minimize));
32247
- var canCancel;
32248
- // To preserve legacy behavior, cancel needs a little more checks
32249
- if (typeof ((_j = (_h = props.config.header) === null || _h === void 0 ? void 0 : _h.actions) === null || _j === void 0 ? void 0 : _j.cancel) === "boolean") {
32250
- canCancel = !dockedMode && !staticMode && ((_l = (_k = props.config.header) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.cancel);
32251
- }
32252
- else {
32253
- canCancel = !dockedMode && !staticMode;
32254
- }
32255
- // For backward compatibility. Note: the action will create the actual "visuals" object" (this is a copy).
32256
- if (!chatState.visuals) {
32257
- chatState.visuals = {};
32177
+ function createDefaultState(state, options) {
32178
+ var _a;
32179
+ if (!state) {
32180
+ state = {};
32258
32181
  }
32259
- // Our state - pull from storage
32260
- var _8 = useState((!canMinimize && !canCancel) ||
32261
- // !!get("visible") ||
32262
- chatState.visuals.visible ||
32263
- (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
32264
- window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _8[0], setVisibleState = _8[1];
32265
- var _9 = useState(false); _9[0]; var setTypingState = _9[1]; // false initially - state kept for potential external observers
32266
- // Ref to track typing state for use in timeout callbacks
32267
- var typingRef = useRef(false);
32268
- // Timeout ref for debouncing "stop typing" events
32269
- var stopTypingTimeoutRef = useRef(null);
32270
- var chatServer = useContext(ChatServerContext);
32271
- var patternsConfig = (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.autoOpenOnPattern) === null || _q === void 0 ? void 0 : _q.patterns;
32272
- var currentUrl = window.location.href;
32273
- var patternExist = patternsConfig && patternsConfig.length > 0;
32274
- var patternMatches = patternsConfig === null || patternsConfig === void 0 ? void 0 : patternsConfig.some(function (pattern) { return currentUrl.includes(pattern); });
32275
- var configWidth = (_s = (_r = props.config) === null || _r === void 0 ? void 0 : _r.autoOpenOnPattern) === null || _s === void 0 ? void 0 : _s.minimumWidth;
32276
- // eslint-disable-next-line no-restricted-globals
32277
- var currentWidth = screen.width;
32278
- var setVisible = useCallback(function (newVisible) {
32279
- if (staticMode) {
32280
- return;
32281
- }
32282
- log("setVisible: ".concat(newVisible));
32283
- setVisibleState(newVisible);
32284
- innerDispatch(setVisualStatus({
32285
- visible: newVisible,
32286
- }));
32287
- }, [innerDispatch, staticMode]);
32288
- useEffect(function () {
32289
- var _a, _b;
32290
- var handleKeyDown = function (event) {
32291
- var body = document.getElementsByTagName("body")[0];
32292
- body.tabIndex = -1;
32293
- if (event.key === "Escape") {
32294
- body.focus();
32295
- }
32296
- };
32297
- document.addEventListener("keydown", handleKeyDown);
32298
- if (checkSessionExpiration((chatState === null || chatState === void 0 ? void 0 : chatState.sessionExpiration) || ((_a = props.config) === null || _a === void 0 ? void 0 : _a.sessionExpiration), (_b = chatState === null || chatState === void 0 ? void 0 : chatState.chats[chatState.chats.length - 1]) === null || _b === void 0 ? void 0 : _b.timestamp, chatState === null || chatState === void 0 ? void 0 : chatState.lastTimestamp)) {
32299
- innerDispatch(setSessionId(undefined));
32300
- innerDispatch(reset());
32301
- }
32302
- return function () {
32303
- document.removeEventListener("keydown", handleKeyDown);
32304
- // Cleanup typing timeout on unmount
32305
- if (stopTypingTimeoutRef.current) {
32306
- clearTimeout(stopTypingTimeoutRef.current);
32307
- }
32308
- };
32309
- // eslint-disable-next-line react-hooks/exhaustive-deps
32310
- }, []);
32311
- var _10 = useState(!document.hidden), isTabVisible = _10[0], setIsTabVisible = _10[1];
32312
- useEffect(function () {
32313
- var handleVisibilityChange = function () {
32314
- setIsTabVisible(!document.hidden);
32315
- };
32316
- document.addEventListener("visibilitychange", handleVisibilityChange);
32317
- return function () {
32318
- document.removeEventListener("visibilitychange", handleVisibilityChange);
32319
- };
32320
- }, []);
32321
- useEffect(function () {
32322
- // Too early?
32323
- if (!chatServer) {
32324
- log("Tab visibility changed but no chatServer yet");
32325
- return;
32326
- }
32327
- if (!isTabVisible) {
32328
- log("Tab is HIDDEN - calling sleep()");
32329
- chatServer.sleep();
32330
- }
32331
- else {
32332
- log("Tab is VISIBLE - calling wakeup()");
32333
- chatServer.wakeup();
32334
- }
32335
- }, [chatServer, innerDispatch, isTabVisible]);
32336
- // Handle beforeunload to notify router when user is leaving
32337
- useEffect(function () {
32338
- if (!chatServer) {
32339
- return undefined;
32340
- }
32341
- var handleBeforeUnload = function () {
32342
- log("beforeunload - notifying router of user disconnecting");
32343
- if (chatServer.notifyDisconnecting) {
32344
- chatServer.notifyDisconnecting();
32345
- }
32346
- };
32347
- window.addEventListener("beforeunload", handleBeforeUnload);
32348
- return function () {
32349
- window.removeEventListener("beforeunload", handleBeforeUnload);
32350
- };
32351
- }, [chatServer]);
32352
- useEffect(function () {
32353
- // For reopen widget after move on same window
32354
- // if (get("opened")) {
32355
- // setVisible(true);
32356
- // }
32357
- if (chatState.visuals.opened) {
32358
- setVisible(true);
32359
- }
32360
- else {
32361
- if (mode === "normal") {
32362
- setVisible(false);
32363
- }
32364
- }
32365
- if (patternExist && patternMatches) {
32366
- setVisible(true);
32367
- }
32368
- if (currentWidth < +configWidth) {
32369
- setVisible(false);
32370
- }
32371
- }, [
32372
- currentWidth,
32373
- patternExist,
32374
- patternMatches,
32375
- configWidth,
32376
- setVisible,
32377
- chatState.visuals.opened,
32378
- mode,
32379
- ]);
32380
- var stopTyping = useCallback(function () {
32381
- if (!typingRef.current)
32382
- return;
32383
- // Clear the timeout if it exists
32384
- if (stopTypingTimeoutRef.current) {
32385
- clearTimeout(stopTypingTimeoutRef.current);
32386
- stopTypingTimeoutRef.current = null;
32387
- }
32388
- dispatch(sendTyping(false));
32389
- setTypingState(false);
32390
- typingRef.current = false;
32391
- }, [dispatch]);
32392
- var handleOnChange = useCallback(function () {
32393
- // Send "typing" event only on first keystroke
32394
- if (!typingRef.current) {
32395
- dispatch(sendTyping(true));
32396
- setTypingState(true);
32397
- typingRef.current = true;
32398
- }
32399
- // Clear any existing timeout
32400
- if (stopTypingTimeoutRef.current) {
32401
- clearTimeout(stopTypingTimeoutRef.current);
32182
+ state.userId = state.userId ? state.userId : visitorFingerprint();
32183
+ state.visitorId = state.userId;
32184
+ // Determine if debug mode should be enabled
32185
+ var debugMode = false;
32186
+ if (typeof window !== 'undefined') {
32187
+ var localStorageSetting = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem('xaErrorOverlay');
32188
+ if (localStorageSetting === 'enabled') {
32189
+ debugMode = true;
32402
32190
  }
32403
- // Set a new timeout to send "stop typing" after inactivity
32404
- stopTypingTimeoutRef.current = window.setTimeout(function () {
32405
- stopTyping();
32406
- }, TYPING_INDICATOR_DEBOUNCE_MS);
32407
- }, [dispatch, stopTyping]);
32408
- function handleSendMessage(msg) {
32409
- dispatch(sendMessage(msg));
32410
- }
32411
- function handleWriteMessage(msg) {
32412
- innerDispatch(writeMessage(msg.msg, msg.user));
32413
- }
32414
- var handleOpenUrl = useOpenUrlCallback();
32415
- function handleOnSubmit(rawInput) {
32416
- // Don't allow visitor to send msg if not chatting
32417
- if (chatState.accountStatus === "offline" && !chatState.isChatting)
32418
- return;
32419
- // Don't send empty messages
32420
- if (!rawInput)
32421
- return;
32422
- // Immediately stop typing
32423
- stopTyping();
32424
- chatServer.flush();
32425
- dispatch(executeAction(rawInput));
32426
- }
32427
- function handleFileUpload(event) {
32428
- var _a;
32429
- event.preventDefault();
32430
- // Only send the first file dropped on input
32431
- var file = (_a = event === null || event === void 0 ? void 0 : event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files[0];
32432
- if (!file) {
32433
- return;
32191
+ else if (localStorageSetting === 'disabled') {
32192
+ debugMode = false;
32434
32193
  }
32435
- dispatch(sendFile(file));
32436
- }
32437
- function getVisibilityClass() {
32438
- return visible ? "visible" : "";
32439
- }
32440
- function openRestartConfirmationModal() {
32441
- // Interesting, this call the modalRef of the restart modal
32442
- modalRef.current.style.display = "block";
32443
- }
32444
- function closeRestartConfirmationModal() {
32445
- modalRef.current.style.display = "none";
32446
- }
32447
- function handleReset() {
32448
- innerDispatch(reset());
32449
- }
32450
- function handleRestartClick() {
32451
- if (config.header.actions.refreshShowConfirmation) {
32452
- // show the modal
32453
- openRestartConfirmationModal();
32454
- // we will close when
32194
+ else if (options === null || options === void 0 ? void 0 : options.enableErrorOverlay) {
32195
+ // Use config option if no localStorage override
32196
+ debugMode = true;
32455
32197
  }
32456
- else {
32457
- handleReset();
32198
+ else if (typeof globalThis.__DEV__ !== 'undefined' && globalThis.__DEV__) {
32199
+ // For React Native, default to true in development mode
32200
+ debugMode = true;
32458
32201
  }
32459
32202
  }
32460
- function handleRestartModalCloseClick() {
32461
- closeRestartConfirmationModal();
32462
- }
32463
- /** Called when minimize button is clicked */
32464
- function handleMinimizeClick() {
32465
- innerDispatch(setVisualStatus({
32466
- opened: false,
32467
- hasInteracted: true,
32468
- }));
32469
- setVisible(false);
32470
- }
32471
- /** Called when cancel is clicked */
32472
- function handleCancelClick() {
32473
- // First reset to clear all state
32474
- innerDispatch(reset());
32475
- // Then set hasInteracted to prevent CTA from showing after cancel
32476
- innerDispatch(setVisualStatus({
32477
- opened: false,
32478
- hasInteracted: true,
32479
- }));
32480
- setVisible(false);
32481
- }
32482
- function chatButtonOnClick() {
32483
- innerDispatch(setVisualStatus({
32203
+ return __assign({ connection: {
32204
+ connectionStatus: "offline",
32205
+ token: null,
32206
+ greetingRequested: false
32207
+ }, accountStatus: "offline", departments: {}, visitor: DEFAULT_VISITOR, agents: {}, chats: [], lastTimestamp: 0, lastRatingRequestTimestamp: 0, hasRating: false, isChatting: false, queuePosition: 0, failureMsg: null, visitorId: visitorFingerprint(), chips: [], sessionExpiration: "24h", visuals: {}, debugMode: debugMode }, state);
32208
+ }
32209
+ var DEFAULT_STATE = createDefaultState();
32210
+
32211
+ function createStateFromMessages(messages) {
32212
+ var def = createDefaultState();
32213
+ return __assign(__assign({}, def), { connection: __assign(__assign({}, def.connection), { connectionStatus: "online" }), accountStatus: "online", chats: __spreadArray$1([], messages, true), isChatting: true, visuals: {
32214
+ visible: true,
32484
32215
  opened: true,
32485
- hasInteracted: true,
32486
- }));
32487
- setVisible(true);
32488
- setTimeout(function () {
32489
- document.getElementById("chatWidgetInput").focus();
32490
- }, 100);
32491
- }
32492
- function handleCtaDismiss() {
32493
- innerDispatch(setVisualStatus({
32494
- hasInteracted: true,
32495
- }));
32496
- }
32497
- function handleWsButtonPress(buttonId) {
32498
- var _a, _b;
32499
- log("WS Button pressed: ".concat(buttonId));
32500
- // If the button has spinning behavior, update state to show spinner
32501
- if (((_a = chatState.wsButton) === null || _a === void 0 ? void 0 : _a.pressBehavior) === "spinning") {
32502
- // Update the button state to show it's pressed
32503
- innerDispatch({
32504
- type: "ws_button_display",
32505
- detail: __assign(__assign({}, chatState.wsButton), { isPressed: true, timestamp: +new Date() })
32506
- });
32507
- }
32508
- // Send the button pressed event to the server
32509
- if (chatServer && chatServer.sendButtonPressed) {
32510
- chatServer.sendButtonPressed(buttonId).catch(function (error) {
32511
- err("Failed to send button press: ".concat(error));
32512
- // Reset button state on error
32513
- if (chatState.wsButton) {
32514
- innerDispatch({
32515
- type: "ws_button_display",
32516
- detail: __assign(__assign({}, chatState.wsButton), { isPressed: false, timestamp: +new Date() })
32517
- });
32518
- }
32519
- });
32520
- }
32521
- // If the button has disabled behavior, animate out then dismiss
32522
- if (((_b = chatState.wsButton) === null || _b === void 0 ? void 0 : _b.pressBehavior) === "disabled") {
32523
- // First set dismissing state to trigger animation
32524
- innerDispatch({
32525
- type: "ws_button_display",
32526
- detail: __assign(__assign({}, chatState.wsButton), { isDismissing: true, timestamp: +new Date() })
32527
- });
32528
- // Then actually dismiss after animation completes
32529
- setTimeout(function () {
32530
- innerDispatch({
32531
- type: "ws_button_dismiss",
32532
- detail: {
32533
- id: buttonId,
32534
- timestamp: +new Date()
32535
- }
32536
- });
32537
- }, WS_BUTTON_DISMISS_ANIMATION_DURATION);
32538
- }
32539
- }
32540
- var isOffline = chatState.accountStatus === "offline" && !chatState.isChatting;
32541
- var messages = chatState && chatState.chats;
32542
- log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
32543
- var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
32544
- useGreeting(!isOffline && !props.preChatFormEnabled && visible);
32545
- var connectionStatus = chatState.connection.connectionStatus;
32546
- useEffect(function () {
32547
- if (onConnectionStatusChange) {
32548
- onConnectionStatusChange(connectionStatus);
32549
- }
32550
- }, [connectionStatus, onConnectionStatusChange]);
32551
- useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
32552
- // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
32553
- var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
32554
- (config === null || config === void 0 ? void 0 : config.agent) || {
32555
- nick: "agent:robot",
32556
- avatarPath: config.avatarUrl,
32557
- display_name: "Agent",
32558
- };
32559
- return (jsxs(Fragment, { children: [jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()), children: [jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, onSubmit: handleOnSubmit }), jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_w = (_v = props.config) === null || _v === void 0 ? void 0 : _v.typingStatus) === null || _w === void 0 ? void 0 : _w.textTypingStatusEnabled, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && jsx(ServerOffline, {}), chatState.wsButton && visible && (jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_x = config === null || config === void 0 ? void 0 : config.input) === null || _x === void 0 ? void 0 : _x.placeholder, sendButtonIcon: (_z = (_y = config === null || config === void 0 ? void 0 : config.footer) === null || _y === void 0 ? void 0 : _y.sendButton) === null || _z === void 0 ? void 0 : _z.icon, sendButtonIconHover: (_1 = (_0 = config === null || config === void 0 ? void 0 : config.footer) === null || _0 === void 0 ? void 0 : _0.sendButton) === null || _1 === void 0 ? void 0 : _1.iconHover, sendButtonIconDisabled: (_3 = (_2 = config === null || config === void 0 ? void 0 : config.footer) === null || _2 === void 0 ? void 0 : _2.sendButton) === null || _3 === void 0 ? void 0 : _3.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: props.config.menu, footerConfig: (_4 = props.config) === null || _4 === void 0 ? void 0 : _4.footer, inputConfig: (_5 = props.config) === null || _5 === void 0 ? void 0 : _5.input, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }), jsx(ChatButton, { addClass: getVisibilityClass(), onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_6 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _6 === void 0 ? void 0 : _6.imageUrl, visible: visible, hasInteracted: (_7 = chatState.visuals) === null || _7 === void 0 ? void 0 : _7.hasInteracted, onCtaDismiss: handleCtaDismiss }), jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
32560
- };
32216
+ } });
32217
+ }
32561
32218
 
32562
32219
  // src/utils/formatProdErrorMessage.ts
32563
32220
  function formatProdErrorMessage$1(code) {
@@ -33714,6 +33371,576 @@ function formatProdErrorMessage(code) {
33714
33371
  return `Minified Redux Toolkit error #${code}; visit https://redux-toolkit.js.org/Errors?code=${code} for the full message or use the non-minified dev environment for full errors. `;
33715
33372
  }
33716
33373
 
33374
+ // Function to create a static reducer
33375
+ function createStaticReducer(state) {
33376
+ return function (oldState, action) {
33377
+ if (oldState === void 0) { oldState = state; }
33378
+ log("static reducer", oldState, action);
33379
+ return oldState;
33380
+ };
33381
+ }
33382
+ // Function to create the store using @reduxjs/toolkit's configureStore
33383
+ function createStaticStore(state) {
33384
+ var reducer = createStaticReducer(state);
33385
+ var store = configureStore({
33386
+ reducer: reducer,
33387
+ middleware: function (getDefaultMiddleware) {
33388
+ return getDefaultMiddleware({
33389
+ thunk: true, // Thunk is included by default, this is just for explicitness
33390
+ });
33391
+ },
33392
+ devTools: "production" !== 'production', // Redux DevTools in non-production environments
33393
+ });
33394
+ return store;
33395
+ }
33396
+
33397
+ var StaticChatWidgetContainer = function (props) {
33398
+ var state = props.state;
33399
+ var store = useMemo(function () {
33400
+ return createStaticStore(state);
33401
+ }, [state]);
33402
+ var config = useMemo(function () {
33403
+ return __assign(__assign({}, props.config), { connection: {
33404
+ serverUrl: "",
33405
+ type: "local",
33406
+ } });
33407
+ }, [props.config]);
33408
+ return (jsx(Provider, { store: store, children: jsx(ChatWidgetUI, __assign({}, props, { config: config, disableAutoScroll: props.disableAutoScroll, preview: props.preview })) }));
33409
+ };
33410
+
33411
+ var StaticMessagesChatWidgetContainer = function (props) {
33412
+ var messages = props.messages;
33413
+ var state = useMemo(function () {
33414
+ return createStateFromMessages(messages);
33415
+ }, [messages]);
33416
+ return jsx(StaticChatWidgetContainer, { state: state, mode: props.mode, config: props.config, disableAutoScroll: props.disableAutoScroll, preview: props.preview });
33417
+ };
33418
+
33419
+ /**
33420
+ * Debounce delay for typing indicator in milliseconds.
33421
+ * After this delay of inactivity, the "stop typing" event will be sent.
33422
+ */
33423
+ var TYPING_INDICATOR_DEBOUNCE_MS = 2000;
33424
+ /**
33425
+ * Check if we're in development mode. Uses typeof to prevent TypeScript
33426
+ * errors after Rollup replaces "production" with a literal string.
33427
+ */
33428
+ typeof process !== "undefined" &&
33429
+ typeof process.env !== "undefined" &&
33430
+ "production" !== "production";
33431
+ /**
33432
+ * Converts preview messages to ChatDetail format for rendering.
33433
+ * This allows preview mode to use the existing message rendering infrastructure.
33434
+ *
33435
+ * @param messages - Array of preview messages to convert
33436
+ * @returns Array of ChatDetail objects suitable for MessageList rendering
33437
+ */
33438
+ function convertPreviewMessagesToChatDetails(messages) {
33439
+ var timestamp = Date.now();
33440
+ return messages.map(function (msg, index) {
33441
+ var _a;
33442
+ var chatDetail = {
33443
+ type: "chat.msg",
33444
+ user: {
33445
+ nick: "agent:robot",
33446
+ display_name: "Assistant",
33447
+ },
33448
+ timestamp: timestamp + index,
33449
+ msg: {
33450
+ text: msg.displayText,
33451
+ html: msg.html,
33452
+ // Use "#" as fallback for suggestions without URLs to prevent navigation
33453
+ options: (_a = msg.suggestions) === null || _a === void 0 ? void 0 : _a.map(function (s) { return ({
33454
+ label: s.title,
33455
+ actionUrl: s.url || "#"
33456
+ }); }),
33457
+ list: msg.list ? {
33458
+ type: msg.list.type,
33459
+ title: msg.list.title,
33460
+ // description maps to subTitle in internal representation
33461
+ items: msg.list.items.map(function (item) { return ({
33462
+ title: item.title,
33463
+ subTitle: item.description,
33464
+ url: item.url,
33465
+ imageUrl: item.imageUrl,
33466
+ }); })
33467
+ } : undefined
33468
+ }
33469
+ };
33470
+ return chatDetail;
33471
+ });
33472
+ }
33473
+ /**
33474
+ * Preview mode component - renders a static chat widget without server connection.
33475
+ * Separated to avoid violating React hooks rules in the main wrapper.
33476
+ */
33477
+ var ChatWidgetPreview = function (props) {
33478
+ var _a;
33479
+ var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "preview";
33480
+ // Memoize preview message conversion to avoid recalculating on every render
33481
+ var previewChatDetails = useMemo(function () {
33482
+ return convertPreviewMessagesToChatDetails(props.previewMessages || []);
33483
+ }, [props.previewMessages]);
33484
+ return (jsx(StaticMessagesChatWidgetContainer, { messages: previewChatDetails, mode: mode, config: props.config, disableAutoScroll: props.disableAutoScroll, preview: true }));
33485
+ };
33486
+ /**
33487
+ * Main wrapper component that handles config loading and server connection.
33488
+ */
33489
+ var ChatWidgetConnected = function (props) {
33490
+ var _a;
33491
+ var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
33492
+ var _b = useState(props.config), rawConfig = _b[0], setRawConfig = _b[1];
33493
+ var _c = useState(!!props.getConfig), configLoading = _c[0], setConfigLoading = _c[1];
33494
+ var _d = useState(), configError = _d[0], setConfigError = _d[1];
33495
+ // Load config from callback if provided
33496
+ useEffect(function () {
33497
+ var cancelled = false;
33498
+ if (props.getConfig) {
33499
+ setConfigLoading(true);
33500
+ setConfigError(undefined);
33501
+ props
33502
+ .getConfig()
33503
+ .then(function (config) {
33504
+ if (!cancelled) {
33505
+ setRawConfig(config);
33506
+ setConfigLoading(false);
33507
+ log("[ChatWidget] Config loaded from getConfig callback");
33508
+ }
33509
+ })
33510
+ .catch(function (error) {
33511
+ if (!cancelled) {
33512
+ setConfigError(error);
33513
+ setConfigLoading(false);
33514
+ err("[ChatWidget] Failed to load config: ".concat(error.message));
33515
+ }
33516
+ });
33517
+ }
33518
+ else if (props.config) {
33519
+ // If no callback, use the config prop directly
33520
+ setRawConfig(props.config);
33521
+ setConfigLoading(false);
33522
+ }
33523
+ return function () {
33524
+ cancelled = true;
33525
+ };
33526
+ // Only depend on getConfig and config - not the entire props object to avoid infinite loops
33527
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33528
+ }, [props.getConfig, props.config]);
33529
+ var connection = useConnectionInfo(rawConfig);
33530
+ var config = useMemo(function () {
33531
+ var _a;
33532
+ return (__assign(__assign({}, rawConfig), { connection: connection, assetUrl: (_a = connection === null || connection === void 0 ? void 0 : connection.serverUrl) !== null && _a !== void 0 ? _a : defaultServerUrl, env: rawConfig }));
33533
+ }, [connection, rawConfig]);
33534
+ var token = useSelector(function (state) { return state.connection.token; });
33535
+ var options = useMemo(function () {
33536
+ var configurableMessages = getConfigurableMessages();
33537
+ if ((rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages) &&
33538
+ Array.isArray(rawConfig.configurableMessages.items) &&
33539
+ rawConfig.configurableMessages.items.length > 0) {
33540
+ configurableMessages = rawConfig.configurableMessages;
33541
+ }
33542
+ return {
33543
+ token: token,
33544
+ bot: {
33545
+ nick: "Bot",
33546
+ displayName: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
33547
+ avatarPath: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
33548
+ },
33549
+ configurableMessages: configurableMessages,
33550
+ hooks: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
33551
+ env: rawConfig,
33552
+ };
33553
+ }, [
33554
+ token,
33555
+ rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
33556
+ rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
33557
+ rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages,
33558
+ rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
33559
+ ]);
33560
+ // Only create chat server when config is ready (not loading and no error)
33561
+ var chatServer = useChatServer(configLoading || configError ? null : connection, configLoading || configError ? null : options);
33562
+ // Determine mode class for loading/error states (mode already declared above)
33563
+ var modeClass = "widget-container--".concat(mode);
33564
+ // Show loading state while config is being fetched
33565
+ if (configLoading) {
33566
+ return (jsx("div", { className: "widget-container widget-container--loading ".concat(modeClass), children: jsx("div", { className: "xa-spinner-container visible", children: jsx("div", { className: "xa-spinner" }) }) }));
33567
+ }
33568
+ // Show error state if config failed to load
33569
+ if (configError) {
33570
+ return (jsx("div", { className: "widget-container widget-container--error ".concat(modeClass), children: jsxs("div", { className: "widget-error-message", children: ["Failed to load chat configuration: ", configError.message] }) }));
33571
+ }
33572
+ return (jsx(ChatConfigContext.Provider, { value: config, children: jsx(ChatServerContext.Provider, { value: chatServer, children: jsx(ChatWidgetUI, __assign({}, props, { config: rawConfig })) }) }));
33573
+ };
33574
+ /**
33575
+ * Inner ChatWidget component that renders the actual UI.
33576
+ * Exported for use by StaticChatWidgetContainer to avoid infinite loops.
33577
+ */
33578
+ var ChatWidgetUI = function (props) {
33579
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
33580
+ var innerDispatch = useChatDispatch();
33581
+ var dispatch = useChatServerDispatch();
33582
+ // From Redux
33583
+ var chatState = useSelector(function (state) { return state; });
33584
+ // Refresh modalReference
33585
+ var modalRef = useRef({});
33586
+ var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
33587
+ var dockedMode = mode === "docked";
33588
+ var staticMode = mode === "static";
33589
+ // Preview mode can be enabled via `preview` prop (recommended) or legacy `mode="preview"`
33590
+ var previewMode = props.preview === true || mode === "preview";
33591
+ var modeClass = "widget-container--".concat(mode);
33592
+ var canRefresh = (_c = (_b = props.config.header) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.refresh;
33593
+ // can't minimize in docked mode, static mode, or preview mode.
33594
+ var canMinimize = !dockedMode && !staticMode && !previewMode && ((_e = (_d = props.config.header) === null || _d === void 0 ? void 0 : _d.actions) === null || _e === void 0 ? void 0 : _e.minimize);
33595
+ log("docked: ".concat(dockedMode, " static: ").concat(staticMode, " preview: ").concat(previewMode, " minimized: ").concat((_g = (_f = props.config.header) === null || _f === void 0 ? void 0 : _f.actions) === null || _g === void 0 ? void 0 : _g.minimize));
33596
+ var canCancel;
33597
+ // To preserve legacy behavior, cancel needs a little more checks
33598
+ if (typeof ((_j = (_h = props.config.header) === null || _h === void 0 ? void 0 : _h.actions) === null || _j === void 0 ? void 0 : _j.cancel) === "boolean") {
33599
+ canCancel = !dockedMode && !staticMode && !previewMode && ((_l = (_k = props.config.header) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.cancel);
33600
+ }
33601
+ else {
33602
+ canCancel = !dockedMode && !staticMode && !previewMode;
33603
+ }
33604
+ // For backward compatibility. Note: the action will create the actual "visuals" object" (this is a copy).
33605
+ if (!chatState.visuals) {
33606
+ chatState.visuals = {};
33607
+ }
33608
+ // Our state - pull from storage
33609
+ var _9 = useState((!canMinimize && !canCancel) ||
33610
+ // !!get("visible") ||
33611
+ chatState.visuals.visible ||
33612
+ (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
33613
+ window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _9[0], setVisibleState = _9[1];
33614
+ var _10 = useState(false); _10[0]; var setTypingState = _10[1]; // false initially - state kept for potential external observers
33615
+ // Ref to track typing state for use in timeout callbacks
33616
+ var typingRef = useRef(false);
33617
+ // Timeout ref for debouncing "stop typing" events
33618
+ var stopTypingTimeoutRef = useRef(null);
33619
+ var chatServer = useContext(ChatServerContext);
33620
+ var patternsConfig = (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.autoOpenOnPattern) === null || _q === void 0 ? void 0 : _q.patterns;
33621
+ var currentUrl = window.location.href;
33622
+ var patternExist = patternsConfig && patternsConfig.length > 0;
33623
+ var patternMatches = patternsConfig === null || patternsConfig === void 0 ? void 0 : patternsConfig.some(function (pattern) { return currentUrl.includes(pattern); });
33624
+ var configWidth = (_s = (_r = props.config) === null || _r === void 0 ? void 0 : _r.autoOpenOnPattern) === null || _s === void 0 ? void 0 : _s.minimumWidth;
33625
+ // eslint-disable-next-line no-restricted-globals
33626
+ var currentWidth = screen.width;
33627
+ var setVisible = useCallback(function (newVisible) {
33628
+ if (staticMode || previewMode) {
33629
+ return;
33630
+ }
33631
+ log("setVisible: ".concat(newVisible));
33632
+ setVisibleState(newVisible);
33633
+ innerDispatch(setVisualStatus({
33634
+ visible: newVisible,
33635
+ }));
33636
+ }, [innerDispatch, staticMode, previewMode]);
33637
+ useEffect(function () {
33638
+ var _a, _b;
33639
+ var handleKeyDown = function (event) {
33640
+ var body = document.getElementsByTagName("body")[0];
33641
+ body.tabIndex = -1;
33642
+ if (event.key === "Escape") {
33643
+ body.focus();
33644
+ }
33645
+ };
33646
+ document.addEventListener("keydown", handleKeyDown);
33647
+ if (checkSessionExpiration((chatState === null || chatState === void 0 ? void 0 : chatState.sessionExpiration) || ((_a = props.config) === null || _a === void 0 ? void 0 : _a.sessionExpiration), (_b = chatState === null || chatState === void 0 ? void 0 : chatState.chats[chatState.chats.length - 1]) === null || _b === void 0 ? void 0 : _b.timestamp, chatState === null || chatState === void 0 ? void 0 : chatState.lastTimestamp)) {
33648
+ innerDispatch(setSessionId(undefined));
33649
+ innerDispatch(reset());
33650
+ }
33651
+ return function () {
33652
+ document.removeEventListener("keydown", handleKeyDown);
33653
+ // Cleanup typing timeout on unmount
33654
+ if (stopTypingTimeoutRef.current) {
33655
+ clearTimeout(stopTypingTimeoutRef.current);
33656
+ }
33657
+ };
33658
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33659
+ }, []);
33660
+ var _11 = useState(!document.hidden), isTabVisible = _11[0], setIsTabVisible = _11[1];
33661
+ useEffect(function () {
33662
+ var handleVisibilityChange = function () {
33663
+ setIsTabVisible(!document.hidden);
33664
+ };
33665
+ document.addEventListener("visibilitychange", handleVisibilityChange);
33666
+ return function () {
33667
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
33668
+ };
33669
+ }, []);
33670
+ useEffect(function () {
33671
+ // Too early?
33672
+ if (!chatServer) {
33673
+ log("Tab visibility changed but no chatServer yet");
33674
+ return;
33675
+ }
33676
+ if (!isTabVisible) {
33677
+ log("Tab is HIDDEN - calling sleep()");
33678
+ chatServer.sleep();
33679
+ }
33680
+ else {
33681
+ log("Tab is VISIBLE - calling wakeup()");
33682
+ chatServer.wakeup();
33683
+ }
33684
+ }, [chatServer, innerDispatch, isTabVisible]);
33685
+ // Handle beforeunload to notify router when user is leaving
33686
+ useEffect(function () {
33687
+ if (!chatServer) {
33688
+ return undefined;
33689
+ }
33690
+ var handleBeforeUnload = function () {
33691
+ log("beforeunload - notifying router of user disconnecting");
33692
+ if (chatServer.notifyDisconnecting) {
33693
+ chatServer.notifyDisconnecting();
33694
+ }
33695
+ };
33696
+ window.addEventListener("beforeunload", handleBeforeUnload);
33697
+ return function () {
33698
+ window.removeEventListener("beforeunload", handleBeforeUnload);
33699
+ };
33700
+ }, [chatServer]);
33701
+ useEffect(function () {
33702
+ // For reopen widget after move on same window
33703
+ // if (get("opened")) {
33704
+ // setVisible(true);
33705
+ // }
33706
+ if (chatState.visuals.opened) {
33707
+ setVisible(true);
33708
+ }
33709
+ else {
33710
+ if (mode === "normal") {
33711
+ setVisible(false);
33712
+ }
33713
+ }
33714
+ if (patternExist && patternMatches) {
33715
+ setVisible(true);
33716
+ }
33717
+ if (currentWidth < +configWidth) {
33718
+ setVisible(false);
33719
+ }
33720
+ }, [
33721
+ currentWidth,
33722
+ patternExist,
33723
+ patternMatches,
33724
+ configWidth,
33725
+ setVisible,
33726
+ chatState.visuals.opened,
33727
+ mode,
33728
+ ]);
33729
+ var stopTyping = useCallback(function () {
33730
+ if (!typingRef.current)
33731
+ return;
33732
+ // Clear the timeout if it exists
33733
+ if (stopTypingTimeoutRef.current) {
33734
+ clearTimeout(stopTypingTimeoutRef.current);
33735
+ stopTypingTimeoutRef.current = null;
33736
+ }
33737
+ dispatch(sendTyping(false));
33738
+ setTypingState(false);
33739
+ typingRef.current = false;
33740
+ }, [dispatch]);
33741
+ var handleOnChange = useCallback(function () {
33742
+ // Send "typing" event only on first keystroke
33743
+ if (!typingRef.current) {
33744
+ dispatch(sendTyping(true));
33745
+ setTypingState(true);
33746
+ typingRef.current = true;
33747
+ }
33748
+ // Clear any existing timeout
33749
+ if (stopTypingTimeoutRef.current) {
33750
+ clearTimeout(stopTypingTimeoutRef.current);
33751
+ }
33752
+ // Set a new timeout to send "stop typing" after inactivity
33753
+ stopTypingTimeoutRef.current = window.setTimeout(function () {
33754
+ stopTyping();
33755
+ }, TYPING_INDICATOR_DEBOUNCE_MS);
33756
+ }, [dispatch, stopTyping]);
33757
+ function handleSendMessage(msg) {
33758
+ // Don't allow sending messages in preview mode
33759
+ if (previewMode) {
33760
+ return;
33761
+ }
33762
+ dispatch(sendMessage(msg));
33763
+ }
33764
+ function handleWriteMessage(msg) {
33765
+ // Don't allow writing messages in preview mode
33766
+ if (previewMode) {
33767
+ return;
33768
+ }
33769
+ innerDispatch(writeMessage(msg.msg, msg.user));
33770
+ }
33771
+ var handleOpenUrl = useOpenUrlCallback();
33772
+ function handleOnSubmit(rawInput) {
33773
+ // Don't allow any actions in preview mode
33774
+ if (previewMode) {
33775
+ return;
33776
+ }
33777
+ // Don't allow visitor to send msg if not chatting
33778
+ if (chatState.accountStatus === "offline" && !chatState.isChatting)
33779
+ return;
33780
+ // Don't send empty messages
33781
+ if (!rawInput)
33782
+ return;
33783
+ // Immediately stop typing
33784
+ stopTyping();
33785
+ chatServer.flush();
33786
+ dispatch(executeAction(rawInput));
33787
+ }
33788
+ function handleFileUpload(event) {
33789
+ var _a;
33790
+ event.preventDefault();
33791
+ // Don't allow file uploads in preview mode
33792
+ if (previewMode) {
33793
+ return;
33794
+ }
33795
+ // Only send the first file dropped on input
33796
+ var file = (_a = event === null || event === void 0 ? void 0 : event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files[0];
33797
+ if (!file) {
33798
+ return;
33799
+ }
33800
+ dispatch(sendFile(file));
33801
+ }
33802
+ function getVisibilityClass() {
33803
+ return visible ? "visible" : "";
33804
+ }
33805
+ function openRestartConfirmationModal() {
33806
+ // Interesting, this call the modalRef of the restart modal
33807
+ modalRef.current.style.display = "block";
33808
+ }
33809
+ function closeRestartConfirmationModal() {
33810
+ modalRef.current.style.display = "none";
33811
+ }
33812
+ function handleReset() {
33813
+ innerDispatch(reset());
33814
+ }
33815
+ function handleRestartClick() {
33816
+ if (config.header.actions.refreshShowConfirmation) {
33817
+ // show the modal
33818
+ openRestartConfirmationModal();
33819
+ // we will close when
33820
+ }
33821
+ else {
33822
+ handleReset();
33823
+ }
33824
+ }
33825
+ function handleRestartModalCloseClick() {
33826
+ closeRestartConfirmationModal();
33827
+ }
33828
+ /** Called when minimize button is clicked */
33829
+ function handleMinimizeClick() {
33830
+ innerDispatch(setVisualStatus({
33831
+ opened: false,
33832
+ hasInteracted: true,
33833
+ }));
33834
+ setVisible(false);
33835
+ }
33836
+ /** Called when cancel is clicked */
33837
+ function handleCancelClick() {
33838
+ // First reset to clear all state
33839
+ innerDispatch(reset());
33840
+ // Then set hasInteracted to prevent CTA from showing after cancel
33841
+ innerDispatch(setVisualStatus({
33842
+ opened: false,
33843
+ hasInteracted: true,
33844
+ }));
33845
+ setVisible(false);
33846
+ }
33847
+ function chatButtonOnClick() {
33848
+ innerDispatch(setVisualStatus({
33849
+ opened: true,
33850
+ hasInteracted: true,
33851
+ }));
33852
+ setVisible(true);
33853
+ setTimeout(function () {
33854
+ document.getElementById("chatWidgetInput").focus();
33855
+ }, 100);
33856
+ }
33857
+ function handleCtaDismiss() {
33858
+ innerDispatch(setVisualStatus({
33859
+ hasInteracted: true,
33860
+ }));
33861
+ }
33862
+ function handleWsButtonPress(buttonId) {
33863
+ var _a, _b;
33864
+ log("WS Button pressed: ".concat(buttonId));
33865
+ // If the button has spinning behavior, update state to show spinner
33866
+ if (((_a = chatState.wsButton) === null || _a === void 0 ? void 0 : _a.pressBehavior) === "spinning") {
33867
+ // Update the button state to show it's pressed
33868
+ innerDispatch({
33869
+ type: "ws_button_display",
33870
+ detail: __assign(__assign({}, chatState.wsButton), { isPressed: true, timestamp: +new Date() })
33871
+ });
33872
+ }
33873
+ // Send the button pressed event to the server
33874
+ if (chatServer && chatServer.sendButtonPressed) {
33875
+ chatServer.sendButtonPressed(buttonId).catch(function (error) {
33876
+ err("Failed to send button press: ".concat(error));
33877
+ // Reset button state on error
33878
+ if (chatState.wsButton) {
33879
+ innerDispatch({
33880
+ type: "ws_button_display",
33881
+ detail: __assign(__assign({}, chatState.wsButton), { isPressed: false, timestamp: +new Date() })
33882
+ });
33883
+ }
33884
+ });
33885
+ }
33886
+ // If the button has disabled behavior, animate out then dismiss
33887
+ if (((_b = chatState.wsButton) === null || _b === void 0 ? void 0 : _b.pressBehavior) === "disabled") {
33888
+ // First set dismissing state to trigger animation
33889
+ innerDispatch({
33890
+ type: "ws_button_display",
33891
+ detail: __assign(__assign({}, chatState.wsButton), { isDismissing: true, timestamp: +new Date() })
33892
+ });
33893
+ // Then actually dismiss after animation completes
33894
+ setTimeout(function () {
33895
+ innerDispatch({
33896
+ type: "ws_button_dismiss",
33897
+ detail: {
33898
+ id: buttonId,
33899
+ timestamp: +new Date()
33900
+ }
33901
+ });
33902
+ }, WS_BUTTON_DISMISS_ANIMATION_DURATION);
33903
+ }
33904
+ }
33905
+ var isOffline = chatState.accountStatus === "offline" && !chatState.isChatting;
33906
+ var messages = chatState && chatState.chats;
33907
+ log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
33908
+ var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
33909
+ // Disable greeting in preview mode
33910
+ useGreeting(!isOffline && !props.preChatFormEnabled && visible && !previewMode);
33911
+ var connectionStatus = chatState.connection.connectionStatus;
33912
+ useEffect(function () {
33913
+ if (onConnectionStatusChange) {
33914
+ onConnectionStatusChange(connectionStatus);
33915
+ }
33916
+ }, [connectionStatus, onConnectionStatusChange]);
33917
+ useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
33918
+ // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
33919
+ var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
33920
+ (config === null || config === void 0 ? void 0 : config.agent) || {
33921
+ nick: "agent:robot",
33922
+ avatarPath: config.avatarUrl,
33923
+ display_name: "Agent",
33924
+ };
33925
+ return (jsxs(Fragment, { children: [jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()), children: [jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, onSubmit: handleOnSubmit }), jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_w = (_v = props.config) === null || _v === void 0 ? void 0 : _v.typingStatus) === null || _w === void 0 ? void 0 : _w.textTypingStatusEnabled, disableAutoScroll: props.disableAutoScroll, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && jsx(ServerOffline, {}), chatState.wsButton && visible && (jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_x = config === null || config === void 0 ? void 0 : config.input) === null || _x === void 0 ? void 0 : _x.placeholder, sendButtonIcon: (_z = (_y = config === null || config === void 0 ? void 0 : config.footer) === null || _y === void 0 ? void 0 : _y.sendButton) === null || _z === void 0 ? void 0 : _z.icon, sendButtonIconHover: (_1 = (_0 = config === null || config === void 0 ? void 0 : config.footer) === null || _0 === void 0 ? void 0 : _0.sendButton) === null || _1 === void 0 ? void 0 : _1.iconHover, sendButtonIconDisabled: (_3 = (_2 = config === null || config === void 0 ? void 0 : config.footer) === null || _2 === void 0 ? void 0 : _2.sendButton) === null || _3 === void 0 ? void 0 : _3.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_4 = props.config) === null || _4 === void 0 ? void 0 : _4.menu, footerConfig: (_5 = props.config) === null || _5 === void 0 ? void 0 : _5.footer, inputConfig: (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }), jsx(ChatButton, { addClass: getVisibilityClass(), onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_7 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _7 === void 0 ? void 0 : _7.imageUrl, visible: visible, hasInteracted: (_8 = chatState.visuals) === null || _8 === void 0 ? void 0 : _8.hasInteracted, onCtaDismiss: handleCtaDismiss }), jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
33926
+ };
33927
+ /**
33928
+ * Top-level wrapper that dispatches between preview mode and connected mode.
33929
+ * This separation ensures React hooks rules are not violated.
33930
+ */
33931
+ var ChatWidgetWrapper = function (props) {
33932
+ var _a;
33933
+ var rawMode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
33934
+ // Support both `preview` prop and legacy `mode="preview"`
33935
+ var isPreviewMode = props.preview === true || rawMode === "preview";
33936
+ // For visual layout, use "static" when mode is "preview" (legacy), otherwise use the actual mode
33937
+ var visualMode = rawMode === "preview" ? "static" : rawMode;
33938
+ if (isPreviewMode) {
33939
+ return jsx(ChatWidgetPreview, __assign({}, props, { mode: visualMode }));
33940
+ }
33941
+ return jsx(ChatWidgetConnected, __assign({}, props));
33942
+ };
33943
+
33717
33944
  function tryParseJson(str) {
33718
33945
  try {
33719
33946
  if (str) {
@@ -33743,55 +33970,6 @@ var BrowserStateStorage = /** @class */ (function () {
33743
33970
  return BrowserStateStorage;
33744
33971
  }());
33745
33972
 
33746
- /**
33747
- * Id the visitor (cookies, browser fingerprint, etc)
33748
- *
33749
- * @export
33750
- * @returns {string}
33751
- */
33752
- function visitorFingerprint() {
33753
- return uuid_1();
33754
- }
33755
-
33756
- var DEFAULT_VISITOR = {
33757
- displayName: "Visitor",
33758
- nick: "visitor:",
33759
- typing: false
33760
- };
33761
- function createDefaultState(state, options) {
33762
- var _a;
33763
- if (!state) {
33764
- state = {};
33765
- }
33766
- state.userId = state.userId ? state.userId : visitorFingerprint();
33767
- state.visitorId = state.userId;
33768
- // Determine if debug mode should be enabled
33769
- var debugMode = false;
33770
- if (typeof window !== 'undefined') {
33771
- var localStorageSetting = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem('xaErrorOverlay');
33772
- if (localStorageSetting === 'enabled') {
33773
- debugMode = true;
33774
- }
33775
- else if (localStorageSetting === 'disabled') {
33776
- debugMode = false;
33777
- }
33778
- else if (options === null || options === void 0 ? void 0 : options.enableErrorOverlay) {
33779
- // Use config option if no localStorage override
33780
- debugMode = true;
33781
- }
33782
- else if (typeof globalThis.__DEV__ !== 'undefined' && globalThis.__DEV__) {
33783
- // For React Native, default to true in development mode
33784
- debugMode = true;
33785
- }
33786
- }
33787
- return __assign({ connection: {
33788
- connectionStatus: "offline",
33789
- token: null,
33790
- greetingRequested: false
33791
- }, accountStatus: "offline", departments: {}, visitor: DEFAULT_VISITOR, agents: {}, chats: [], lastTimestamp: 0, lastRatingRequestTimestamp: 0, hasRating: false, isChatting: false, queuePosition: 0, failureMsg: null, visitorId: visitorFingerprint(), chips: [], sessionExpiration: "24h", visuals: {}, debugMode: debugMode }, state);
33792
- }
33793
- var DEFAULT_STATE = createDefaultState();
33794
-
33795
33973
  /**
33796
33974
  * Polyfill for Browser Local Storage
33797
33975
  *
@@ -34207,7 +34385,18 @@ function generateKey(connection, sessionId) {
34207
34385
  }
34208
34386
  }
34209
34387
 
34210
- var ChatWidgetContainer = function (props) {
34388
+ /**
34389
+ * Preview mode container - skips server config and store creation.
34390
+ */
34391
+ var ChatWidgetPreviewContainer = function (props) {
34392
+ var messageMiddleware = useStandardMiddleware();
34393
+ var widgetProps = __assign(__assign({}, props), { messageMiddleware: messageMiddleware });
34394
+ return jsx(ChatWidgetWrapper, __assign({}, widgetProps));
34395
+ };
34396
+ /**
34397
+ * Connected mode container - creates store and connects to server.
34398
+ */
34399
+ var ChatWidgetConnectedContainer = function (props) {
34211
34400
  var _a, _b, _c, _d, _e, _f;
34212
34401
  var messageMiddleware = useStandardMiddleware();
34213
34402
  var connection = useServerConfig(props.config);
@@ -34229,55 +34418,18 @@ var ChatWidgetContainer = function (props) {
34229
34418
  });
34230
34419
  return (jsx(Provider, { store: chatStore, children: jsx(ChatWidgetWrapper, __assign({}, widgetProps)) }));
34231
34420
  };
34232
-
34233
- // Function to create a static reducer
34234
- function createStaticReducer(state) {
34235
- return function (oldState, action) {
34236
- if (oldState === void 0) { oldState = state; }
34237
- log("static reducer", oldState, action);
34238
- return oldState;
34239
- };
34240
- }
34241
- // Function to create the store using @reduxjs/toolkit's configureStore
34242
- function createStaticStore(state) {
34243
- var reducer = createStaticReducer(state);
34244
- var store = configureStore({
34245
- reducer: reducer,
34246
- middleware: function (getDefaultMiddleware) {
34247
- return getDefaultMiddleware({
34248
- thunk: true, // Thunk is included by default, this is just for explicitness
34249
- });
34250
- },
34251
- devTools: "production" !== 'production', // Redux DevTools in non-production environments
34252
- });
34253
- return store;
34254
- }
34255
-
34256
- var StaticChatWidgetContainer = function (props) {
34257
- var state = props.state;
34258
- var store = useMemo(function () {
34259
- return createStaticStore(state);
34260
- }, [state]);
34261
- var config = useMemo(function () {
34262
- return __assign(__assign({}, props.config), { connection: {
34263
- serverUrl: "",
34264
- type: "local",
34265
- } });
34266
- }, [props.config]);
34267
- return (jsx(Provider, { store: store, children: jsx(ChatWidgetWrapper, __assign({}, props, { config: config })) }));
34268
- };
34269
-
34270
- function createStateFromMessages(messages) {
34271
- var def = createDefaultState();
34272
- return __assign(__assign({}, def), { connection: __assign(__assign({}, def.connection), { connectionStatus: "online" }), accountStatus: "online", chats: __spreadArray$1([], messages, true), isChatting: true, visuals: {} });
34273
- }
34274
-
34275
- var StaticMessagesChatWidgetContainer = function (props) {
34276
- var messages = props.messages;
34277
- var state = useMemo(function () {
34278
- return createStateFromMessages(messages);
34279
- }, [messages]);
34280
- return jsx(StaticChatWidgetContainer, { state: state, mode: props.mode, config: props.config });
34421
+ /**
34422
+ * Top-level container that dispatches between preview and connected modes.
34423
+ */
34424
+ var ChatWidgetContainer = function (props) {
34425
+ var _a;
34426
+ var rawMode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
34427
+ // Support both `preview` prop and legacy `mode="preview"`
34428
+ var isPreviewMode = props.preview === true || rawMode === "preview";
34429
+ if (isPreviewMode) {
34430
+ return jsx(ChatWidgetPreviewContainer, __assign({}, props));
34431
+ }
34432
+ return jsx(ChatWidgetConnectedContainer, __assign({}, props));
34281
34433
  };
34282
34434
 
34283
34435
  export { ActionButton, Avatar, CardMiddleware, Carousel, CarouselItem, ChatWidgetContainer as Chat, ChatButton, ChatCard, ChatChip, ChatChips, ChatHeader, ChatMenu, ChatMessage, ChatMessageBubble, ChatMessagePart, ChatWidgetWrapper as ChatWidget, CtaBubble, CtaBubbleContainer, List, ListItem, ListMiddleware, MessageList, MiddlewareContextFactory, StaticChatWidgetContainer, StaticMessagesChatWidgetContainer, joinMiddlewares, responseToMessage, useLateMiddleware, useStandardMiddleware };