@xapp/chat-widget 1.78.1 → 1.79.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.
Files changed (33) hide show
  1. package/dist/App.d.ts +0 -0
  2. package/dist/components/ChatButton/ChatButton.d.ts +0 -0
  3. package/dist/components/ChatFooter/ChatFooter.d.ts +0 -0
  4. package/dist/components/ChatFooter/ChatFooter.stories.d.ts +3 -0
  5. package/dist/components/ChatWidget/ChatWidget.d.ts +0 -0
  6. package/dist/components/ChatWidget/ChatWidget.stories.d.ts +6 -0
  7. package/dist/components/ChatWidget/ChatWidget.test.d.ts +1 -0
  8. package/dist/components/ChatWidget/ChatWidgetContainer.d.ts +0 -0
  9. package/dist/components/ChatWidget/ChatWidgetEnv.stories.d.ts +1 -0
  10. package/dist/components/ErrorOverlay/ErrorOverlay.d.ts +0 -0
  11. package/dist/components/ErrorOverlay/ErrorOverlay.stories.d.ts +8 -0
  12. package/dist/components/ErrorOverlay/index.d.ts +0 -0
  13. package/dist/components/Input/Input.d.ts +0 -0
  14. package/dist/components/Input/Input.stories.d.ts +7 -3
  15. package/dist/components/SendButton/SendButton.d.ts +0 -0
  16. package/dist/components/SendButton/SendButton.stories.d.ts +8 -4
  17. package/dist/index.css +1 -1
  18. package/dist/index.es.js +581 -96
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.js +581 -96
  21. package/dist/index.js.map +1 -1
  22. package/dist/store/ChatAction.d.ts +0 -0
  23. package/dist/store/ChatState.d.ts +0 -0
  24. package/dist/store/DefaultState.d.ts +0 -0
  25. package/dist/store/actions/index.d.ts +0 -0
  26. package/dist/store/actions/toggleDebugMode.d.ts +0 -0
  27. package/dist/xapp/StentorRouterChat.d.ts +0 -0
  28. package/dist/xapp/XappChat.d.ts +0 -0
  29. package/dist/xapp/__tests__/StentorRouterChat.test.d.ts +2 -0
  30. package/dist/xapp-chat-widget.css +1 -1
  31. package/dist/xapp-chat-widget.js +4 -4
  32. package/dist/xapp-chat-widget.js.map +1 -1
  33. package/package.json +10 -10
package/dist/index.js CHANGED
@@ -306,6 +306,27 @@ function getTimeAgo(date) {
306
306
  }
307
307
 
308
308
  var EMAIL_REGEX = "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$";
309
+ // Initialize log level from localStorage on module load
310
+ var STORAGE_KEY = 'xaLogLevel';
311
+ var storedLogLevel = localStorage.getItem(STORAGE_KEY);
312
+ if (storedLogLevel) {
313
+ window.xaLogLevel = storedLogLevel;
314
+ }
315
+ // Override the setter to persist to localStorage
316
+ Object.defineProperty(window, 'xaLogLevel', {
317
+ get: function () {
318
+ return this._xaLogLevel;
319
+ },
320
+ set: function (value) {
321
+ this._xaLogLevel = value;
322
+ if (value) {
323
+ localStorage.setItem(STORAGE_KEY, value);
324
+ }
325
+ else {
326
+ localStorage.removeItem(STORAGE_KEY);
327
+ }
328
+ }
329
+ });
309
330
  // Returns current timestamp
