@xapp/chat-widget 1.82.1 → 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';
@@ -9050,7 +9050,7 @@ var ChatMessage = function (props) {
9050
9050
  var agentInfo = (_a = props.agents) === null || _a === void 0 ? void 0 : _a[props.message.user.nick];
9051
9051
  var hideUserInfo = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.hideUserInfo) || false;
9052
9052
  function renderByType() {
9053
- var _a, _b, _c, _d;
9053
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
9054
9054
  var msg = props.message.msg;
9055
9055
  switch (props.message.type) {
9056
9056
  // TODO: props actually requires it to be "chat.msg". Fix prop typing?
@@ -9059,15 +9059,15 @@ var ChatMessage = function (props) {
9059
9059
  // OR card OR list only. Avatar with text bubble.
9060
9060
  var avaKey = avaKeys.find(function (key) { return !!msg[key]; });
9061
9061
  return (jsxs(Fragment, { children: [msg.text &&
9062
- 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 &&
9063
- 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) {
9064
9064
  if (display.type === "ScheduleButton") {
9065
9065
  return (jsx(ChatScheduleWidget, { minimizeOnClick: props.minimizeOnClick, display: display }));
9066
9066
  }
9067
9067
  var Middleware = middleware;
9068
9068
  return (jsx(Middleware, { msg: display, ctx: props.middlewareContext }, index));
9069
9069
  }), msg.permissionRequest && ctx &&
9070
- 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 }) })] }));
9071
9071
  }
9072
9072
  return (jsx(Fragment, {}));
9073
9073
  }
@@ -9174,10 +9174,12 @@ function useChatServerVisitorId() {
9174
9174
  }
9175
9175
  //send whenever server settings or visitor id changes
9176
9176
  function useGreeting(active) {
9177
+ var _a, _b;
9177
9178
  var curr = useChatServerVisitorId();
9178
9179
  var snapshotRef = useRef(null);
9179
9180
  var ctx = useContext(ChatConfigContext);
9180
- 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;
9181
9183
  var sessionId = useSelector(function (state) { return state.sessionId; });
9182
9184
  useEffect(function () {
9183
9185
  if (active) {
@@ -9576,7 +9578,7 @@ var Suggestions = function (props) {
9576
9578
 
9577
9579
  var ChatFooter = function (props) {
9578
9580
  var _a, _b, _c;
9579
- 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;
9580
9582
  var innerDispatch = useChatDispatch();
9581
9583
  var _d = useState(false), drawerOpen = _d[0], setDrawerState = _d[1]; // false initially
9582
9584
  var _e = useState(), suggestionSearch = _e[0], setSuggestionSearch = _e[1];
@@ -9634,7 +9636,7 @@ var ChatFooter = function (props) {
9634
9636
  setEnableInput(status);
9635
9637
  };
9636
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 ?
9637
- 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,
9638
9640
  // onFocus={this.inputOnFocus}
9639
9641
  onFileUpload: props.onFileUpload }) }), brandingEnabled && brandingText && jsx(ChatBranding, { text: brandingText })] }));
9640
9642
  };
@@ -32158,425 +32160,61 @@ var ModalContent = function (_a) {
32158
32160
  };
32159
32161
 
32160
32162
  /**
32161
- * Debounce delay for typing indicator in milliseconds.
32162
- * 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}
32163
32167
  */
32164
- var TYPING_INDICATOR_DEBOUNCE_MS = 2000;
32165
- var ChatWidgetWrapper = function (props) {
32166
- var _a;
32167
- var _b = useState(props.config), rawConfig = _b[0], setRawConfig = _b[1];
32168
- var _c = useState(!!props.getConfig), configLoading = _c[0], setConfigLoading = _c[1];
32169
- var _d = useState(), configError = _d[0], setConfigError = _d[1];
32170
- // Load config from callback if provided
32171
- useEffect(function () {
32172
- var cancelled = false;
32173
- if (props.getConfig) {
32174
- setConfigLoading(true);
32175
- setConfigError(undefined);
32176
- props
32177
- .getConfig()
32178
- .then(function (config) {
32179
- if (!cancelled) {
32180
- setRawConfig(config);
32181
- setConfigLoading(false);
32182
- log("[ChatWidget] Config loaded from getConfig callback");
32183
- }
32184
- })
32185
- .catch(function (error) {
32186
- if (!cancelled) {
32187
- setConfigError(error);
32188
- setConfigLoading(false);
32189
- err("[ChatWidget] Failed to load config: ".concat(error.message));
32190
- }
32191
- });
32192
- }
32193
- else if (props.config) {
32194
- // If no callback, use the config prop directly
32195
- setRawConfig(props.config);
32196
- setConfigLoading(false);
32197
- }
32198
- return function () {
32199
- cancelled = true;
32200
- };
32201
- // Only depend on getConfig and config - not the entire props object to avoid infinite loops
32202
- // eslint-disable-next-line react-hooks/exhaustive-deps
32203
- }, [props.getConfig, props.config]);
32204
- var connection = useConnectionInfo(rawConfig);
32205
- var config = useMemo(function () {
32206
- var _a;
32207
- 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 }));
32208
- }, [connection, rawConfig]);
32209
- var token = useSelector(function (state) { return state.connection.token; });
32210
- var options = useMemo(function () {
32211
- var configurableMessages = getConfigurableMessages();
32212
- if ((rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages) &&
32213
- Array.isArray(rawConfig.configurableMessages.items) &&
32214
- rawConfig.configurableMessages.items.length > 0) {
32215
- configurableMessages = rawConfig.configurableMessages;
32216
- }
32217
- return {
32218
- token: token,
32219
- bot: {
32220
- nick: "Bot",
32221
- displayName: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
32222
- avatarPath: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
32223
- },
32224
- configurableMessages: configurableMessages,
32225
- hooks: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
32226
- env: rawConfig,
32227
- };
32228
- }, [
32229
- token,
32230
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
32231
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
32232
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages,
32233
- rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
32234
- ]);
32235
- // Only create chat server when config is ready (not loading and no error)
32236
- var chatServer = useChatServer(configLoading || configError ? null : connection, configLoading || configError ? null : options);
32237
- // Determine mode class for loading/error states
32238
- var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
32239
- var modeClass = "widget-container--".concat(mode);
32240
- // Show loading state while config is being fetched
32241
- if (configLoading) {
32242
- 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" }) }) }));
32243
- }
32244
- // Show error state if config failed to load
32245
- if (configError) {
32246
- 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] }) }));
32247
- }
32248
- 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
32249
32176
  };