310
331
  function getCurrentDateString() {
311
332
  return (new Date()).toISOString() + " ::";
@@ -605,7 +626,7 @@ function resize(vec, length) {
605
626
  return mul(vec, length / currentLength);
606
627
  }
607
628
  function rotateZ(vec, angle) {
608
- var rad = angle * Math.PI / 180;
629
+ var rad = (angle * Math.PI) / 180;
609
630
  var ca = Math.cos(rad);
610
631
  var sa = Math.sin(rad);
611
632
  var resx = vec.x * ca + vec.y * sa;
@@ -622,9 +643,9 @@ function intersectLineEllipsis(ellipsis, line) {
622
643
  var ry = ellipsis.radius.y;
623
644
  var rx2 = rx * rx;
624
645
  var ry2 = ry * ry;
625
- var a = (kx * kx / rx2 + ky * ky / ry2);
626
- var b = (2 * kx * sx / rx2 + 2 * ky * sy / ry2);
627
- var c = (sx * sx / rx2 + sy * sy / ry2 - 1);
646
+ var a = (kx * kx) / rx2 + (ky * ky) / ry2;
647
+ var b = (2 * kx * sx) / rx2 + (2 * ky * sy) / ry2;
648
+ var c = (sx * sx) / rx2 + (sy * sy) / ry2 - 1;
628
649
  var d2 = b * b - 4 * a * c;
629
650
  if (d2 >= 0) {
630
651
  var d = Math.sqrt(d2);
@@ -643,10 +664,13 @@ function intersectLineEllipsis(ellipsis, line) {
643
664
  return line.start;
644
665
  }
645
666
  function getTailSvgPath$1(props, viewport) {
646
- var baseAngleRad = props.direction * Math.PI / 180;
667
+ var baseAngleRad = (props.direction * Math.PI) / 180;
647
668
  var radius = { x: props.width / 2, y: props.height / 2 };
648
669
  var center = { x: viewport.x / 2, y: viewport.y / 2 };
649
- var intersectionRel = { x: radius.x * Math.cos(baseAngleRad), y: radius.y * Math.sin(baseAngleRad) };
670
+ var intersectionRel = {
671
+ x: radius.x * Math.cos(baseAngleRad),
672
+ y: radius.y * Math.sin(baseAngleRad),
673
+ };
650
674
  var farRel = resize(intersectionRel, len(intersectionRel) + props.length);
651
675
  var far = add$1(farRel, center);
652
676
  var direction = sub$1(intersectionRel, farRel);
@@ -655,15 +679,15 @@ function getTailSvgPath$1(props, viewport) {
655
679
  var rightDirection = rotateZ(direction, -angle / 2);
656
680
  var ellipsis = {
657
681
  center: center,
658
- radius: radius
682
+ radius: radius,
659
683
  };
660
684
  var left = intersectLineEllipsis(ellipsis, {
661
685
  start: far,
662
- direction: leftDirection
686
+ direction: leftDirection,
663
687
  });
664
688
  var right = intersectLineEllipsis(ellipsis, {
665
689
  start: far,
666
- direction: rightDirection
690
+ direction: rightDirection,
667
691
  });
668
692
  return "m ".concat(far.x, " ").concat(far.y, " L ").concat(left.x, " ").concat(left.y, " L ").concat(right.x, " ").concat(right.y, " z");
669
693
  }
@@ -671,9 +695,9 @@ var CtaBubbleTail = function (props) {
671
695
  var _a, _b;
672
696
  var _c = useDimensions(), ref = _c[0], rect = _c[1];
673
697
  var viewPort = { x: (_a = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _a !== void 0 ? _a : 0, y: (_b = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _b !== void 0 ? _b : 0 };
674
- return React$1.createElement("div", { ref: ref, className: "cta-bubble__tail" },
698
+ return (React$1.createElement("div", { ref: ref, className: "cta-bubble__tail" },
675
699
  React$1.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 ".concat(viewPort.x, " ").concat(viewPort.y) },
676
- React$1.createElement("path", { d: getTailSvgPath$1(props, viewPort), fill: "currentColor" })));
700
+ React$1.createElement("path", { d: getTailSvgPath$1(props, viewPort), fill: "currentColor" }))));
677
701
  };
678
702
  var CtaBubble = function (props) {
679
703
  var _a, _b, _c, _d;
@@ -686,10 +710,11 @@ var CtaBubble = function (props) {
686
710
  props.onDismiss();
687
711
  }
688
712
  };
713
+ console.log("Returning CTABubble with message: ", props.children);
689
714
  return (React$1.createElement("div", { ref: ref, style: {
690
- border: props.borderStyle ? 'solid' : 'none',
691
- borderWidth: ((_a = props.borderStyle) === null || _a === void 0 ? void 0 : _a.width) || '0px',
692
- borderColor: ((_b = props.borderStyle) === null || _b === void 0 ? void 0 : _b.color) || 'transparent',
715
+ border: props.borderStyle ? "solid" : "none",
716
+ borderWidth: ((_a = props.borderStyle) === null || _a === void 0 ? void 0 : _a.width) || "0px",
717
+ borderColor: ((_b = props.borderStyle) === null || _b === void 0 ? void 0 : _b.color) || "transparent",
693
718
  animation: "".concat(animation, " 1s infinite"),
694
719
  }, className: "cta-bubble", onClick: props.onClick },
695
720
  React$1.createElement(CtaBubbleTail, { width: (_c = rect === null || rect === void 0 ? void 0 : rect.width) !== null && _c !== void 0 ? _c : 0, height: (_d = rect === null || rect === void 0 ? void 0 : rect.height) !== null && _d !== void 0 ? _d : 0, direction: 60, angle: 45, length: 16 }),
@@ -701,54 +726,102 @@ var CtaBubble = function (props) {
701
726
 
702
727
  var CtaBubbleContainer = function (props) {
703
728
  var visible = props.visible, timeout = props.timeout, delay = props.delay, animate = props.animate, buttonAnimation = props.buttonAnimation, dismissible = props.dismissible, onClick = props.onClick, onDismiss = props.onDismiss, children = props.children;
729
+ // Debug: Log all props received
730
+ log("[CtaBubbleContainer] Props received:", {
731
+ visible: visible,
732
+ timeout: timeout,
733
+ delay: delay,
734
+ animate: animate,
735
+ buttonAnimation: buttonAnimation,
736
+ dismissible: dismissible,
737
+ hasOnClick: !!onClick,
738
+ hasOnDismiss: !!onDismiss,
739
+ hasChildren: !!children,
740
+ childrenContent: children,
741
+ });
704
742
  var startTime = React$1.useMemo(function () {
705
- return visible ? new Date().valueOf() : undefined;
743
+ var time = visible ? new Date().valueOf() : undefined;
744
+ log("[CtaBubbleContainer] StartTime calculated: visible=".concat(visible, ", startTime=").concat(time));
745
+ return time;
706
746
  }, [visible]);
707
747
  var _a = React$1.useState(false), showBubble = _a[0], setShowBubble = _a[1];
708
- var isMounted = React$1.useRef(true);
748
+ log("[CtaBubbleContainer] Current state: showBubble=".concat(showBubble));
709
749
  React$1.useEffect(function () {
750
+ log("[CtaBubbleContainer] useEffect triggered with:", {
751
+ startTime: startTime,
752
+ startTimeExists: !!startTime,
753
+ timeout: timeout,
754
+ timeoutType: typeof timeout,
755
+ delay: delay,
756
+ delayType: typeof delay,
757
+ visible: visible,
758
+ });
710
759
  var hideTimer = null;
760
+ var delayTimer = null;
761
+ var isMounted = true;
711
762
  if (startTime) {
712
- var delayTimer_1 = setTimeout(function () {
713
- if (isMounted.current) {
763
+ var actualDelay_1 = delay || 0;
764
+ log("[CtaBubbleContainer] Timer setup: actualDelay=".concat(actualDelay_1, "ms (from delay=").concat(delay, ")"));
765
+ delayTimer = setTimeout(function () {
766
+ log("[CtaBubbleContainer] Delay timer fired after ".concat(actualDelay_1, "ms, isMounted=").concat(isMounted));
767
+ if (isMounted) {
768
+ log("[CtaBubbleContainer] Component still mounted, setting showBubble=true");
714
769
  setShowBubble(true);
770
+ if (typeof timeout === "number") {
771
+ log("[CtaBubbleContainer] Hide timer setup: will hide after ".concat(timeout, "ms"));
772
+ hideTimer = setTimeout(function () {
773
+ log("[CtaBubbleContainer] Hide timer fired after ".concat(timeout, "ms, isMounted=").concat(isMounted));
774
+ if (isMounted) {
775
+ log("[CtaBubbleContainer] Component still mounted, setting showBubble=false");
776
+ setShowBubble(false);
777
+ }
778
+ else {
779
+ log("[CtaBubbleContainer] Component unmounted, skipping hide");
780
+ }
781
+ }, timeout);
782
+ }
783
+ else {
784
+ log("[CtaBubbleContainer] No hide timer set (timeout=".concat(timeout, ", type=").concat(typeof timeout, ")"));
785
+ }
715
786
  }
716
- if (typeof timeout === "number") {
717
- hideTimer = setTimeout(function () {
718
- if (isMounted.current) {
719
- setShowBubble(false);
720
- }
721
- }, timeout);
722
- }
723
- }, delay);
724
- return function () {
725
- if (hideTimer) {
726
- clearTimeout(hideTimer);
787
+ else {
788
+ log("[CtaBubbleContainer] Component unmounted before delay timer, not showing bubble");
727
789
  }
728
- clearTimeout(delayTimer_1);
729
- isMounted.current = false;
730
- };
790
+ }, actualDelay_1);
791
+ }
792
+ else {
793
+ log("[CtaBubbleContainer] No startTime, skipping timer setup");
731
794
  }
732
- // Return a cleanup function for the case where startTime is undefined
733
795
  return function () {
796
+ log("[CtaBubbleContainer] Cleanup triggered, unmounting component");
797
+ isMounted = false;
734
798
  if (hideTimer) {
799
+ log("[CtaBubbleContainer] Clearing hide timer");
735
800
  clearTimeout(hideTimer);
736
801
  }
737
- isMounted.current = false;
802
+ if (delayTimer) {
803
+ log("[CtaBubbleContainer] Clearing delay timer");
804
+ clearTimeout(delayTimer);
805
+ }
738
806
  };
739
- }, [startTime, timeout, delay]);
807
+ }, [startTime, timeout, delay, visible]);
740
808
  var handleDismiss = function () {
809
+ log("[CtaBubbleContainer] handleDismiss called, setting showBubble=false");
741
810
  setShowBubble(false);
742
811
  if (onDismiss) {
812
+ log("[CtaBubbleContainer] Calling onDismiss callback");
743
813
  onDismiss();
744
814
  }
745
815
  };
816
+ // Final render decision
817
+ var shouldRender = visible && showBubble;
818
+ log("[CtaBubbleContainer] Render decision: visible=".concat(visible, ", showBubble=").concat(showBubble, ", shouldRender=").concat(shouldRender));
746
819
  return (React$1.createElement(React$1.Fragment, null, visible && showBubble && (React$1.createElement(CtaBubble, { onClick: onClick, animate: animate, buttonAnimation: buttonAnimation, dismissible: dismissible, onDismiss: handleDismiss }, children))));
747
820
  };
748
821
 
749
822
  var ChatButton = function (_a) {
750
823
  var _b;
751
- var onClick = _a.onClick, addClass = _a.addClass, config = _a.config, visible = _a.visible, borderStyle = _a.borderStyle, imageUrl = _a.imageUrl;
824
+ var onClick = _a.onClick, addClass = _a.addClass, config = _a.config, visible = _a.visible, borderStyle = _a.borderStyle, imageUrl = _a.imageUrl, hasInteracted = _a.hasInteracted, onCtaDismiss = _a.onCtaDismiss;
752
825
  var _c = React$1.useState(defaultWidgetButtonWidth), buttonWidth = _c[0], setButtonWidth = _c[1];
753
826
  var _d = React$1.useState(false), animate = _d[0], setAnimate = _d[1];
754
827
  var mobileWidth = ((_b = config === null || config === void 0 ? void 0 : config.mobile) === null || _b === void 0 ? void 0 : _b.applyAtLessThanWidth) || defaultNonMobileScreenWidth;
@@ -767,6 +840,11 @@ var ChatButton = function (_a) {
767
840
  var _a;
768
841
  var delayTimer;
769
842
  var timeoutTimer;
843
+ // Don't animate if user has already interacted
844
+ if (hasInteracted) {
845
+ setAnimate(false);
846
+ return function () { }; // Return empty cleanup function
847
+ }
770
848
  // Use animationDelay if provided, otherwise fall back to delay for backwards compatibility
771
849
  var animationDelayToUse = (_a = configToApply === null || configToApply === void 0 ? void 0 : configToApply.animationDelay) !== null && _a !== void 0 ? _a : configToApply === null || configToApply === void 0 ? void 0 : configToApply.delay;
772
850
  if (animationDelayToUse) {
@@ -799,8 +877,8 @@ var ChatButton = function (_a) {
799
877
  if (timeoutTimer)
800
878
  clearTimeout(timeoutTimer);
801
879
  };
802
- }, [configToApply, setAnimate]);
803
- var animation = animate ? (configToApply === null || configToApply === void 0 ? void 0 : configToApply.animation) || "wiggle" : "none";
880
+ }, [configToApply, setAnimate, hasInteracted]);
881
+ var animation = animate && !hasInteracted ? (configToApply === null || configToApply === void 0 ? void 0 : configToApply.animation) || "wiggle" : "none";
804
882
  React$1.useEffect(function () {
805
883
  var handleResize = function () {
806
884
  var screenWidth = window.innerWidth;
@@ -832,8 +910,8 @@ var ChatButton = function (_a) {
832
910
  // Fallback to default SVG
833
911
  React$1.createElement("svg", { width: svgSize, height: svgSize, viewBox: "0 0 22 22" },
834
912
  React$1.createElement("path", { d: "M13 22l-4-6H2c-1.11-.043-2-.935-2-2V2C0 .89.89 0 2 0h18c1.11 0 2 .892 2 2v12c0 1.067-.89 1.957-2 2h-3l-4 6zm3-8h4c-.005.3-.01-12 0-12-.01.004-18 .006-18 0 .005.006 0 12 0 12h8l3 5 3-5z", fill: "#FFF", fillRule: "evenodd" })))),
835
- configToApply && configToApply.message && (React$1.createElement("div", { className: "xapp-chat-button__cta" },
836
- React$1.createElement(CtaBubbleContainer, { timeout: configToApply === null || configToApply === void 0 ? void 0 : configToApply.timeout, delay: configToApply === null || configToApply === void 0 ? void 0 : configToApply.delay, animate: animate, buttonAnimation: animation, dismissible: configToApply === null || configToApply === void 0 ? void 0 : configToApply.dismissible, visible: !visible /** Why is this !visible */ }, configToApply === null || configToApply === void 0 ? void 0 : configToApply.message)))));
913
+ configToApply && configToApply.message && !hasInteracted && (React$1.createElement("div", { className: "xapp-chat-button__cta" },
914
+ React$1.createElement(CtaBubbleContainer, { timeout: configToApply === null || configToApply === void 0 ? void 0 : configToApply.timeout, delay: configToApply === null || configToApply === void 0 ? void 0 : configToApply.delay, animate: animate, buttonAnimation: animation, dismissible: configToApply === null || configToApply === void 0 ? void 0 : configToApply.dismissible, visible: !visible /** Why is this !visible */, onDismiss: onCtaDismiss }, configToApply === null || configToApply === void 0 ? void 0 : configToApply.message)))));
837
915
  };
838
916
 
839
917
  var ChatCard = function (props) {
@@ -2813,6 +2891,7 @@ var StentorRouterChat = /** @class */ (function () {
2813
2891
  this._sessionId = "";
2814
2892
  this.accessToken = "";
2815
2893
  this.attributes = {};
2894
+ this.routerAttributes = {};
2816
2895
  this.isAdmin = false;
2817
2896
  this.urlAttributes = {};
2818
2897
  this.handlers = {};
@@ -2881,13 +2960,19 @@ var StentorRouterChat = /** @class */ (function () {
2881
2960
  this.handlers["new message"] = function (data, sender, ts) {
2882
2961
  // Because the router's internal message format is Stentor channel compatible.
2883
2962
  // So the data is either a stentor Request (from widget) or a stentor Response (from bot)
2884
- var _a;
2963
+ var _a, _b;
2885
2964
  var message;
2886
2965
  if (sender.deviceId === "Bot") {
2887
- if (typeof ((_a = _this.options.hooks) === null || _a === void 0 ? void 0 : _a.onResponse) === "function") {
2888
- _this.options.hooks.onResponse(message);
2966
+ var response = data;
2967
+ // Extract custom attributes from router response
2968
+ if (((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.routerAttributes) && typeof response.data.routerAttributes === "object") {
2969
+ _this.routerAttributes = __assign({}, response.data.routerAttributes);
2970
+ log("Extracted router attributes: ".concat(JSON.stringify(_this.routerAttributes)));
2971
+ }
2972
+ if (typeof ((_b = _this.options.hooks) === null || _b === void 0 ? void 0 : _b.onResponse) === "function") {
2973
+ _this.options.hooks.onResponse(response);
2889
2974
  }
2890
- message = responseToMessage(data);
2975
+ message = responseToMessage(response);
2891
2976
  }
2892
2977
  else if (sender.deviceId === "Widget") {
2893
2978
  message = requestToMessage(data);
@@ -3320,7 +3405,7 @@ var StentorRouterChat = /** @class */ (function () {
3320
3405
  };
3321
3406
  StentorRouterChat.prototype.postMessage = function (message) {
3322
3407
  return __awaiter$1(this, void 0, void 0, function () {
3323
- var userId, sessionId, accessToken, attributes, rwgToken, merchantId, environment, enablePreferredTime, service, origin, request;
3408
+ var userId, sessionId, accessToken, attributes, rwgToken, merchantId, environment, enablePreferredTime, service, origin, mergedAttributes, request;
3324
3409
  return __generator$1(this, function (_a) {
3325
3410
  userId = this._userId;
3326
3411
  sessionId = this._sessionId;
@@ -3350,7 +3435,8 @@ var StentorRouterChat = /** @class */ (function () {
3350
3435
  if (origin) {
3351
3436
  attributes["origin"] = origin;
3352
3437
  }
3353
- request = requestFromMessage(message, userId, this.isNewSession, sessionId, accessToken, attributes, this.visitorInfo);
3438
+ mergedAttributes = __assign(__assign({}, attributes), this.routerAttributes);
3439
+ request = requestFromMessage(message, userId, this.isNewSession, sessionId, accessToken, mergedAttributes, this.visitorInfo);
3354
3440
  this.emit("new message", request);
3355
3441
  return [2 /*return*/];
3356
3442
  });
@@ -7642,22 +7728,21 @@ var StentorServerChat = /** @class */ (function () {
7642
7728
  return StentorServerChat;
7643
7729
  }());
7644
7730
 
7645
- // import { useWhatChanged } from '@simbathesailor/use-what-changed';
7646
7731
  function createChatServerCore(config, options) {
7647
7732
  switch (config.type) {
7648
7733
  case "direct":
7649
7734
  return new StentorDirectChat({
7650
7735
  url: config.serverUrl,
7651
7736
  key: config.accountKey,
7652
- timeout: config.timeout
7737
+ timeout: config.timeout,
7653
7738
  }, options);
7654
7739
  case "websocket":
7655
7740
  return new StentorServerChat({
7656
- url: config.serverUrl
7741
+ url: config.serverUrl,
7657
7742
  }, options);
7658
7743
  case "websocketraw":
7659
7744
  return new StentorRouterChat({
7660
- url: config.serverUrl
7745
+ url: config.serverUrl,
7661
7746
  }, options);
7662
7747
  case "local":
7663
7748
  return new StentorLocalChat();
@@ -7676,6 +7761,11 @@ function useChatServer(config, options) {
7676
7761
  // Log what caused the "effect"
7677
7762
  // useWhatChanged(deps, "options, config, dispatch");
7678
7763
  React$1.useEffect(function () {
7764
+ // Don't create server if config is null (waiting for async config load)
7765
+ if (!config || !options) {
7766
+ setServer(undefined);
7767
+ return undefined;
7768
+ }
7679
7769
  var newServer = createChatServer(config, options);
7680
7770
  newServer.init(dispatch);
7681
7771
  setServer(newServer);
@@ -8960,13 +9050,25 @@ var SendIcon = function () {
8960
9050
  };
8961
9051
 
8962
9052
  var SendButton = function (props) {
8963
- return (React$1.createElement(React$1.Fragment, null, !props.sendButtonIcon ? (React$1.createElement(IconButton_1, { className: "xappw-send-button ".concat(props.className || ""), tabIndex: props.tabIndex, onClick: props.onClick, icon: SendIcon })) : (React$1.createElement("button", { className: "xappw-custom-send-button", tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, onClick: props.onClick },
8964
- React$1.createElement("img", { src: props.sendButtonIcon, alt: "Send button", draggable: false })))));
9053
+ var _a = props.disabled, disabled = _a === void 0 ? false : _a;
9054
+ var _b = React$1.useState(false), isHovered = _b[0], setIsHovered = _b[1];
9055
+ // Determine which icon to show based on state
9056
+ var getIconSrc = function () {
9057
+ if (disabled && props.sendButtonIconDisabled) {
9058
+ return props.sendButtonIconDisabled;
9059
+ }
9060
+ if (isHovered && !disabled && props.sendButtonIconHover) {
9061
+ return props.sendButtonIconHover;
9062
+ }
9063
+ return props.sendButtonIcon;
9064
+ };
9065
+ return (React$1.createElement(React$1.Fragment, null, !props.sendButtonIcon ? (React$1.createElement(IconButton_1, { className: "xappw-send-button ".concat(props.className || "", " ").concat(disabled ? 'disabled' : ''), tabIndex: props.tabIndex, onClick: disabled ? undefined : props.onClick, icon: SendIcon })) : (React$1.createElement("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); } },
9066
+ React$1.createElement("img", { src: getIconSrc(), alt: "Send button", draggable: false, className: "send-button-icon" })))));
8965
9067
  };
8966
9068
 
8967
9069
  var Input = function (props) {
8968
9070
  var _a, _b;
8969
- var value = props.value, placeholder = props.placeholder, sendButtonIcon = props.sendButtonIcon, suggestion = props.suggestion, footerConfig = props.footerConfig, inputConfig = props.inputConfig, onChange = props.onChange, onSubmit = props.onSubmit, onSuggestionCommand = props.onSuggestionCommand;
9071
+ 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;
8970
9072
  var _c = React$1.useState(false), dragover = _c[0], setDragover = _c[1];
8971
9073
  function onDragOver(event) {
8972
9074
  setDragover(true);
@@ -9022,7 +9124,7 @@ var Input = function (props) {
9022
9124
  value: value, spellCheck: true }),
9023
9125
  React$1.createElement("div", { className: "xappw-input-form__buttons" },
9024
9126
  value.text && (React$1.createElement(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 })),
9025
- React$1.createElement(SendButton, { className: "xappw-input-form__btn", sendButtonIcon: sendButtonIcon, tabIndex: (_b = footerConfig === null || footerConfig === void 0 ? void 0 : footerConfig.sendButton) === null || _b === void 0 ? void 0 : _b.tabIndex, onClick: handleOnSubmit })))));
9127
+ React$1.createElement(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 })))));
9026
9128
  };
9027
9129
 
9028
9130
  function createActions(onItemUse) {
@@ -9084,7 +9186,7 @@ var Suggestions = function (props) {
9084
9186
 
9085
9187
  var ChatFooter = function (props) {
9086
9188
  var _a, _b, _c;
9087
- var isAdmin = props.isAdmin, isChatting = props.isChatting, visible = props.visible, placeholder = props.placeholder, sendButtonIcon = props.sendButtonIcon, footerConfig = props.footerConfig, menuConfig = props.menuConfig, inputConfig = props.inputConfig, onSubmit = props.onSubmit;
9189
+ 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, onSubmit = props.onSubmit;
9088
9190
  var innerDispatch = useChatDispatch();
9089
9191
  var _d = React$1.useState(false), drawerOpen = _d[0], setDrawerState = _d[1]; // false initially
9090
9192
  var _e = React$1.useState(), suggestionSearch = _e[0], setSuggestionSearch = _e[1];
@@ -9150,7 +9252,7 @@ var ChatFooter = function (props) {
9150
9252
  React$1.createElement(Suggestions, { className: "xappw-chat-footer__suggestions", data: suggestions.suggestions, index: suggestions.index, searchTerms: suggestionSearch, onItemClick: handleItemClick, onItemUse: handleItemUse }),
9151
9253
  isAdmin && isChatting && visible && React$1.createElement(AdminBar, { onAdminJoin: handleAdminJoin }),
9152
9254
  React$1.createElement("div", { style: { pointerEvents: enableInput ? "auto" : "none", opacity: enableInput ? 1 : 0.5 } },
9153
- React$1.createElement(Input, { addClass: "chat-footer__input " + (isChatting && visible ? "visible" : ""), suggestion: suggestions.item, value: input, placeholder: placeholder, sendButtonIcon: sendButtonIcon, footerConfig: footerConfig, inputConfig: inputConfig, onSubmit: handleSubmit, onChange: handleChange, onSuggestionCommand: suggestions.execute,
9255
+ React$1.createElement(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,
9154
9256
  // onFocus={this.inputOnFocus}
9155
9257
  onFileUpload: props.onFileUpload })),
9156
9258
  brandingEnabled && brandingText && React$1.createElement(ChatBranding, { text: brandingText })));
@@ -31378,6 +31480,9 @@ function getButtonStyle(style) {
31378
31480
  _a.sent();
31379
31481
  return [4 /*yield*/, ["button-hover-bg", style === null || style === void 0 ? void 0 : style.hoverBackground]];
31380
31482
  case 4:
31483
+ _a.sent();
31484
+ return [4 /*yield*/, ["button-disabled-bg", style === null || style === void 0 ? void 0 : style.disabledBackground]];
31485
+ case 5:
31381
31486
  _a.sent();
31382
31487
  return [2 /*return*/];
31383
31488
  }
@@ -31434,6 +31539,270 @@ function getCtaStyle(style) {
31434
31539
  return union(getBackgroundStyle(style.background), getTextStyle(style.text));
31435
31540
  }
31436
31541
 
31542
+ /**
31543
+ * Safely stringify objects, handling circular references and errors
31544
+ */
31545
+ var safeStringify = function (arg) {
31546
+ if (arg === null)
31547
+ return "null";
31548
+ if (arg === undefined)
31549
+ return "undefined";
31550
+ if (typeof arg !== "object")
31551
+ return String(arg);
31552
+ try {
31553
+ var seen_1 = new WeakSet();
31554
+ return JSON.stringify(arg, function (_key, value) {
31555
+ if (typeof value === "object" && value !== null) {
31556
+ if (seen_1.has(value)) {
31557
+ return "[Circular]";
31558
+ }
31559
+ seen_1.add(value);
31560
+ }
31561
+ return value;
31562
+ }, 2);
31563
+ }
31564
+ catch (e) {
31565
+ return "[Object: ".concat(Object.prototype.toString.call(arg), "]");
31566
+ }
31567
+ };
31568
+ var ErrorOverlay = function (_a) {
31569
+ var enableErrorOverlay = _a.enableErrorOverlay;
31570
+ var _b = React$1.useState([]), errors = _b[0], setErrors = _b[1];
31571
+ var _c = React$1.useState(false), isMinimized = _c[0], setIsMinimized = _c[1];
31572
+ var _d = React$1.useState(true), isVisible = _d[0], setIsVisible = _d[1];
31573
+ var _e = React$1.useState(false), isEnabled = _e[0], setIsEnabled = _e[1];
31574
+ var _f = React$1.useState("bottom"), position = _f[0], setPosition = _f[1];
31575
+ // Check if error overlay should be enabled and load position
31576
+ React$1.useEffect(function () {
31577
+ var _a;
31578
+ var checkEnabled = function () {
31579
+ var _a;
31580
+ if (typeof window === "undefined")
31581
+ return false;
31582
+ // 1. Check localStorage (highest priority)
31583
+ var localStorageSetting = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem("xaErrorOverlay");
31584
+ if (localStorageSetting === "enabled") {
31585
+ console.log("[ErrorOverlay] Enabled via localStorage");
31586
+ return true;
31587
+ }
31588
+ if (localStorageSetting === "disabled") {
31589
+ console.log("[ErrorOverlay] Disabled via localStorage");
31590
+ return false;
31591
+ }
31592
+ // 2. Check widget configuration
31593
+ if (enableErrorOverlay !== undefined) {
31594
+ console.log("[ErrorOverlay] Using config setting:", enableErrorOverlay);
31595
+ return enableErrorOverlay;
31596
+ }
31597
+ // 3. Check for React Native dev mode
31598
+ if (typeof globalThis.__DEV__ !== "undefined" && globalThis.__DEV__) {
31599
+ console.log("[ErrorOverlay] Enabled via __DEV__");
31600
+ return true;
31601
+ }
31602
+ // 4. Default: disabled
31603
+ return false;
31604
+ };
31605
+ var enabled = checkEnabled();
31606
+ setIsEnabled(enabled);
31607
+ // Load position preference
31608
+ var savedPosition = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem("xaErrorOverlayPosition");
31609
+ if (savedPosition === "top" || savedPosition === "bottom") {
31610
+ setPosition(savedPosition);
31611
+ }
31612
+ console.log("[ErrorOverlay] Component mounted, enabled:", enabled, "position:", savedPosition || "bottom");
31613
+ }, [enableErrorOverlay]);
31614
+ // Define addError with useCallback to prevent stale closures
31615
+ var addError = React$1.useCallback(function (error) {
31616
+ console.log("[ErrorOverlay] Adding error:", error);
31617
+ setErrors(function (prev) {
31618
+ var timestamp = new Date().toISOString();
31619
+ // Check if this is a duplicate of the last error
31620
+ var lastError = prev[prev.length - 1];
31621
+ if (lastError && lastError.message === error.message) {
31622
+ // Update count of last error
31623
+ return __spreadArray$1(__spreadArray$1([], prev.slice(0, -1), true), [
31624
+ __assign(__assign({}, lastError), { count: lastError.count + 1, timestamp: timestamp }),
31625
+ ], false);
31626
+ }
31627
+ // Add new error
31628
+ var newError = __assign({ id: "".concat(timestamp, "-").concat(Math.random()), timestamp: timestamp, count: 1 }, error);
31629
+ // Keep only last 50 errors
31630
+ var updated = __spreadArray$1(__spreadArray$1([], prev, true), [newError], false);
31631
+ return updated.slice(-50);
31632
+ });
31633
+ }, []);
31634
+ React$1.useEffect(function () {
31635
+ if (!isEnabled) {
31636
+ console.log("[ErrorOverlay] Not enabled, skipping error interception");
31637
+ return undefined;
31638
+ }
31639
+ console.log("[ErrorOverlay] Setting up error interception");
31640
+ // Store original console methods
31641
+ var originalError = console.error;
31642
+ var originalWarn = console.warn;
31643
+ // Override console.error
31644
+ console.error = function () {
31645
+ var args = [];
31646
+ for (var _i = 0; _i < arguments.length; _i++) {
31647
+ args[_i] = arguments[_i];
31648
+ }
31649
+ var message = args.map(function (arg) { return safeStringify(arg); }).join(" ");
31650
+ var error = new Error();
31651
+ addError({
31652
+ message: message,
31653
+ type: "error",
31654
+ stack: error.stack,
31655
+ });
31656
+ // Call original method
31657
+ originalError.apply(console, args);
31658
+ };
31659
+ // Override console.warn
31660
+ console.warn = function () {
31661
+ var args = [];
31662
+ for (var _i = 0; _i < arguments.length; _i++) {
31663
+ args[_i] = arguments[_i];
31664
+ }
31665
+ var message = args.map(function (arg) { return safeStringify(arg); }).join(" ");
31666
+ addError({
31667
+ message: message,
31668
+ type: "warning",
31669
+ });
31670
+ // Call original method
31671
+ originalWarn.apply(console, args);
31672
+ };
31673
+ // Capture unhandled errors
31674
+ var handleError = function (event) {
31675
+ var _a;
31676
+ addError({
31677
+ message: event.message,
31678
+ type: "error",
31679
+ stack: (_a = event.error) === null || _a === void 0 ? void 0 : _a.stack,
31680
+ });
31681
+ };
31682
+ // Capture unhandled promise rejections
31683
+ var handleRejection = function (event) {
31684
+ var _a;
31685
+ addError({
31686
+ message: "Unhandled Promise Rejection: ".concat(event.reason),
31687
+ type: "error",
31688
+ stack: (_a = event.reason) === null || _a === void 0 ? void 0 : _a.stack,
31689
+ });
31690
+ };
31691
+ window.addEventListener("error", handleError);
31692
+ window.addEventListener("unhandledrejection", handleRejection);
31693
+ // Cleanup
31694
+ return function () {
31695
+ console.log("[ErrorOverlay] Cleaning up error interception");
31696
+ console.error = originalError;
31697
+ console.warn = originalWarn;
31698
+ window.removeEventListener("error", handleError);
31699
+ window.removeEventListener("unhandledrejection", handleRejection);
31700
+ };
31701
+ }, [isEnabled, addError]);
31702
+ var clearErrors = function () {
31703
+ setErrors([]);
31704
+ };
31705
+ var copyToClipboard = function () { return __awaiter$1(void 0, void 0, void 0, function () {
31706
+ var errorText, err_1, textarea;
31707
+ return __generator$1(this, function (_a) {
31708
+ switch (_a.label) {
31709
+ case 0:
31710
+ errorText = errors
31711
+ .map(function (e) {
31712
+ return "[".concat(e.timestamp, "] ").concat(e.type.toUpperCase(), ": ").concat(e.message).concat(e.stack ? "\n" + e.stack : "");
31713
+ })
31714
+ .join("\n\n");
31715
+ _a.label = 1;
31716
+ case 1:
31717
+ _a.trys.push([1, 3, , 4]);
31718
+ return [4 /*yield*/, navigator.clipboard.writeText(errorText)];
31719
+ case 2:
31720
+ _a.sent();
31721
+ console.log("[ErrorOverlay] Errors copied to clipboard");
31722
+ return [3 /*break*/, 4];
31723
+ case 3:
31724
+ err_1 = _a.sent();
31725
+ console.error("[ErrorOverlay] Failed to copy to clipboard:", err_1);
31726
+ textarea = document.createElement("textarea");
31727
+ textarea.value = errorText;
31728
+ textarea.style.position = "fixed";
31729
+ textarea.style.opacity = "0";
31730
+ document.body.appendChild(textarea);
31731
+ textarea.select();
31732
+ try {
31733
+ document.execCommand("copy");
31734
+ console.log("[ErrorOverlay] Errors copied to clipboard using fallback method");
31735
+ }
31736
+ catch (fallbackErr) {
31737
+ console.error("[ErrorOverlay] Fallback copy method also failed:", fallbackErr);
31738
+ }
31739
+ document.body.removeChild(textarea);
31740
+ return [3 /*break*/, 4];
31741
+ case 4: return [2 /*return*/];
31742
+ }
31743
+ });
31744
+ }); };
31745
+ // Toggle enabled state
31746
+ var toggleEnabled = function () {
31747
+ var newState = !isEnabled;
31748
+ setIsEnabled(newState);
31749
+ if (typeof window !== "undefined" && window.localStorage) {
31750
+ window.localStorage.setItem("xaErrorOverlay", newState ? "enabled" : "disabled");
31751
+ }
31752
+ console.log("[ErrorOverlay] Toggled enabled state to:", newState);
31753
+ // Clear errors when disabling
31754
+ if (!newState) {
31755
+ setErrors([]);
31756
+ }
31757
+ };
31758
+ // Toggle position between top and bottom
31759
+ var togglePosition = function () {
31760
+ var newPosition = position === "bottom" ? "top" : "bottom";
31761
+ setPosition(newPosition);
31762
+ if (typeof window !== "undefined" && window.localStorage) {
31763
+ window.localStorage.setItem("xaErrorOverlayPosition", newPosition);
31764
+ }
31765
+ };
31766
+ // Don't render anything if not enabled
31767
+ if (!isEnabled) {
31768
+ return null;
31769
+ }
31770
+ // If enabled but not visible (user closed it), don't render
31771
+ if (!isVisible) {
31772
+ return null;
31773
+ }
31774
+ // If enabled and visible but no errors yet, don't show the overlay
31775
+ // Only show when there are actual errors to display
31776
+ if (errors.length === 0) {
31777
+ return null;
31778
+ }
31779
+ return (React$1.createElement("div", { className: "error-overlay error-overlay--".concat(position, " ").concat(isMinimized ? "minimized" : "") },
31780
+ React$1.createElement("div", { className: "error-overlay-header" },
31781
+ React$1.createElement("span", { className: "error-overlay-title" },
31782
+ "Debug Console (",
31783
+ errors.length,
31784
+ ")"),
31785
+ React$1.createElement("div", { className: "error-overlay-actions" },
31786
+ React$1.createElement("button", { onClick: copyToClipboard, title: "Copy all to clipboard" }, "\uD83D\uDCCB"),
31787
+ React$1.createElement("button", { onClick: clearErrors, title: "Clear all" }, "\uD83D\uDDD1\uFE0F"),
31788
+ React$1.createElement("button", { onClick: togglePosition, title: "Move to ".concat(position === "bottom" ? "top" : "bottom") }, position === "bottom" ? "⬆️" : "⬇️"),
31789
+ React$1.createElement("button", { onClick: toggleEnabled, title: "Disable Error Overlay" }, "\uD83D\uDD27"),
31790
+ React$1.createElement("button", { onClick: function () { return setIsMinimized(!isMinimized); }, title: "Minimize/Maximize" }, isMinimized ? "▲" : "▼"),
31791
+ React$1.createElement("button", { onClick: function () { return setIsVisible(false); }, title: "Close" }, "\u2715"))),
31792
+ !isMinimized && (React$1.createElement("div", { className: "error-overlay-content" }, errors.map(function (error) { return (React$1.createElement("div", { key: error.id, className: "error-entry error-".concat(error.type) },
31793
+ React$1.createElement("div", { className: "error-header" },
31794
+ React$1.createElement("span", { className: "error-time" }, new Date(error.timestamp).toLocaleTimeString()),
31795
+ error.count > 1 && (React$1.createElement("span", { className: "error-count" },
31796
+ "(",
31797
+ error.count,
31798
+ "x)")),
31799
+ React$1.createElement("span", { className: "error-badge error-badge-".concat(error.type) }, error.type)),
31800
+ React$1.createElement("div", { className: "error-message" }, error.message),
31801
+ error.stack && (React$1.createElement("details", { className: "error-stack" },
31802
+ React$1.createElement("summary", null, "Stack trace"),
31803
+ React$1.createElement("pre", null, error.stack))))); })))));
31804
+ };
31805
+
31437
31806
  var ModalContent = function (_a) {
31438
31807
  var onClose = _a.onClose, onReset = _a.onReset;
31439
31808
  return (React$1.createElement("div", { className: "modalContent" },
@@ -31446,7 +31815,44 @@ var ModalContent = function (_a) {
31446
31815
  };
31447
31816
 
31448
31817
  var ChatWidgetWrapper = function (props) {
31449
- var rawConfig = props.config;
31818
+ var _a;
31819
+ var _b = React$1.useState(props.config), rawConfig = _b[0], setRawConfig = _b[1];
31820
+ var _c = React$1.useState(!!props.getConfig), configLoading = _c[0], setConfigLoading = _c[1];
31821
+ var _d = React$1.useState(), configError = _d[0], setConfigError = _d[1];
31822
+ // Load config from callback if provided
31823
+ React$1.useEffect(function () {
31824
+ var cancelled = false;
31825
+ if (props.getConfig) {
31826
+ setConfigLoading(true);
31827
+ setConfigError(undefined);
31828
+ props
31829
+ .getConfig()
31830
+ .then(function (config) {
31831
+ if (!cancelled) {
31832
+ setRawConfig(config);
31833
+ setConfigLoading(false);
31834
+ log("[ChatWidget] Config loaded from getConfig callback");
31835
+ }
31836
+ })
31837
+ .catch(function (error) {
31838
+ if (!cancelled) {
31839
+ setConfigError(error);
31840
+ setConfigLoading(false);
31841
+ err("[ChatWidget] Failed to load config: ".concat(error.message));
31842
+ }
31843
+ });
31844
+ }
31845
+ else if (props.config) {
31846
+ // If no callback, use the config prop directly
31847
+ setRawConfig(props.config);
31848
+ setConfigLoading(false);
31849
+ }
31850
+ return function () {
31851
+ cancelled = true;
31852
+ };
31853
+ // Only depend on getConfig and config - not the entire props object to avoid infinite loops
31854
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31855
+ }, [props.getConfig, props.config]);
31450
31856
  var connection = useConnectionInfo(rawConfig);
31451
31857
  var config = React$1.useMemo(function () {
31452
31858
  var _a;
@@ -31455,7 +31861,7 @@ var ChatWidgetWrapper = function (props) {
31455
31861
  var token = reactRedux.useSelector(function (state) { return state.connection.token; });
31456
31862
  var options = React$1.useMemo(function () {
31457
31863
  var configurableMessages = getConfigurableMessages();
31458
- if (rawConfig.configurableMessages &&
31864
+ if ((rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.configurableMessages) &&
31459
31865
  Array.isArray(rawConfig.configurableMessages.items) &&
31460
31866
  rawConfig.configurableMessages.items.length > 0) {
31461
31867
  configurableMessages = rawConfig.configurableMessages;
@@ -31464,20 +31870,37 @@ var ChatWidgetWrapper = function (props) {
31464
31870
  token: token,
31465
31871
  bot: {
31466
31872
  nick: "Bot",
31467
- displayName: rawConfig.botName,
31468
- avatarPath: rawConfig.avatarUrl
31873
+ displayName: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.botName,
31874
+ avatarPath: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.avatarUrl,
31469
31875
  },
31470
31876
  configurableMessages: configurableMessages,
31471
- hooks: rawConfig.hooks
31877
+ hooks: rawConfig === null || rawConfig === void 0 ? void 0 : rawConfig.hooks,
31472
31878
  };
31473
31879
  }, [token, rawConfig]);
31474
- var chatServer = useChatServer(connection, options);
31880
+ // Only create chat server when config is ready (not loading and no error)
31881
+ var chatServer = useChatServer(configLoading || configError ? null : connection, configLoading || configError ? null : options);
31882
+ // Determine mode class for loading/error states
31883
+ var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "normal";
31884
+ var modeClass = "widget-container--".concat(mode);
31885
+ // Show loading state while config is being fetched
31886
+ if (configLoading) {
31887
+ return (React$1.createElement("div", { className: "widget-container widget-container--loading ".concat(modeClass) },
31888
+ React$1.createElement("div", { className: "xa-spinner-container visible" },
31889
+ React$1.createElement("div", { className: "xa-spinner" }))));
31890
+ }
31891
+ // Show error state if config failed to load
31892
+ if (configError) {
31893
+ return (React$1.createElement("div", { className: "widget-container widget-container--error ".concat(modeClass) },
31894
+ React$1.createElement("div", { className: "widget-error-message" },
31895
+ "Failed to load chat configuration: ",
31896
+ configError.message)));
31897
+ }
31475
31898
  return (React$1.createElement(ChatConfigContext.Provider, { value: config },
31476
31899
  React$1.createElement(ChatServerContext.Provider, { value: chatServer },
31477
- React$1.createElement(ChatWidget, __assign({}, props)))));
31900
+ React$1.createElement(ChatWidget, __assign({}, props, { config: rawConfig })))));
31478
31901
  };
31479
31902
  var ChatWidget = function (props) {
31480
- 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;
31903
+ 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;
31481
31904
  var innerDispatch = useChatDispatch();
31482
31905
  var dispatch = useChatServerDispatch();
31483
31906
  // From Redux
@@ -31491,7 +31914,7 @@ var ChatWidget = function (props) {
31491
31914
  var canRefresh = (_c = (_b = props.config.header) === null || _b === void 0 ? void 0 : _b.actions) === null || _c === void 0 ? void 0 : _c.refresh;
31492
31915
  // can't minimize in docked mode or static mode.
31493
31916
  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);
31494
- log("docked: ".concat(dockedMode, " static: ").concat(staticMode, " minimimized: ").concat((_g = (_f = props.config.header) === null || _f === void 0 ? void 0 : _f.actions) === null || _g === void 0 ? void 0 : _g.minimize));
31917
+ 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));
31495
31918
  var canCancel;
31496
31919
  // To preserve legacy behavior, cancel needs a little more checks
31497
31920
  if (typeof ((_j = (_h = props.config.header) === null || _h === void 0 ? void 0 : _h.actions) === null || _j === void 0 ? void 0 : _j.cancel) === "boolean") {
@@ -31500,17 +31923,17 @@ var ChatWidget = function (props) {
31500
31923
  else {
31501
31924
  canCancel = !dockedMode && !staticMode;
31502
31925
  }
31503
- // For backward compatibility. Note: the action will create the actual "visuals" object" (this is a copy).
31926
+ // For backward compatibility. Note: the action will create the actual "visuals" object" (this is a copy).
31504
31927
  if (!chatState.visuals) {
31505
31928
  chatState.visuals = {};
31506
31929
  }
31507
31930
  // Our state - pull from storage
31508
- var _3 = React$1.useState((!canMinimize && !canCancel) ||
31931
+ var _8 = React$1.useState((!canMinimize && !canCancel) ||
31509
31932
  // !!get("visible") ||
31510
31933
  chatState.visuals.visible ||
31511
31934
  (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
31512
- window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _3[0], setVisibleState = _3[1];
31513
- var _4 = React$1.useState(false), typing = _4[0], setTypingState = _4[1]; // false initially
31935
+ window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _8[0], setVisibleState = _8[1];
31936
+ var _9 = React$1.useState(false), typing = _9[0], setTypingState = _9[1]; // false initially
31514
31937
  var chatServer = React$1.useContext(ChatServerContext);
31515
31938
  var patternsConfig = (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.autoOpenOnPattern) === null || _q === void 0 ? void 0 : _q.patterns;
31516
31939
  var currentUrl = window.location.href;
@@ -31525,32 +31948,36 @@ var ChatWidget = function (props) {
31525
31948
  }
31526
31949
  setVisibleState(newVisible);
31527
31950
  innerDispatch(setVisualStatus({
31528
- visible: newVisible
31951
+ visible: newVisible,
31529
31952
  }));
31530
31953
  }, [innerDispatch, staticMode]);
31531
31954
  React$1.useEffect(function () {
31532
31955
  var _a, _b;
31533
- document.addEventListener("keydown", function (event) {
31956
+ var handleKeyDown = function (event) {
31534
31957
  var body = document.getElementsByTagName("body")[0];
31535
31958
  body.tabIndex = -1;
31536
31959
  if (event.key === "Escape") {
31537
31960
  body.focus();
31538
31961
  }
31539
- });
31962
+ };
31963
+ document.addEventListener("keydown", handleKeyDown);
31540
31964
  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)) {
31541
31965
  innerDispatch(setSessionId(undefined));
31542
31966
  innerDispatch(reset());
31543
31967
  }
31968
+ return function () {
31969
+ document.removeEventListener("keydown", handleKeyDown);
31970
+ };
31544
31971
  // eslint-disable-next-line react-hooks/exhaustive-deps
31545
31972
  }, []);
31546
- var _5 = React$1.useState(!document.hidden), isTabVisible = _5[0], setIsTabVisible = _5[1];
31973
+ var _10 = React$1.useState(!document.hidden), isTabVisible = _10[0], setIsTabVisible = _10[1];
31547
31974
  React$1.useEffect(function () {
31548
31975
  var handleVisibilityChange = function () {
31549
31976
  setIsTabVisible(!document.hidden);
31550
31977
  };
31551
- document.addEventListener('visibilitychange', handleVisibilityChange);
31978
+ document.addEventListener("visibilitychange", handleVisibilityChange);
31552
31979
  return function () {
31553
- document.removeEventListener('visibilitychange', handleVisibilityChange);
31980
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
31554
31981
  };
31555
31982
  }, []);
31556
31983
  React$1.useEffect(function () {
@@ -31584,7 +32011,15 @@ var ChatWidget = function (props) {
31584
32011
  if (currentWidth < +configWidth) {
31585
32012
  setVisible(false);
31586
32013
  }
31587
- }, [currentWidth, patternExist, patternMatches, configWidth, setVisible, chatState.visuals.opened, mode]);
32014
+ }, [
32015
+ currentWidth,
32016
+ patternExist,
32017
+ patternMatches,
32018
+ configWidth,
32019
+ setVisible,
32020
+ chatState.visuals.opened,
32021
+ mode,
32022
+ ]);
31588
32023
  function handleOnChange() {
31589
32024
  if (!typing) {
31590
32025
  dispatch(sendTyping(true));
@@ -31656,28 +32091,37 @@ var ChatWidget = function (props) {
31656
32091
  /** Called when minimize button is clicked */
31657
32092
  function handleMinimizeClick() {
31658
32093
  innerDispatch(setVisualStatus({
31659
- opened: false
32094
+ opened: false,
32095
+ hasInteracted: true,
31660
32096
  }));
31661
32097
  setVisible(false);
31662
32098
  }
31663
32099
  /** Called when cancel is clicked */
31664
32100
  function handleCancelClick() {
31665
- //set("opened", false);
32101
+ // First reset to clear all state
32102
+ innerDispatch(reset());
32103
+ // Then set hasInteracted to prevent CTA from showing after cancel
31666
32104
  innerDispatch(setVisualStatus({
31667
- opened: false
32105
+ opened: false,
32106
+ hasInteracted: true,
31668
32107
  }));
31669
- innerDispatch(reset());
31670
32108
  setVisible(false);
31671
32109
  }
31672
32110
  function chatButtonOnClick() {
31673
32111
  innerDispatch(setVisualStatus({
31674
- opened: true
32112
+ opened: true,
32113
+ hasInteracted: true,
31675
32114
  }));
31676
32115
  setVisible(true);
31677
32116
  setTimeout(function () {
31678
32117
  document.getElementById("chatWidgetInput").focus();
31679
32118
  }, 100);
31680
32119
  }
32120
+ function handleCtaDismiss() {
32121
+ innerDispatch(setVisualStatus({
32122
+ hasInteracted: true,
32123
+ }));
32124
+ }
31681
32125
  var isOffline = chatState.accountStatus === "offline" && !chatState.isChatting;
31682
32126
  var messages = chatState && chatState.chats;
31683
32127
  var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
@@ -31690,8 +32134,12 @@ var ChatWidget = function (props) {
31690
32134
  }, [connectionStatus, onConnectionStatusChange]);
31691
32135
  useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
31692
32136
  // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
31693
- var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) || (config === null || config === void 0 ? void 0 : config.agent)
31694
- || { nick: "agent:robot", avatarPath: config.avatarUrl, display_name: "Agent" };
32137
+ var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
32138
+ (config === null || config === void 0 ? void 0 : config.agent) || {
32139
+ nick: "agent:robot",
32140
+ avatarPath: config.avatarUrl,
32141
+ display_name: "Agent",
32142
+ };
31695
32143
  return (React$1.createElement(React$1.Fragment, null,
31696
32144
  React$1.createElement("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()) },
31697
32145
  React$1.createElement(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }),
@@ -31700,10 +32148,11 @@ var ChatWidget = function (props) {
31700
32148
  React$1.createElement("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : "") },
31701
32149
  React$1.createElement("div", { className: "xa-spinner" })),
31702
32150
  connectionStatus === "offline" && React$1.createElement(ServerOffline, null),
31703
- React$1.createElement(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, visible: visible, menuConfig: props.config.menu, footerConfig: (_0 = props.config) === null || _0 === void 0 ? void 0 : _0.footer, inputConfig: (_1 = props.config) === null || _1 === void 0 ? void 0 : _1.input, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }),
32151
+ React$1.createElement(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, 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 }),
31704
32152
  React$1.createElement("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick },
31705
32153
  React$1.createElement(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }))),
31706
- React$1.createElement(ChatButton, { addClass: getVisibilityClass(), onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_2 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _2 === void 0 ? void 0 : _2.imageUrl, visible: visible })));
32154
+ React$1.createElement(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 }),
32155
+ React$1.createElement(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })));
31707
32156
  };
31708
32157
 
31709
32158
  // src/utils/formatProdErrorMessage.ts
@@ -32180,17 +32629,37 @@ var DEFAULT_VISITOR = {
32180
32629
  nick: "visitor:",
32181
32630
  typing: false
32182
32631
  };
32183
- function createDefaultState(state) {
32632
+ function createDefaultState(state, options) {
32633
+ var _a;
32184
32634
  if (!state) {
32185
32635
  state = {};
32186
32636
  }
32187
32637
  state.userId = state.userId ? state.userId : visitorFingerprint();
32188
32638
  state.visitorId = state.userId;
32639
+ // Determine if debug mode should be enabled
32640
+ var debugMode = false;
32641
+ if (typeof window !== 'undefined') {
32642
+ var localStorageSetting = (_a = window.localStorage) === null || _a === void 0 ? void 0 : _a.getItem('xaErrorOverlay');
32643
+ if (localStorageSetting === 'enabled') {
32644
+ debugMode = true;
32645
+ }
32646
+ else if (localStorageSetting === 'disabled') {
32647
+ debugMode = false;
32648
+ }
32649
+ else if (options === null || options === void 0 ? void 0 : options.enableErrorOverlay) {
32650
+ // Use config option if no localStorage override
32651
+ debugMode = true;
32652
+ }
32653
+ else if (typeof globalThis.__DEV__ !== 'undefined' && globalThis.__DEV__) {
32654
+ // For React Native, default to true in development mode
32655
+ debugMode = true;
32656
+ }
32657
+ }
32189
32658
  return __assign({ connection: {
32190
32659
  connectionStatus: "offline",
32191
32660
  token: null,
32192
32661
  greetingRequested: false
32193
- }, 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: {} }, state);
32662
+ }, 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);
32194
32663
  }
32195
32664
  var DEFAULT_STATE = createDefaultState();
32196
32665
 
@@ -32239,6 +32708,10 @@ var LocalStorage = /** @class */ (function () {
32239
32708
  function persistStateReducer(storage, initialState, innerReducer) {
32240
32709
  var _a;
32241
32710
  var obj = (_a = storage.get()) !== null && _a !== void 0 ? _a : initialState;
32711
+ // Clean up hasInteracted flag on page load - don't persist across browser refreshes
32712
+ if (obj && obj.visuals) {
32713
+ obj = __assign(__assign({}, obj), { visuals: __assign(__assign({}, obj.visuals), { hasInteracted: undefined }) });
32714
+ }
32242
32715
  return function (state, action) {
32243
32716
  if (state === void 0) { state = obj; }
32244
32717
  var res = innerReducer(state, action);
@@ -32297,13 +32770,14 @@ function memberLeave(state, detail) {
32297
32770
 
32298
32771
  function resetReducer(state) {
32299
32772
  if (state === void 0) { state = DEFAULT_STATE; }
32300
- // pass through some of the items to persis
32301
32773
  return __assign(__assign({}, createDefaultState({
32302
32774
  accessToken: state.accessToken,
32303
32775
  userId: state.userId,
32304
32776
  attributes: state.attributes,
32305
32777
  sessionExpiration: state.sessionExpiration
32306
- })), { connection: __assign(__assign({}, state.connection), { greetingRequested: false, nonce: uuid_1() }), visitor: state.visitor, visitorId: state.visitorId });
32778
+ })), { connection: __assign(__assign({}, state.connection), { greetingRequested: false, nonce: uuid_1() }), visitor: state.visitor, visitorId: state.visitorId,
32779
+ // Explicitly reset visuals to clear hasInteracted flag for widget refresh
32780
+ visuals: {} });
32307
32781
  }
32308
32782
 
32309
32783
  // todo: create reducer (requires redux-thunk dependency)
@@ -32336,7 +32810,14 @@ function update(state, action) {
32336
32810
  case "session_id":
32337
32811
  return __assign(__assign({}, state), { lastTimestamp: (_g = action.detail) === null || _g === void 0 ? void 0 : _g.timestamp, sessionId: action.detail.sessionId });
32338
32812
  case "visual_status":
32339
- return __assign(__assign({}, state), { lastTimestamp: (_h = action.detail) === null || _h === void 0 ? void 0 : _h.timestamp, visuals: __assign(__assign(__assign(__assign({}, (state.visuals || {})), (action.detail.status.drawer !== undefined && { drawer: action.detail.status.drawer })), (action.detail.status.opened !== undefined && { opened: action.detail.status.opened })), (action.detail.status.visible !== undefined && { visible: action.detail.status.visible })) });
32813
+ return __assign(__assign({}, state), { lastTimestamp: (_h = action.detail) === null || _h === void 0 ? void 0 : _h.timestamp, visuals: __assign(__assign(__assign(__assign(__assign({}, (state.visuals || {})), (action.detail.status.drawer !== undefined && { drawer: action.detail.status.drawer })), (action.detail.status.opened !== undefined && { opened: action.detail.status.opened })), (action.detail.status.visible !== undefined && { visible: action.detail.status.visible })), (action.detail.status.hasInteracted !== undefined && { hasInteracted: action.detail.status.hasInteracted })) });
32814
+ case "toggle_debug_mode":
32815
+ var newDebugMode = !state.debugMode;
32816
+ // Update localStorage to persist the setting
32817
+ if (typeof window !== 'undefined' && window.localStorage) {
32818
+ window.localStorage.setItem('xaErrorOverlay', newDebugMode ? 'enabled' : 'disabled');
32819
+ }
32820
+ return __assign(__assign({}, state), { debugMode: newDebugMode });
32340
32821
  case "department_update":
32341
32822
  return __assign(__assign({}, state), { lastTimestamp: (_j = action.detail) === null || _j === void 0 ? void 0 : _j.timestamp, departments: __assign(__assign({}, state.departments), (_a = {}, _a[action.detail.id] = __assign({}, action.detail), _a)) });
32342
32823
  case "visitor_update":
@@ -32487,6 +32968,8 @@ function createChatStore(config, dataStorage) {
32487
32968
  userId: config.userId,
32488
32969
  attributes: config.attributes,
32489
32970
  sessionExpiration: config.sessionExpiration
32971
+ }, {
32972
+ enableErrorOverlay: config.enableErrorOverlay
32490
32973
  });
32491
32974
  var chatReducer = persistStateReducer(storage, defaultState, storeHandler);
32492
32975
  // Configure store with @reduxjs/toolkit
@@ -32526,23 +33009,25 @@ function generateKey(connection, sessionId) {
32526
33009
  }
32527
33010
 
32528
33011
  var ChatWidgetContainer = function (props) {
32529
- var _a, _b, _c, _d, _e;
33012
+ var _a, _b, _c, _d, _e, _f;
32530
33013
  var messageMiddleware = useStandardMiddleware();
32531
33014
  var connection = useServerConfig(props.config);
32532
33015
  var chatStore = React$1.useMemo(function () {
32533
- var _a, _b, _c, _d;
33016
+ var _a, _b, _c, _d, _e;
32534
33017
  return createChatStore({
32535
33018
  connection: connection,
32536
33019
  userId: (_a = props.config) === null || _a === void 0 ? void 0 : _a.userId,
32537
33020
  accessToken: (_b = props.config) === null || _b === void 0 ? void 0 : _b.accessToken,
32538
33021
  attributes: (_c = props.config) === null || _c === void 0 ? void 0 : _c.attributes,
32539
- sessionExpiration: (_d = props.config) === null || _d === void 0 ? void 0 : _d.sessionExpiration
33022
+ sessionExpiration: (_d = props.config) === null || _d === void 0 ? void 0 : _d.sessionExpiration,
33023
+ enableErrorOverlay: (_e = props.config) === null || _e === void 0 ? void 0 : _e.enableErrorOverlay
32540
33024
  });
32541
- }, [connection, (_a = props.config) === null || _a === void 0 ? void 0 : _a.userId, (_b = props.config) === null || _b === void 0 ? void 0 : _b.accessToken, (_c = props.config) === null || _c === void 0 ? void 0 : _c.attributes, (_d = props.config) === null || _d === void 0 ? void 0 : _d.sessionExpiration]);
32542
- if ((_e = props.config) === null || _e === void 0 ? void 0 : _e.disabled) {
33025
+ }, [connection, (_a = props.config) === null || _a === void 0 ? void 0 : _a.userId, (_b = props.config) === null || _b === void 0 ? void 0 : _b.accessToken, (_c = props.config) === null || _c === void 0 ? void 0 : _c.attributes, (_d = props.config) === null || _d === void 0 ? void 0 : _d.sessionExpiration, (_e = props.config) === null || _e === void 0 ? void 0 : _e.enableErrorOverlay]);
33026
+ if ((_f = props.config) === null || _f === void 0 ? void 0 : _f.disabled) {
32543
33027
  return React$1.createElement(React$1.Fragment, null);
32544
33028
  }
32545
- var widgetProps = __assign(__assign({}, props), { messageMiddleware: messageMiddleware });
33029
+ var widgetProps = __assign(__assign({}, props), { messageMiddleware: messageMiddleware, getConfig: props.getConfig // Pass through getConfig callback
33030
+ });
32546
33031
  return (React$1.createElement(reactRedux.Provider, { store: chatStore },
32547
33032
  React$1.createElement(ChatWidgetWrapper, __assign({}, widgetProps))));
32548
33033
  };