32250
- var ChatWidget = function (props) {
32251
- 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;
32252
- var innerDispatch = useChatDispatch();
32253
- var dispatch = useChatServerDispatch();
32254
- // From Redux
32255
- var chatState = useSelector(function (state) { return state; });
32256
- // Refresh modalReference
32257
- var modalRef = useRef({});
32258
- var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
32259
- var dockedMode = mode === "docked";
32260
- var staticMode = mode === "static";
32261
- var modeClass = "widget-container--".concat(mode);
32262
- var canRefresh = (_c = (_b = props.config.header) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.refresh;
32263
- // can't minimize in docked mode or static mode.
32264
- 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);
32265
- 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));
32266
- var canCancel;
32267
- // To preserve legacy behavior, cancel needs a little more checks
32268
- if (typeof ((_j = (_h = props.config.header) === null || _h === void 0 ? void 0 : _h.actions) === null || _j === void 0 ? void 0 : _j.cancel) === "boolean") {
32269
- canCancel = !dockedMode && !staticMode && ((_l = (_k = props.config.header) === null || _k === void 0 ? void 0 : _k.actions) === null || _l === void 0 ? void 0 : _l.cancel);
32270
- }
32271
- else {
32272
- canCancel = !dockedMode && !staticMode;
32273
- }
32274
- // For backward compatibility. Note: the action will create the actual "visuals" object" (this is a copy).
32275
- if (!chatState.visuals) {
32276
- chatState.visuals = {};
32177
+ function createDefaultState(state, options) {
32178
+ var _a;
32179
+ if (!state) {
32180
+ state = {};
32277
32181
  }
32278
- // Our state - pull from storage
32279
- var _8 = useState((!canMinimize && !canCancel) ||
32280
- // !!get("visible") ||
32281
- chatState.visuals.visible ||
32282
- (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
32283
- window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _8[0], setVisibleState = _8[1];
32284
- var _9 = useState(false); _9[0]; var setTypingState = _9[1]; // false initially - state kept for potential external observers
32285
- // Ref to track typing state for use in timeout callbacks
32286
- var typingRef = useRef(false);
32287
- // Timeout ref for debouncing "stop typing" events
32288
- var stopTypingTimeoutRef = useRef(null);
32289
- var chatServer = useContext(ChatServerContext);
32290
- var patternsConfig = (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.autoOpenOnPattern) === null || _q === void 0 ? void 0 : _q.patterns;
32291
- var currentUrl = window.location.href;
32292
- var patternExist = patternsConfig && patternsConfig.length > 0;
32293
- var patternMatches = patternsConfig === null || patternsConfig === void 0 ? void 0 : patternsConfig.some(function (pattern) { return currentUrl.includes(pattern); });
32294
- var configWidth = (_s = (_r = props.config) === null || _r === void 0 ? void 0 : _r.autoOpenOnPattern) === null || _s === void 0 ? void 0 : _s.minimumWidth;
32295
- // eslint-disable-next-line no-restricted-globals
32296
- var currentWidth = screen.width;
32297
- var setVisible = useCallback(function (newVisible) {
32298
- if (staticMode) {
32299
- return;
32300
- }
32301
- log("setVisible: ".concat(newVisible));
32302
- setVisibleState(newVisible);
32303
- innerDispatch(setVisualStatus({
32304
- visible: newVisible,
32305
- }));
32306
- }, [innerDispatch, staticMode]);
32307
- useEffect(function () {
32308
- var _a, _b;
32309
- var handleKeyDown = function (event) {
32310
- var body = document.getElementsByTagName("body")[0];
32311
- body.tabIndex = -1;
32312
- if (event.key === "Escape") {
32313
- body.focus();
32314
- }
32315
- };
32316
- document.addEventListener("keydown", handleKeyDown);
32317
- 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)) {
32318
- innerDispatch(setSessionId(undefined));
32319
- innerDispatch(reset());
32320
- }
32321
- return function () {
32322
- document.removeEventListener("keydown", handleKeyDown);
32323
- // Cleanup typing timeout on unmount
32324
- if (stopTypingTimeoutRef.current) {
32325
- clearTimeout(stopTypingTimeoutRef.current);
32326
- }
32327
- };
32328
- // eslint-disable-next-line react-hooks/exhaustive-deps
32329
- }, []);
32330
- var _10 = useState(!document.hidden), isTabVisible = _10[0], setIsTabVisible = _10[1];
32331
- useEffect(function () {
32332
- var handleVisibilityChange = function () {
32333
- setIsTabVisible(!document.hidden);
32334
- };
32335
- document.addEventListener("visibilitychange", handleVisibilityChange);
32336
- return function () {
32337
- document.removeEventListener("visibilitychange", handleVisibilityChange);
32338
- };
32339
- }, []);
32340
- useEffect(function () {
32341
- // Too early?
32342
- if (!chatServer) {
32343
- log("Tab visibility changed but no chatServer yet");
32344
- return;
32345
- }
32346
- if (!isTabVisible) {
32347
- log("Tab is HIDDEN - calling sleep()");
32348
- chatServer.sleep();
32349
- }
32350
- else {
32351
- log("Tab is VISIBLE - calling wakeup()");
32352
- chatServer.wakeup();
32353
- }
32354
- }, [chatServer, innerDispatch, isTabVisible]);
32355
- // Handle beforeunload to notify router when user is leaving
32356
- useEffect(function () {
32357
- if (!chatServer) {
32358
- return undefined;
32359
- }
32360
- var handleBeforeUnload = function () {
32361
- log("beforeunload - notifying router of user disconnecting");
32362
- if (chatServer.notifyDisconnecting) {
32363
- chatServer.notifyDisconnecting();
32364
- }
32365
- };
32366
- window.addEventListener("beforeunload", handleBeforeUnload);
32367
- return function () {
32368
- window.removeEventListener("beforeunload", handleBeforeUnload);
32369
- };
32370
- }, [chatServer]);
32371
- useEffect(function () {
32372
- // For reopen widget after move on same window
32373
- // if (get("opened")) {
32374
- // setVisible(true);
32375
- // }
32376
- if (chatState.visuals.opened) {
32377
- setVisible(true);
32378
- }
32379
- else {
32380
- if (mode === "normal") {
32381
- setVisible(false);
32382
- }
32383
- }
32384
- if (patternExist && patternMatches) {
32385
- setVisible(true);
32386
- }
32387
- if (currentWidth < +configWidth) {
32388
- setVisible(false);
32389
- }
32390
- }, [
32391
- currentWidth,
32392
- patternExist,
32393
- patternMatches,
32394
- configWidth,
32395
- setVisible,
32396
- chatState.visuals.opened,
32397
- mode,
32398
- ]);
32399
- var stopTyping = useCallback(function () {
32400
- if (!typingRef.current)
32401
- return;
32402
- // Clear the timeout if it exists
32403
- if (stopTypingTimeoutRef.current) {
32404
- clearTimeout(stopTypingTimeoutRef.current);
32405
- stopTypingTimeoutRef.current = null;
32406
- }
32407
- dispatch(sendTyping(false));
32408
- setTypingState(false);
32409
- typingRef.current = false;
32410
- }, [dispatch]);
32411
- var handleOnChange = useCallback(function () {
32412
- // Send "typing" event only on first keystroke
32413
- if (!typingRef.current) {
32414
- dispatch(sendTyping(true));
32415
- setTypingState(true);
32416
- typingRef.current = true;
32417
- }
32418
- // Clear any existing timeout
32419
- if (stopTypingTimeoutRef.current) {
32420
- 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;
32421
32190
  }
32422
- // Set a new timeout to send "stop typing" after inactivity
32423
- stopTypingTimeoutRef.current = window.setTimeout(function () {
32424
- stopTyping();
32425
- }, TYPING_INDICATOR_DEBOUNCE_MS);
32426
- }, [dispatch, stopTyping]);
32427
- function handleSendMessage(msg) {
32428
- dispatch(sendMessage(msg));
32429
- }
32430
- function handleWriteMessage(msg) {
32431
- innerDispatch(writeMessage(msg.msg, msg.user));
32432
- }
32433
- var handleOpenUrl = useOpenUrlCallback();
32434
- function handleOnSubmit(rawInput) {
32435
- // Don't allow visitor to send msg if not chatting
32436
- if (chatState.accountStatus === "offline" && !chatState.isChatting)
32437
- return;
32438
- // Don't send empty messages
32439
- if (!rawInput)
32440
- return;
32441
- // Immediately stop typing
32442
- stopTyping();
32443
- chatServer.flush();
32444
- dispatch(executeAction(rawInput));
32445
- }
32446
- function handleFileUpload(event) {
32447
- var _a;
32448
- event.preventDefault();
32449
- // Only send the first file dropped on input
32450
- var file = (_a = event === null || event === void 0 ? void 0 : event.dataTransfer) === null || _a === void 0 ? void 0 : _a.files[0];
32451
- if (!file) {
32452
- return;
32191
+ else if (localStorageSetting === 'disabled') {
32192
+ debugMode = false;
32453
32193
  }
32454
- dispatch(sendFile(file));
32455
- }
32456
- function getVisibilityClass() {
32457
- return visible ? "visible" : "";
32458
- }
32459
- function openRestartConfirmationModal() {
32460
- // Interesting, this call the modalRef of the restart modal
32461
- modalRef.current.style.display = "block";
32462
- }
32463
- function closeRestartConfirmationModal() {
32464
- modalRef.current.style.display = "none";
32465
- }
32466
- function handleReset() {
32467
- innerDispatch(reset());
32468
- }
32469
- function handleRestartClick() {
32470
- if (config.header.actions.refreshShowConfirmation) {
32471
- // show the modal
32472
- openRestartConfirmationModal();
32473
- // 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;
32474
32197
  }
32475
- else {
32476
- handleReset();
32198
+ else if (typeof globalThis.__DEV__ !== 'undefined' && globalThis.__DEV__) {
32199
+ // For React Native, default to true in development mode
32200
+ debugMode = true;
32477
32201
  }
32478
32202
  }
32479
- function handleRestartModalCloseClick() {
32480
- closeRestartConfirmationModal();
32481
- }
32482
- /** Called when minimize button is clicked */
32483
- function handleMinimizeClick() {
32484
- innerDispatch(setVisualStatus({
32485
- opened: false,
32486
- hasInteracted: true,
32487
- }));
32488
- setVisible(false);
32489
- }
32490
- /** Called when cancel is clicked */
32491
- function handleCancelClick() {
32492
- // First reset to clear all state
32493
- innerDispatch(reset());
32494
- // Then set hasInteracted to prevent CTA from showing after cancel
32495
- innerDispatch(setVisualStatus({
32496
- opened: false,
32497
- hasInteracted: true,
32498
- }));
32499
- setVisible(false);
32500
- }
32501
- function chatButtonOnClick() {
32502
- 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,
32503
32215
  opened: true,
32504
- hasInteracted: true,
32505
- }));
32506
- setVisible(true);
32507
- setTimeout(function () {
32508
- document.getElementById("chatWidgetInput").focus();
32509
- }, 100);
32510
- }
32511
- function handleCtaDismiss() {
32512
- innerDispatch(setVisualStatus({
32513
- hasInteracted: true,
32514
- }));
32515
- }
32516
- function handleWsButtonPress(buttonId) {
32517
- var _a, _b;
32518
- log("WS Button pressed: ".concat(buttonId));
32519
- // If the button has spinning behavior, update state to show spinner
32520
- if (((_a = chatState.wsButton) === null || _a === void 0 ? void 0 : _a.pressBehavior) === "spinning") {
32521
- // Update the button state to show it's pressed
32522
- innerDispatch({
32523
- type: "ws_button_display",
32524
- detail: __assign(__assign({}, chatState.wsButton), { isPressed: true, timestamp: +new Date() })
32525
- });
32526
- }
32527
- // Send the button pressed event to the server
32528
- if (chatServer && chatServer.sendButtonPressed) {
32529
- chatServer.sendButtonPressed(buttonId).catch(function (error) {
32530
- err("Failed to send button press: ".concat(error));
32531
- // Reset button state on error
32532
- if (chatState.wsButton) {
32533
- innerDispatch({
32534
- type: "ws_button_display",
32535
- detail: __assign(__assign({}, chatState.wsButton), { isPressed: false, timestamp: +new Date() })
32536
- });
32537
- }
32538
- });
32539
- }
32540
- // If the button has disabled behavior, animate out then dismiss
32541
- if (((_b = chatState.wsButton) === null || _b === void 0 ? void 0 : _b.pressBehavior) === "disabled") {
32542
- // First set dismissing state to trigger animation
32543
- innerDispatch({
32544
- type: "ws_button_display",
32545
- detail: __assign(__assign({}, chatState.wsButton), { isDismissing: true, timestamp: +new Date() })
32546
- });
32547
- // Then actually dismiss after animation completes
32548
- setTimeout(function () {
32549
- innerDispatch({
32550
- type: "ws_button_dismiss",
32551
- detail: {
32552
- id: buttonId,
32553
- timestamp: +new Date()
32554
- }
32555
- });
32556
- }, WS_BUTTON_DISMISS_ANIMATION_DURATION);
32557
- }
32558
- }
32559
- var isOffline = chatState.accountStatus === "offline" && !chatState.isChatting;
32560
- var messages = chatState && chatState.chats;
32561
- log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
32562
- var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
32563
- useGreeting(!isOffline && !props.preChatFormEnabled && visible);
32564
- var connectionStatus = chatState.connection.connectionStatus;
32565
- useEffect(function () {
32566
- if (onConnectionStatusChange) {
32567
- onConnectionStatusChange(connectionStatus);
32568
- }
32569
- }, [connectionStatus, onConnectionStatusChange]);
32570
- useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
32571
- // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
32572
- var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
32573
- (config === null || config === void 0 ? void 0 : config.agent) || {
32574
- nick: "agent:robot",
32575
- avatarPath: config.avatarUrl,
32576
- display_name: "Agent",
32577
- };
32578
- 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: 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 })] }));
32579
- };
32216
+ } });
32217
+ }
32580
32218
 
32581
32219
  // src/utils/formatProdErrorMessage.ts
32582
32220
  function formatProdErrorMessage$1(code) {
@@ -33733,6 +33371,576 @@ function formatProdErrorMessage(code) {
33733
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. `;
33734
33372
  }
33735
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
+
33736
33944
  function tryParseJson(str) {
33737
33945
  try {
33738
33946
  if (str) {
@@ -33762,55 +33970,6 @@ var BrowserStateStorage = /** @class */ (function () {
33762
33970
  return BrowserStateStorage;
33763
33971
  }());
33764
33972
 
33765
- /**
33766
- * Id the visitor (cookies, browser fingerprint, etc)
33767
- *
33768
- * @export
33769
- * @returns {string}
33770
- */
33771
- function visitorFingerprint() {
33772
- return uuid_1();
33773
- }
33774
-
33775
- var DEFAULT_VISITOR = {
33776
- displayName: "Visitor",
33777
- nick: "visitor:",
33778
- typing: false
33779
- };
33780
- function createDefaultState(state, options) {
33781
- var _a;
33782
- if (!state) {
33783
- state = {};
33784
- }
33785
- state.userId = state.userId ? state.userId : visitorFingerprint();
33786
- state.visitorId = state.userId;
33787
- // Determine if debug mode should be enabled
33788
- var debugMode = false;
33789
- if (typeof window !== 'undefined') {
33790
- var localStorageSetting = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem('xaErrorOverlay');
33791
- if (localStorageSetting === 'enabled') {
33792
- debugMode = true;
33793
- }
33794
- else if (localStorageSetting === 'disabled') {
33795
- debugMode = false;
33796
- }
33797
- else if (options === null || options === void 0 ? void 0 : options.enableErrorOverlay) {
33798
- // Use config option if no localStorage override
33799
- debugMode = true;
33800
- }
33801
- else if (typeof globalThis.__DEV__ !== 'undefined' && globalThis.__DEV__) {
33802
- // For React Native, default to true in development mode
33803
- debugMode = true;
33804
- }
33805
- }
33806
- return __assign({ connection: {
33807
- connectionStatus: "offline",
33808
- token: null,
33809
- greetingRequested: false
33810
- }, 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);
33811
- }
33812
- var DEFAULT_STATE = createDefaultState();
33813
-
33814
33973
  /**
33815
33974
  * Polyfill for Browser Local Storage
33816
33975
  *
@@ -34226,7 +34385,18 @@ function generateKey(connection, sessionId) {
34226
34385
  }
34227
34386
  }
34228
34387
 
34229
- 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) {
34230
34400
  var _a, _b, _c, _d, _e, _f;
34231
34401
  var messageMiddleware = useStandardMiddleware();
34232
34402
  var connection = useServerConfig(props.config);
@@ -34248,55 +34418,18 @@ var ChatWidgetContainer = function (props) {
34248
34418
  });
34249
34419
  return (jsx(Provider, { store: chatStore, children: jsx(ChatWidgetWrapper, __assign({}, widgetProps)) }));
34250
34420
  };
34251
-
34252
- // Function to create a static reducer
34253
- function createStaticReducer(state) {
34254
- return function (oldState, action) {
34255
- if (oldState === void 0) { oldState = state; }
34256
- log("static reducer", oldState, action);
34257
- return oldState;
34258
- };
34259
- }
34260
- // Function to create the store using @reduxjs/toolkit's configureStore
34261
- function createStaticStore(state) {
34262
- var reducer = createStaticReducer(state);
34263
- var store = configureStore({
34264
- reducer: reducer,
34265
- middleware: function (getDefaultMiddleware) {
34266
- return getDefaultMiddleware({
34267
- thunk: true, // Thunk is included by default, this is just for explicitness
34268
- });
34269
- },
34270
- devTools: "production" !== 'production', // Redux DevTools in non-production environments
34271
- });
34272
- return store;
34273
- }
34274
-
34275
- var StaticChatWidgetContainer = function (props) {
34276
- var state = props.state;
34277
- var store = useMemo(function () {
34278
- return createStaticStore(state);
34279
- }, [state]);
34280
- var config = useMemo(function () {
34281
- return __assign(__assign({}, props.config), { connection: {
34282
- serverUrl: "",
34283
- type: "local",
34284
- } });
34285
- }, [props.config]);
34286
- return (jsx(Provider, { store: store, children: jsx(ChatWidgetWrapper, __assign({}, props, { config: config, disableAutoScroll: props.disableAutoScroll })) }));
34287
- };
34288
-
34289
- function createStateFromMessages(messages) {
34290
- var def = createDefaultState();
34291
- return __assign(__assign({}, def), { connection: __assign(__assign({}, def.connection), { connectionStatus: "online" }), accountStatus: "online", chats: __spreadArray$1([], messages, true), isChatting: true, visuals: {} });
34292
- }
34293
-
34294
- var StaticMessagesChatWidgetContainer = function (props) {
34295
- var messages = props.messages;
34296
- var state = useMemo(function () {
34297
- return createStateFromMessages(messages);
34298
- }, [messages]);
34299
- return jsx(StaticChatWidgetContainer, { state: state, mode: props.mode, config: props.config, disableAutoScroll: props.disableAutoScroll });
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));
34300
34433
  };
34301
34434
 
34302
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 };